1 /*    Modified by -------------->Allegro<--------------
2  *    (the first line is for the seek test), the "Modified by" is to
3  *    silence genexample.py.
4  *
5  *    Example program for the Allegro library, by Peter Wang and
6  *    Elias Pschernig.
7  *
8  *    This program demonstrates the use of the packfile functions, with some
9  *    simple tests.
10  *
11  *    The first test uses the standard packfile functions to transfer a
12  *    bitmap file into a block of memory, then reads the bitmap out of the
13  *    block of memory, using a custom packfile vtable.
14  *
15  *    The second test reads in a bitmap with another custom packfile
16  *    vtable, which uses libc's file stream functions.
17  *
18  *    The third test demonstrates seeking with a custom vtable.
19  *
20  *    The fourth test reads two bitmaps, and dumps them back into a
21  *    single file, using a custom vtable again.
22  */
23 
24 #include <stdio.h>
25 #include <string.h>
26 #include "allegro.h"
27 
28 /*----------------------------------------------------------------------*/
29 /*                memory vtable                                         */
30 /*----------------------------------------------------------------------*/
31 
32 /* The packfile data for our memory reader. */
33 typedef struct MEMREAD_INFO
34 {
35    AL_CONST unsigned char *block;
36    long length;
37    long offset;
38 } MEMREAD_INFO;
39 
memread_getc(void * userdata)40 static int memread_getc(void *userdata)
41 {
42    MEMREAD_INFO *info = userdata;
43    ASSERT(info);
44    ASSERT(info->offset <= info->length);
45 
46    if (info->offset == info->length)
47       return EOF;
48    else
49       return info->block[info->offset++];
50 }
51 
memread_ungetc(int c,void * userdata)52 static int memread_ungetc(int c, void *userdata)
53 {
54    MEMREAD_INFO *info = userdata;
55    unsigned char ch = c;
56 
57    if ((info->offset > 0) && (info->block[info->offset-1] == ch))
58       return ch;
59    else
60       return EOF;
61 }
62 
memread_putc(int c,void * userdata)63 static int memread_putc(int c, void *userdata)
64 {
65    return EOF;
66 }
67 
memread_fread(void * p,long n,void * userdata)68 static long memread_fread(void *p, long n, void *userdata)
69 {
70    MEMREAD_INFO *info = userdata;
71    size_t actual;
72    ASSERT(info);
73    ASSERT(info->offset <= info->length);
74 
75    actual = MIN(n, info->length - info->offset);
76 
77    memcpy(p, info->block + info->offset, actual);
78    info->offset += actual;
79 
80    ASSERT(info->offset <= info->length);
81 
82    return actual;
83 }
84 
memread_fwrite(AL_CONST void * p,long n,void * userdata)85 static long memread_fwrite(AL_CONST void *p, long n, void *userdata)
86 {
87    return 0;
88 }
89 
memread_seek(void * userdata,int offset)90 static int memread_seek(void *userdata, int offset)
91 {
92    MEMREAD_INFO *info = userdata;
93    long actual;
94    ASSERT(info);
95    ASSERT(info->offset <= info->length);
96 
97    actual = MIN(offset, info->length - info->offset);
98 
99    info->offset += actual;
100 
101    ASSERT(info->offset <= info->length);
102 
103    if (offset == actual)
104       return 0;
105    else
106       return -1;
107 }
108 
memread_fclose(void * userdata)109 static int memread_fclose(void *userdata)
110 {
111    return 0;
112 }
113 
memread_feof(void * userdata)114 static int memread_feof(void *userdata)
115 {
116    MEMREAD_INFO *info = userdata;
117 
118    return info->offset >= info->length;
119 }
120 
memread_ferror(void * userdata)121 static int memread_ferror(void *userdata)
122 {
123    (void)userdata;
124 
125    return FALSE;
126 }
127 
128 /* The actual vtable. Note that writing is not supported, the functions for
129  * writing above are only placeholders.
130  */
131 static PACKFILE_VTABLE memread_vtable =
132 {
133    memread_fclose,
134    memread_getc,
135    memread_ungetc,
136    memread_fread,
137    memread_putc,
138    memread_fwrite,
139    memread_seek,
140    memread_feof,
141    memread_ferror
142 };
143 
144 /*----------------------------------------------------------------------*/
145 /*                stdio vtable                                          */
146 /*----------------------------------------------------------------------*/
147 
stdio_fclose(void * userdata)148 static int stdio_fclose(void *userdata)
149 {
150    FILE *fp = userdata;
151    return fclose(fp);
152 }
153 
stdio_getc(void * userdata)154 static int stdio_getc(void *userdata)
155 {
156    FILE *fp = userdata;
157    return fgetc(fp);
158 }
159 
stdio_ungetc(int c,void * userdata)160 static int stdio_ungetc(int c, void *userdata)
161 {
162    FILE *fp = userdata;
163    return ungetc(c, fp);
164 }
165 
stdio_fread(void * p,long n,void * userdata)166 static long stdio_fread(void *p, long n, void *userdata)
167 {
168    FILE *fp = userdata;
169    return fread(p, 1, n, fp);
170 }
171 
stdio_putc(int c,void * userdata)172 static int stdio_putc(int c, void *userdata)
173 {
174    FILE *fp = userdata;
175    return fputc(c, fp);
176 }
177 
stdio_fwrite(AL_CONST void * p,long n,void * userdata)178 static long stdio_fwrite(AL_CONST void *p, long n, void *userdata)
179 {
180    FILE *fp = userdata;
181    return fwrite(p, 1, n, fp);
182 }
183 
stdio_seek(void * userdata,int n)184 static int stdio_seek(void *userdata, int n)
185 {
186    FILE *fp = userdata;
187    return fseek(fp, n, SEEK_CUR);
188 }
189 
stdio_feof(void * userdata)190 static int stdio_feof(void *userdata)
191 {
192    FILE *fp = userdata;
193    return feof(fp);
194 }
195 
stdio_ferror(void * userdata)196 static int stdio_ferror(void *userdata)
197 {
198    FILE *fp = userdata;
199    return ferror(fp);
200 }
201 
202 /* The actual vtable. */
203 static PACKFILE_VTABLE stdio_vtable =
204 {
205    stdio_fclose,
206    stdio_getc,
207    stdio_ungetc,
208    stdio_fread,
209    stdio_putc,
210    stdio_fwrite,
211    stdio_seek,
212    stdio_feof,
213    stdio_ferror
214 };
215 
216 /*----------------------------------------------------------------------*/
217 /*                tests                                                 */
218 /*----------------------------------------------------------------------*/
219 
next(void)220 static void next(void)
221 {
222    textprintf_centre_ex(screen, font, SCREEN_W / 2,
223       SCREEN_H - text_height(font), -1, -1, "Press a key to continue");
224    readkey();
225    clear_bitmap(screen);
226 }
227 
228 #define CHECK(x, err)                                   \
229    do {                                                 \
230       if (!x) {                                         \
231          alert("Error", err, NULL, "Ok", NULL, 0, 0);   \
232          return;                                        \
233       }                                                 \
234    } while (0)
235 
236 /* This reads the files mysha.pcx and allegro.pcx into a memory block as
237  * binary data, and then uses the memory vtable to read the bitmaps out of
238  * the memory block.
239  */
memread_test(void)240 static void memread_test(void)
241 {
242    PACKFILE *f;
243    MEMREAD_INFO memread_info;
244    BITMAP *bmp, *bmp2;
245    unsigned char *block;
246    int64_t l1, l2;
247    PACKFILE *f1, *f2;
248 
249    l1 = file_size_ex("allegro.pcx");
250    l2 = file_size_ex("mysha.pcx");
251 
252    block = malloc(l1 + l2);
253 
254    /* Read mysha.pcx into the memory block. */
255    f1 = pack_fopen("allegro.pcx", "rb");
256    CHECK(f1, "opening allegro.pcx");
257    pack_fread(block, l1, f1);
258    pack_fclose(f1);
259 
260    /* Read allegro.pcx into the memory block. */
261    f2 = pack_fopen("mysha.pcx", "rb");
262    CHECK(f2, "opening mysha.pcx");
263    pack_fread(block + l1, l2, f2);
264    pack_fclose(f2);
265 
266    /* Open the memory block as PACKFILE, using our memory vtable. */
267    memread_info.block = block;
268    memread_info.length = l1 + l2;
269    memread_info.offset = 0;
270    f = pack_fopen_vtable(&memread_vtable, &memread_info);
271    CHECK(f, "reading from memory block");
272 
273    /* Read the bitmaps out of the memory block. */
274    bmp = load_pcx_pf(f, NULL);
275    CHECK(bmp, "load_pcx_pf");
276    bmp2 = load_pcx_pf(f, NULL);
277    CHECK(bmp2, "load_pcx_pf");
278 
279    blit(bmp, screen, 0, 0, 0, 0, bmp->w, bmp->h);
280    textprintf_ex(screen, font, bmp->w + 8, 8, -1, -1,
281       "\"allegro.pcx\"");
282    textprintf_ex(screen, font, bmp->w + 8, 8 + 20, -1, -1,
283       "read out of a memory file");
284 
285    blit(bmp2, screen, 0, 0, 0, bmp->h + 8, bmp2->w, bmp2->h);
286    textprintf_ex(screen, font, bmp2->w + 8, bmp->h + 8, -1, -1,
287       "\"mysha.pcx\"");
288    textprintf_ex(screen, font, bmp2->w + 8, bmp->h + 8 + 20, -1, -1,
289       "read out of a memory file");
290 
291    destroy_bitmap(bmp);
292    destroy_bitmap(bmp2);
293    pack_fclose(f);
294 
295    next();
296 }
297 
298 /* This reads in allegro.pcx, but it does so by using the stdio vtable. */
stdio_read_test(void)299 static void stdio_read_test(void)
300 {
301    FILE *fp, *fp2;
302    PACKFILE *f;
303    BITMAP *bmp,*bmp2;
304 
305    /* Simply open the file with the libc fopen. */
306    fp = fopen("allegro.pcx", "rb");
307    CHECK(fp, "opening allegro.pcx");
308 
309    /* Create a PACKFILE, with our custom stdio vtable. */
310    f = pack_fopen_vtable(&stdio_vtable, fp);
311    CHECK(f, "reading with stdio");
312 
313    /* Now read in the bitmap. */
314    bmp = load_pcx_pf(f, NULL);
315    CHECK(bmp, "load_pcx_pf");
316 
317    /* A little bit hackish, we re-assign the file pointer in our PACKFILE
318     * to another file.
319     */
320    fp2 = freopen("mysha.pcx", "rb", fp);
321    CHECK(fp2, "opening mysha.pcx");
322 
323    /* Read in the other bitmap. */
324    bmp2 = load_pcx_pf(f, NULL);
325    CHECK(bmp, "load_pcx_pf");
326 
327    blit(bmp, screen, 0, 0, 0, 0, bmp->w, bmp->h);
328    textprintf_ex(screen, font, bmp2->w + 8, 8, -1, -1,
329       "\"allegro.pcx\"");
330    textprintf_ex(screen, font, bmp2->w + 8, 8 + 20, -1, -1,
331       "read with stdio functions");
332 
333    blit(bmp2, screen, 0, 0, 0, bmp->h + 8, bmp2->w, bmp2->h);
334    textprintf_ex(screen, font, bmp2->w + 8, bmp->h + 8, -1, -1,
335       "\"mysha.pcx\"");
336    textprintf_ex(screen, font, bmp2->w + 8, bmp->h + 8 + 20, -1, -1,
337       "read with stdio functions");
338 
339    destroy_bitmap(bmp);
340    destroy_bitmap(bmp2);
341    pack_fclose(f);
342 
343    next();
344 }
345 
346 /* This demonstrates seeking. It opens expackf.c, and reads some characters
347  * from it.
348  */
stdio_seek_test(void)349 static void stdio_seek_test(void)
350 {
351    FILE *fp;
352    PACKFILE *f;
353    char str[8];
354 
355    fp = fopen("expackf.c", "rb");
356    if (!fp) {
357       /* Handle the case where the user is running from a build directory
358        * directly under the Allegro root directory.
359        */
360       fp = fopen("../../examples/expackf.c", "rb");
361    }
362    CHECK(fp, "opening expackf.c");
363    f = pack_fopen_vtable(&stdio_vtable, fp);
364    CHECK(f, "reading with stdio");
365 
366    pack_fseek(f, 33);
367    pack_fread(str, 7, f);
368    str[7] = '\0';
369 
370    textprintf_ex(screen, font, 0, 0, -1, -1, "Reading from \"expackf.c\" with stdio.");
371    textprintf_ex(screen, font, 0, 20, -1, -1, "Seeking to byte 33, reading 7 bytes:");
372    textprintf_ex(screen, font, 0, 40, -1, -1, "\"%s\"", str);
373    textprintf_ex(screen, font, 0, 60, -1, -1, "(Should be \"Allegro\")");
374 
375    pack_fclose(f);
376 
377    next();
378 }
379 
380 /* This demonstrates writing. It simply saves the two bitmaps into a binary
381  * file.
382  */
stdio_write_test(void)383 static void stdio_write_test(void)
384 {
385    FILE *fp;
386    PACKFILE *f;
387    BITMAP *bmp, *bmp2;
388 
389    /* Read the bitmaps. */
390    bmp = load_pcx("allegro.pcx", NULL);
391    CHECK(bmp, "load_pcx");
392    bmp2 = load_pcx("mysha.pcx", NULL);
393    CHECK(bmp2, "load_pcx");
394 
395    /* Write them with out custom vtable. */
396    fp = fopen("expackf.out", "wb");
397    CHECK(fp, "writing expackf.out");
398    f = pack_fopen_vtable(&stdio_vtable, fp);
399    CHECK(f, "writing with stdio");
400 
401    save_tga_pf(f, bmp, NULL);
402    save_bmp_pf(f, bmp2, NULL);
403 
404    destroy_bitmap(bmp);
405    destroy_bitmap(bmp2);
406    pack_fclose(f);
407 
408    /* Now read them in again with our custom vtable. */
409    fp = fopen("expackf.out", "rb");
410    CHECK(fp, "fopen");
411    f = pack_fopen_vtable(&stdio_vtable, fp);
412    CHECK(f, "reading from stdio");
413 
414    /* Note: in general you would need to implement a "chunking" system
415     * that knows where the boundary of each file is. Many file format
416     * loaders will happily read everything to the end of the file,
417     * whereas others stop reading as soon as they have all the essential
418     * data (e.g. there may be some metadata at the end of the file).
419     * Concatenating bare files together only works in examples programs.
420     */
421    bmp = load_tga_pf(f, NULL);
422    CHECK(bmp, "load_tga_pf");
423    bmp2 = load_bmp_pf(f, NULL);
424    CHECK(bmp2, "load_bmp_pf");
425 
426    blit(bmp, screen, 0, 0, 0, 0, bmp->w, bmp->h);
427    textprintf_ex(screen, font, bmp2->w + 8, 8, -1, -1,
428       "\"allegro.pcx\" (as tga)");
429    textprintf_ex(screen, font, bmp2->w + 8, 8 + 20, -1, -1,
430       "wrote with stdio functions");
431 
432    blit(bmp2, screen, 0, 0, 0, bmp->h + 8, bmp2->w, bmp2->h);
433    textprintf_ex(screen, font, bmp2->w + 8, bmp->h + 8, -1, -1,
434       "\"mysha.pcx\" (as bmp)");
435    textprintf_ex(screen, font, bmp2->w + 8, bmp->h + 8 + 20, -1, -1,
436       "wrote with stdio functions");
437 
438    destroy_bitmap(bmp);
439    destroy_bitmap(bmp2);
440    pack_fclose(f);
441 
442    next();
443 }
444 
445 /*----------------------------------------------------------------------*/
446 
main(void)447 int main(void)
448 {
449    if (allegro_init() != 0)
450       return 1;
451    install_keyboard();
452    set_color_depth(32);
453    if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0) != 0) {
454       set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
455       allegro_message("Unable to set a 640x480x32 windowed mode\n%s\n", allegro_error);
456       return 1;
457    }
458 
459    memread_test();
460 
461    stdio_read_test();
462 
463    stdio_seek_test();
464 
465    stdio_write_test();
466 
467    return 0;
468 }
469 
470 END_OF_MAIN()
471