fixed RLEsprite bug
[eradicate] / src / sprite.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <assert.h>
6 #include "inttypes.h"
7 #include "sprite.h"
8 #include "util.h"
9
10
11 #define SOP_OP(op)      ((op) & 0xff)
12 #define SOP_LEN(op)     ((op) >> 16)
13
14 #pragma pack (push, 1)
15 struct file_header {
16         char magic[8];
17         uint16_t width, height, bpp;
18         uint16_t count;
19 } PACKED;
20 #pragma pack (pop)
21
22
23 static int read_sprite(struct sprite *spr, int pixsz, FILE *fp);
24
25
26 void destroy_sprites(struct sprites *ss)
27 {
28         int i;
29
30         for(i=0; i<ss->num_sprites; i++) {
31                 free(ss->sprites[i].ops);
32         }
33         free(ss->sprites);
34 }
35
36 int load_sprites(struct sprites *ss, const char *fname)
37 {
38         int i;
39         FILE *fp;
40         struct file_header hdr;
41
42         ss->sprites = 0;
43
44         if(!(fp = fopen(fname, "rb"))) {
45                 fprintf(stderr, "failed to load sprites from %s: %s\n", fname, strerror(errno));
46                 return -1;
47         }
48         if(fread(&hdr, sizeof hdr, 1, fp) <= 0) {
49                 fprintf(stderr, "unexpected EOF while reading from %s\n", fname);
50                 goto err;
51         }
52         if(memcmp(hdr.magic, "RLESPRIT", sizeof hdr.magic) != 0) {
53                 fprintf(stderr, "invalid magic in %s\n", fname);
54                 goto err;
55         }
56
57         ss->width = hdr.width;
58         ss->height = hdr.height;
59         ss->bpp = hdr.bpp;
60         ss->num_sprites = hdr.count;
61
62         if(!(ss->sprites = malloc(hdr.count * sizeof *ss->sprites))) {
63                 fprintf(stderr, "failed to allocate %d sprites for %s\n", hdr.count, fname);
64                 goto err;
65         }
66
67         for(i=0; i<ss->num_sprites; i++) {
68                 if(read_sprite(ss->sprites + i, (hdr.bpp + 7) / 8, fp) == -1) {
69                         goto err;
70                 }
71         }
72
73         fclose(fp);
74         return 0;
75
76 err:
77         free(ss->sprites);
78         fclose(fp);
79         return -1;
80 }
81
82 static int read_sprite(struct sprite *spr, int pixsz, FILE *fp)
83 {
84         int i, idx, max_ops, newmax, len, bufsz;
85         void *tmp;
86         uint32_t op;
87
88         spr->ops = 0;
89         spr->num_ops = max_ops = 0;
90
91         do {
92                 /* read the op */
93                 if(fread(&op, sizeof op, 1, fp) <= 0) {
94                         free(spr->ops);
95                         return -1;
96                 }
97
98                 /* realloc ops array if necessary */
99                 if(spr->num_ops >= max_ops) {
100                         newmax = max_ops ? max_ops << 1 : 16;
101                         if(!(tmp = realloc(spr->ops, newmax * sizeof *spr->ops))) {
102                                 fprintf(stderr, "failed to add sprite op (newmax: %d)\n", newmax);
103                                 goto err;
104                         }
105                         spr->ops = tmp;
106                         max_ops = newmax;
107                 }
108
109                 /* append */
110                 idx = spr->num_ops++;
111                 spr->ops[idx].op = SOP_OP(op);
112                 len = SOP_LEN(op);
113                 bufsz = len * pixsz;
114                 spr->ops[idx].size = bufsz;
115
116                 /* if the op was copy, we need to grab the pixel data */
117                 if(SOP_OP(op) == SOP_COPY) {
118                         if(!(tmp = malloc(bufsz))) {
119                                 fprintf(stderr, "failed to allocate sprite pixel data (%d bytes)\n", bufsz);
120                                 goto err;
121                         }
122                         if(fread(tmp, 1, bufsz, fp) < bufsz) {
123                                 fprintf(stderr, "unexpected EOF while trying to read sprite data (%d pixels)\n", len);
124                                 goto err;
125                         }
126                         spr->ops[idx].data = tmp;
127                 } else {
128                         spr->ops[idx].data = 0;
129                 }
130
131         } while(SOP_OP(op) != SOP_END);
132
133         return 0;
134
135 err:
136         for(i=0; i<spr->num_ops; i++) {
137                 free(spr->ops[i].data);
138         }
139         free(spr->ops);
140         return -1;
141 }
142
143 void draw_sprite(void *dest, int fbpitch, struct sprites *ss, int idx)
144 {
145         struct sprite_op *sop = ss->sprites[idx].ops;
146         unsigned char *fbptr = dest;
147         int xoffs = 0;
148
149         for(;;) {
150                 assert((xoffs & 1) == 0);
151                 switch(sop->op) {
152                 case SOP_END:
153                         return;
154                 case SOP_ENDL:
155                         fbptr += fbpitch;
156                         xoffs = 0;
157                         break;
158                 case SOP_SKIP:
159                         xoffs += sop->size;
160                         break;
161                 case SOP_COPY:
162                         memcpy(fbptr + xoffs, sop->data, sop->size);
163                         xoffs += sop->size;
164                         break;
165                 default:
166                         break;
167                 }
168                 sop++;
169         }
170 }