1 #include <stdlib.h>
2 #include <string.h>
3 
4 #include "shadowdive/internal/reader.h"
5 #include "shadowdive/internal/writer.h"
6 #include "shadowdive/error.h"
7 #include "shadowdive/rec.h"
8 
sd_rec_extra_len(int key)9 int sd_rec_extra_len(int key) {
10     switch(key) {
11         case 2:
12         case 3:
13         case 5:
14             return 1;
15         case 6:
16             return 60;
17         case 10:
18         case 18:
19             return 8;
20     }
21     return 0;
22 }
23 
sd_rec_create(sd_rec_file * rec)24 int sd_rec_create(sd_rec_file *rec) {
25     if(rec == NULL) {
26         return SD_INVALID_INPUT;
27     }
28     memset(rec, 0, sizeof(sd_rec_file));
29     sd_pilot_create(&rec->pilots[0].info);
30     sd_pilot_create(&rec->pilots[1].info);
31     return SD_SUCCESS;
32 }
33 
sd_rec_free(sd_rec_file * rec)34 void sd_rec_free(sd_rec_file *rec) {
35     if(rec == NULL) return;
36     sd_pilot_free(&rec->pilots[0].info);
37     sd_pilot_free(&rec->pilots[1].info);
38     if(rec->moves) {
39         for(int i = 0; i < rec->move_count; i++) {
40             free(rec->moves[i].extra_data);
41         }
42         free(rec->moves);
43     }
44 }
45 
sd_rec_load(sd_rec_file * rec,const char * file)46 int sd_rec_load(sd_rec_file *rec, const char *file) {
47     int ret = SD_FILE_PARSE_ERROR;
48     if(rec == NULL || file == NULL) {
49         return SD_INVALID_INPUT;
50     }
51 
52     sd_reader *r = sd_reader_open(file);
53     if(!r) {
54         return SD_FILE_OPEN_ERROR;
55     }
56 
57     // Make sure we have at least this much data
58     if(sd_reader_filesize(r) < 1224) {
59         goto error_0;
60     }
61 
62     // Read pilot data
63     for(int i = 0; i < 2; i++) {
64         // Read pilot data
65         sd_pilot_create(&rec->pilots[i].info);
66         if((ret = sd_pilot_load(r, &rec->pilots[i].info)) != SD_SUCCESS) { goto error_0; }
67         rec->pilots[i].unknown_a = sd_read_ubyte(r);
68         rec->pilots[i].unknown_b = sd_read_uword(r);
69         sd_palette_create(&rec->pilots[i].pal);
70         sd_palette_load_range(r, &rec->pilots[i].pal, 0, 48);
71         rec->pilots[i].has_photo = sd_read_ubyte(r);
72         sd_sprite_create(&rec->pilots[i].photo);
73         if(rec->pilots[i].has_photo) {
74             if((ret = sd_sprite_load(r, &rec->pilots[i].photo)) != SD_SUCCESS) {
75                 goto error_0;
76             }
77         }
78     }
79 
80     // Scores
81     for(int i = 0; i < 2; i++)
82         rec->scores[i] = sd_read_udword(r);
83 
84     // Other flags
85     rec->unknown_a = sd_read_byte(r);
86     rec->unknown_b = sd_read_byte(r);
87     rec->unknown_c = sd_read_byte(r);
88     rec->throw_range = sd_read_word(r);
89     rec->hit_pause = sd_read_word(r);
90     rec->block_damage = sd_read_word(r);
91     rec->vitality = sd_read_word(r);
92     rec->jump_height = sd_read_word(r);
93     rec->unknown_i = sd_read_word(r);
94     rec->unknown_j = sd_read_word(r);
95     rec->unknown_k = sd_read_word(r);
96     uint32_t in = sd_read_udword(r);
97     rec->knock_down = (in >> 0 ) & 0x03; // 00000000 00000000 00000000 00000011 (2)
98     rec->rehit_mode = (in >> 2 ) & 0x01; // 00000000 00000000 00000000 00000100 (1)
99     rec->def_throws = (in >> 3 ) & 0x01; // 00000000 00000000 00000000 00001000 (1)
100     rec->arena_id =   (in >> 4 ) & 0x1F; // 00000000 00000000 00000001 11110000 (5)
101     rec->power[0] =   (in >> 9 ) & 0x1F; // 00000000 00000000 00111110 00000000 (5)
102     rec->power[1] =   (in >> 14) & 0x1F; // 00000000 00000111 11000000 00000000 (5)
103     rec->hazards =    (in >> 19) & 0x01; // 00000000 00001000 00000000 00000000 (1)
104     rec->round_type = (in >> 20) & 0x03; // 00000000 00110000 00000000 00000000 (2)
105     rec->unknown_l =  (in >> 22) & 0x03; // 00000000 11000000 00000000 00000000 (2)
106     rec->hyper_mode = (in >> 24) & 0x01; // 00000001 00000000 00000000 00000000 (1)
107     rec->unknown_m = sd_read_byte(r);
108 
109     // Allocate enough space for the record blocks
110     // This will be reduced later when we know the ACTUAL count
111     size_t rsize = sd_reader_filesize(r) - sd_reader_pos(r);
112     rec->move_count = rsize / 7;
113     rec->moves = calloc(rec->move_count, sizeof(sd_rec_move));
114 
115     // Read blocks
116     for(int i = 0; i < rec->move_count; i++) {
117         rec->moves[i].tick = sd_read_udword(r);
118         rec->moves[i].lookup_id = sd_read_ubyte(r);
119         rec->moves[i].player_id = sd_read_ubyte(r);
120         int extra_length = sd_rec_extra_len(rec->moves[i].lookup_id);
121         if(extra_length > 0) {
122             uint8_t action = sd_read_ubyte(r);
123             rec->moves[i].raw_action = action;
124 
125             // Parse real action key
126             rec->moves[i].action = SD_ACT_NONE;
127             if(action & 1) {
128                 rec->moves[i].action |= SD_ACT_PUNCH;
129             }
130             if(action & 2) {
131                 rec->moves[i].action |= SD_ACT_KICK;
132             }
133             switch(action & 0xF0) {
134                 case 16: rec->moves[i].action |= SD_ACT_UP; break;
135                 case 32: rec->moves[i].action |= (SD_ACT_UP|SD_ACT_RIGHT); break;
136                 case 48: rec->moves[i].action |= SD_ACT_RIGHT; break;
137                 case 64: rec->moves[i].action |= (SD_ACT_DOWN|SD_ACT_RIGHT); break;
138                 case 80: rec->moves[i].action |= SD_ACT_DOWN; break;
139                 case 96: rec->moves[i].action |= (SD_ACT_DOWN|SD_ACT_LEFT); break;
140                 case 112: rec->moves[i].action |= SD_ACT_LEFT; break;
141                 case 128: rec->moves[i].action |= (SD_ACT_UP|SD_ACT_LEFT); break;
142             }
143 
144             // We already read the action key, so minus one.
145             int unknown_len = extra_length - 1;
146             if(unknown_len > 0) {
147                 rec->moves[i].extra_data = malloc(unknown_len);
148                 sd_read_buf(r, rec->moves[i].extra_data, unknown_len);
149                 rec->move_count--;
150             }
151         }
152     }
153 
154     // Okay, not reduce the allocated memory to match what we actually need
155     // Realloc should keep our old data intact
156     rec->moves = realloc(rec->moves, rec->move_count * sizeof(sd_rec_move));
157 
158     // Close & return
159     sd_reader_close(r);
160     return SD_SUCCESS;
161 
162 error_0:
163     sd_reader_close(r);
164     return ret;
165 }
166 
sd_rec_save(sd_rec_file * rec,const char * file)167 int sd_rec_save(sd_rec_file *rec, const char *file) {
168     sd_writer *w;
169 
170     if(rec == NULL || file == NULL) {
171         return SD_INVALID_INPUT;
172     }
173 
174     if(!(w = sd_writer_open(file))) {
175         return SD_FILE_OPEN_ERROR;
176     }
177 
178     // Write pilots, palettes, etc.
179     for(int i = 0; i < 2; i++) {
180         sd_pilot_save(w, &rec->pilots[i].info);
181         sd_write_ubyte(w, rec->pilots[i].unknown_a);
182         sd_write_uword(w, rec->pilots[i].unknown_b);
183         sd_palette_save_range(w, &rec->pilots[i].pal, 0, 48);
184         sd_write_ubyte(w, rec->pilots[i].has_photo);
185         if(rec->pilots[i].has_photo) {
186             sd_sprite_save(w, &rec->pilots[i].photo);
187         }
188     }
189 
190     // Scores
191     for(int i = 0; i < 2; i++)
192         sd_write_udword(w, rec->scores[i]);
193 
194     // Other header data
195     sd_write_byte(w, rec->unknown_a);
196     sd_write_byte(w, rec->unknown_b);
197     sd_write_byte(w, rec->unknown_c);
198     sd_write_word(w, rec->throw_range);
199     sd_write_word(w, rec->hit_pause);
200     sd_write_word(w, rec->block_damage);
201     sd_write_word(w, rec->vitality);
202     sd_write_word(w, rec->jump_height);
203     sd_write_word(w, rec->unknown_i);
204     sd_write_word(w, rec->unknown_j);
205     sd_write_word(w, rec->unknown_k);
206     uint32_t out = 0;
207     out |= (rec->knock_down & 0x3) << 0;
208     out |= (rec->rehit_mode & 0x1) << 2;
209     out |= (rec->def_throws & 0x1) << 3;
210     out |= (rec->arena_id & 0x1F) << 4;
211     out |= (rec->power[0] & 0x1F) << 9;
212     out |= (rec->power[1] & 0x1F) << 14;
213     out |= (rec->hazards & 0x1) << 19;
214     out |= (rec->round_type & 0x3) << 20;
215     out |= (rec->unknown_l & 0x3) << 22;
216     out |= (rec->hyper_mode & 0x1) << 24;
217     sd_write_udword(w, out);
218     sd_write_byte(w, rec->unknown_m);
219 
220     // Move records
221     for(int i = 0; i < rec->move_count; i++) {
222         sd_write_udword(w, rec->moves[i].tick);
223         sd_write_ubyte(w, rec->moves[i].lookup_id);
224         sd_write_ubyte(w, rec->moves[i].player_id);
225 
226         int extra_length = sd_rec_extra_len(rec->moves[i].lookup_id);
227         if(extra_length > 0) {
228             // Write action information
229             uint8_t raw_action = 0;
230             switch(rec->moves[i].action & SD_MOVE_MASK) {
231                 case (SD_ACT_UP): raw_action = 16; break;
232                 case (SD_ACT_UP|SD_ACT_RIGHT): raw_action = 32; break;
233                 case (SD_ACT_RIGHT): raw_action = 48; break;
234                 case (SD_ACT_DOWN|SD_ACT_RIGHT): raw_action = 64; break;
235                 case (SD_ACT_DOWN): raw_action = 80; break;
236                 case (SD_ACT_DOWN|SD_ACT_LEFT): raw_action = 96; break;
237                 case (SD_ACT_LEFT): raw_action = 112; break;
238                 case (SD_ACT_UP|SD_ACT_LEFT): raw_action = 128; break;
239             }
240             if(rec->moves[i].action & SD_ACT_PUNCH)
241                 raw_action |= 1;
242             if(rec->moves[i].action & SD_ACT_KICK)
243                 raw_action |= 2;
244             sd_write_ubyte(w, raw_action);
245 
246             // If there is more extra data, write it
247             int unknown_len = extra_length - 1;
248             if(unknown_len > 0) {
249                 sd_write_buf(w, rec->moves[i].extra_data, unknown_len);
250             }
251         }
252    }
253 
254     sd_writer_close(w);
255     return SD_SUCCESS;
256 }
257 
sd_rec_delete_action(sd_rec_file * rec,unsigned int number)258 int sd_rec_delete_action(sd_rec_file *rec, unsigned int number) {
259     if(rec == NULL || number >= rec->move_count) {
260         return SD_INVALID_INPUT;
261     }
262 
263     // Only move if we are not deleting the last entry
264     if(number < (rec->move_count - 1)) {
265         memmove(
266             rec->moves + number,
267             rec->moves + number + 1,
268             (rec->move_count - number - 1) * sizeof(sd_rec_move));
269     }
270 
271     // Resize to save memory
272     rec->move_count--;
273     rec->moves = realloc(rec->moves, rec->move_count * sizeof(sd_rec_move));
274 
275     if(rec->moves == NULL) {
276         return SD_OUT_OF_MEMORY;
277     }
278     return SD_SUCCESS;
279 }
280 
sd_rec_insert_action(sd_rec_file * rec,unsigned int number,const sd_rec_move * move)281 int sd_rec_insert_action(sd_rec_file *rec, unsigned int number, const sd_rec_move *move) {
282     if(rec == NULL) {
283         return SD_INVALID_INPUT;
284     }
285     if(number > rec->move_count) {
286         return SD_INVALID_INPUT;
287     }
288 
289     // Resize
290     rec->moves = realloc(rec->moves, (rec->move_count+1) * sizeof(sd_rec_move));
291     if(rec->moves == NULL) {
292         return SD_OUT_OF_MEMORY;
293     }
294 
295     // Only move if we are inserting, not appending
296     // when number == move_count-1, we are pushing the last entry forwards by one
297     // when number == move_count, we are pushing to the end.
298     if(number < rec->move_count) {
299         memmove(
300             rec->moves + number + 1,
301             rec->moves + number,
302             (rec->move_count - number) * sizeof(sd_rec_move));
303     }
304     memcpy(
305         rec->moves + number,
306         move,
307         sizeof(sd_rec_move));
308 
309     rec->move_count++;
310     return SD_SUCCESS;
311 }
312