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