Branch data Line data Source code
1 : : #include "asar.h"
2 : : #include "crc32.h"
3 : :
4 : : #include "platform/file-helpers.h"
5 : :
6 : : mapper_t mapper=lorom;
7 : : int sa1banks[8]={0<<20, 1<<20, -1, -1, 2<<20, 3<<20, -1, -1};
8 : : const unsigned char * romdata= nullptr; // NOTE: Changed into const to prevent direct write access - use writeromdata() functions below
9 : : int romlen;
10 : : static bool header;
11 : : static FileHandleType thisfile = InvalidFileHandle;
12 : :
13 : : asar_error_id openromerror;
14 : :
15 : : autoarray<writtenblockdata> writtenblocks;
16 : : // not immediately put into writtenblocks to allow the freespace finder to use
17 : : // the reclaimed space.
18 : : autoarray<writtenblockdata> cleared_rats_tag_blocks;
19 : :
20 : : // RPG Hacker: Uses binary search to find the insert position of our ROM write
21 : 315490 : static int findromwritepos(int snesoffset, int searchstartpos, int searchendpos)
22 : : {
23 [ + + ]: 471894 : if (searchendpos == searchstartpos)
24 : : {
25 : 79543 : return searchstartpos;
26 : : }
27 : :
28 : 312808 : int centerpos = searchstartpos + ((searchendpos - searchstartpos) / 2);
29 : :
30 [ + + ]: 312808 : if (writtenblocks[centerpos].snesoffset >= snesoffset)
31 : : {
32 : 64634 : return findromwritepos(snesoffset, searchstartpos, centerpos);
33 : : }
34 : :
35 : 183540 : return findromwritepos(snesoffset, centerpos + 1, searchendpos);
36 : : }
37 : :
38 : :
39 : 158626 : static void addromwriteforbank(int snesoffset, int numbytes)
40 : : {
41 : 158626 : int currentbank = (snesoffset & 0xFF0000);
42 : :
43 : 158626 : int insertpos = findromwritepos(snesoffset, 0, writtenblocks.count);
44 : :
45 [ + + + + ]: 158168 : if (insertpos > 0 && (writtenblocks[insertpos - 1].snesoffset & 0xFF0000) == currentbank
46 [ + + + + : 316602 : && writtenblocks[insertpos - 1].snesoffset + writtenblocks[insertpos - 1].numbytes >= snesoffset)
+ - + + +
+ ]
47 : : {
48 : : // Merge if we overlap with a preceding block
49 : 157102 : int firstend = writtenblocks[insertpos - 1].snesoffset + writtenblocks[insertpos - 1].numbytes;
50 : 157102 : int secondend = snesoffset + numbytes;
51 : :
52 [ + + ]: 78551 : int newend = (firstend > secondend ? firstend : secondend);
53 : :
54 : 157102 : numbytes = newend - writtenblocks[insertpos - 1].snesoffset;
55 : 157102 : snesoffset = writtenblocks[insertpos - 1].snesoffset;
56 : :
57 : 157102 : writtenblocks.remove(insertpos - 1);
58 : 78551 : insertpos -= 1;
59 : : }
60 : :
61 [ + + + + ]: 207315 : while (insertpos < writtenblocks.count && (writtenblocks[insertpos].snesoffset & 0xFF0000) == currentbank
62 [ + + + + : 254255 : && snesoffset + numbytes >= writtenblocks[insertpos].snesoffset)
+ + + + ]
63 : : {
64 : : // Merge if we overlap with a succeeding block
65 : 409 : int firstend = snesoffset + numbytes;
66 : 818 : int secondend = writtenblocks[insertpos].snesoffset + writtenblocks[insertpos].numbytes;
67 : :
68 [ + + ]: 409 : int newend = (firstend > secondend ? firstend : secondend);
69 : :
70 : 818 : numbytes = newend - snesoffset;
71 : :
72 : 818 : writtenblocks.remove(insertpos);
73 : : }
74 : :
75 : : // Insert ROM write
76 : : writtenblockdata blockdata;
77 : 158626 : blockdata.snesoffset = snesoffset;
78 : 158626 : blockdata.pcoffset = snestopc(snesoffset);
79 : 158626 : blockdata.numbytes = numbytes;
80 : :
81 : 158626 : writtenblocks.insert(insertpos, blockdata);
82 : 158626 : }
83 : :
84 : :
85 : 158558 : void addromwrite(int pcoffset, int numbytes)
86 : : {
87 : 158558 : int snesaddr = pctosnes(pcoffset);
88 : 79279 : int bytesleft = numbytes;
89 : :
90 : : // RPG Hacker: Some kind of witchcraft which I actually hope works as intended
91 : : // Basically, the purpose of this is to sort all ROM writes into banks for the sake of cleanness
92 : :
93 [ + + ]: 158626 : while (((snesaddr >> 16) & 0xFF) != (((snesaddr + bytesleft) >> 16) & 0xFF))
94 : : {
95 : 68 : int bytesinbank = ((snesaddr + 0x10000) & 0xFF0000) - snesaddr;
96 : :
97 : 68 : addromwriteforbank(snesaddr, bytesinbank);
98 : :
99 : 68 : pcoffset += bytesinbank;
100 : 68 : snesaddr = pctosnes(pcoffset);
101 : 68 : bytesleft -= bytesinbank;
102 : : }
103 : :
104 : 158558 : addromwriteforbank(snesaddr, bytesleft);
105 : 158558 : }
106 : :
107 : 224 : void writeromdata(int pcoffset, const void * indata, int numbytes)
108 : : {
109 : 224 : memcpy(const_cast<unsigned char*>(romdata) + pcoffset, indata, (size_t)numbytes);
110 : 224 : addromwrite(pcoffset, numbytes);
111 : 224 : }
112 : :
113 : 149470 : void writeromdata_byte(int pcoffset, unsigned char indata)
114 : : {
115 : 149470 : memcpy(const_cast<unsigned char*>(romdata) + pcoffset, &indata, 1);
116 : 149470 : addromwrite(pcoffset, 1);
117 : 149470 : }
118 : :
119 : 2 : void writeromdata_bytes(int pcoffset, unsigned char indata, int numbytes)
120 : : {
121 : 2 : memset(const_cast<unsigned char*>(romdata) + pcoffset, indata, (size_t)numbytes);
122 : 2 : addromwrite(pcoffset, numbytes);
123 : 2 : }
124 : :
125 : 70 : int ratsstart(int snesaddr)
126 : : {
127 : 70 : int pcaddr=snestopc(snesaddr);
128 [ + + ]: 70 : if (pcaddr<0x7FFF8) return -1;
129 : 62 : const unsigned char * start=romdata+pcaddr-0x10000;
130 [ + - ]: 1028 : for (int i=0x10000;i>=0;i--)
131 : : {
132 [ + + ]: 1028 : if (!strncmp((const char*)start+i, "STAR", 4) &&
133 [ + - + - ]: 62 : (start[i+4]^start[i+6])==0xFF && (start[i+5]^start[i+7])==0xFF)
134 : : {
135 [ + - ]: 62 : if ((start[i+4]|(start[i+5]<<8))>0x10000-i-8-1) return pctosnes((int)(start-romdata+i));
136 : 0 : return -1;
137 : : }
138 : : }
139 : 0 : return -1;
140 : : }
141 : :
142 : 6 : static void handleprot(int loc, char * name, int len, const unsigned char * contents)
143 : : {
144 : : (void)loc; // RPG Hacker: Silence "unused argument" warning.
145 : :
146 [ + - ]: 6 : if (!strncmp(name, "PROT", 4))
147 : : {
148 : 6 : memcpy(name, "NULL", 4);//to block recursion, in case someone is an idiot
149 [ + + ]: 6 : if (len%3) return;
150 : 6 : len/=3;
151 [ + + ]: 14 : for (int i=0;i<len;i++) removerats((contents[(i*3)+0] )|(contents[(i*3)+1]<<8 )|(contents[(i*3)+2]<<16), 0x00);
152 : : }
153 : : }
154 : :
155 : 16 : void removerats(int snesaddr, unsigned char clean_byte)
156 : : {
157 : 16 : int addr=ratsstart(snesaddr);
158 [ + + ]: 16 : if (addr<0) return;
159 : : // randomdude999: don't forget bank borders
160 : 14 : WalkMetadata(pctosnes(snestopc(addr)+8), handleprot);
161 : 14 : addr=snestopc(addr);
162 : : // don't use writeromdata() because this must not go to the writtenblocks list.
163 : 14 : int len = (romdata[addr+4]|(romdata[addr+5]<<8))+9;
164 : 14 : memset(const_cast<unsigned char*>(romdata) + addr, clean_byte, len);
165 : 14 : cleared_rats_tag_blocks[cleared_rats_tag_blocks.count] = writtenblockdata{addr, 0, len};
166 : : //for (int i=(romdata[addr+4]|(romdata[addr+5]<<8))+8;i>=0;i--) writeromdata_byte(addr+i, clean_byte);
167 : : }
168 : :
169 : 220 : void handle_cleared_rats_tags()
170 : : {
171 [ + + ]: 234 : for(int i = 0; i < cleared_rats_tag_blocks.count; i++) {
172 : 7 : auto & block = cleared_rats_tag_blocks[i];
173 : 14 : addromwrite(block.pcoffset, block.numbytes);
174 : : }
175 : 220 : cleared_rats_tag_blocks.reset();
176 : 220 : }
177 : :
178 : 484 : static inline int trypcfreespace(int start, int end, int size, int banksize, int minalign, unsigned char freespacebyte, bool write_rats)
179 : : {
180 [ + + ]: 19868 : while (start+size<=end)
181 : : {
182 [ + + ]: 19838 : if (write_rats &&
183 [ + + ]: 19824 : ((start+8)&~banksize)!=((start+size-1)&~banksize&0xFFFFFF)//if the contents won't fit in this bank...
184 : 2 : &&
185 [ + - ]: 2 : (start&banksize&0xFFFFF8)!=(banksize&0xFFFFF8)//and the RATS tag can't fit in the bank either...
186 : : )
187 : : {
188 : 2 : start&=~banksize&0xFFFFFF;//round it down to the start of the bank,
189 : 2 : start|=banksize&0xFFFFF8;//then round it up to the end minus the RATS tag...
190 : 2 : continue;
191 : : }
192 [ + + ]: 9925 : else if(!write_rats &&
193 [ - + ]: 14 : (start&~banksize) != ((start+size-1)&~banksize))
194 : : {
195 : : // go to next bank.
196 : 0 : start = (start|banksize)+1;
197 : 0 : continue;
198 : : }
199 [ + + ]: 19836 : if (minalign)
200 : : {
201 : 8 : start&=~minalign&0xFFFFFF;
202 : 8 : start|=minalign&0xFFFFF8;
203 : : }
204 [ + + ]: 19836 : if (!strncmp((const char*)romdata+start, "STAR", 4) &&
205 [ + + + + ]: 19376 : (romdata[start+4]^romdata[start+6])==0xFF && (romdata[start+5]^romdata[start+7])==0xFF)
206 : : {
207 : 19376 : start+=(romdata[start+4]|(romdata[start+5]<<8))+1+8;
208 : 19376 : continue;
209 : : }
210 : 230 : bool bad=false;
211 [ + + ]: 4333524 : for (int i=0;i<size;i++)
212 : : {
213 [ - + ]: 4333064 : if (romdata[start+i]!=freespacebyte)
214 : : {
215 : : // TheBiob: fix freedata align freezing.
216 [ # # # # ]: 0 : if ((start & minalign) == 0x7FF8 && i < 8) i = 8;
217 : 0 : start+=i;
218 [ # # ]: 0 : if (!i) start++;//this could check for a rats tag instead, but somehow I think this will give better performance.
219 : 0 : bad=true;
220 : 0 : break;
221 : : }
222 : : }
223 [ - + ]: 230 : if (bad) continue;
224 : : // check against collisions with any written blocks.
225 : : // written blocks go through like 3x snes->pc->snes and are then sorted by snes.
226 : : // todo: this is janky..... should sort writtenblocks by pc maybe??
227 : 460 : int snesstart = pctosnes(start);
228 : : // this gives the index of the first block whose snespos is >= start.
229 : 460 : int writtenblock_pos = findromwritepos(snesstart, 0, writtenblocks.count);
230 : : // we might need to check the preceding block too.
231 : : // (but we don't need to check any older ones, as blocks are merged together)
232 [ + + ]: 460 : if(writtenblock_pos > 0) writtenblock_pos--;
233 [ + + ]: 920 : for(; writtenblock_pos < writtenblocks.count; writtenblock_pos++)
234 : : {
235 : 234 : auto & block = writtenblocks[writtenblock_pos];
236 [ + + ]: 468 : if(snesstart < block.snesoffset+block.numbytes
237 [ + + ]: 8 : && snesstart+size > block.snesoffset)
238 : : {
239 : 6 : start = block.pcoffset + block.numbytes;
240 : 3 : bad = true;
241 : 3 : break;
242 : : }
243 : : // blocks are sorted by snesoffset, so if they start after our end we know there are no more that could intersect
244 [ + + ]: 462 : if(block.snesoffset > snesstart+size) break;
245 : : }
246 [ + + ]: 233 : if (bad) continue;
247 [ + + ]: 454 : if(write_rats)
248 : : {
249 : 444 : size-=8;
250 [ + - ]: 444 : if (size) size--;//rats tags eat one byte more than specified for some reason
251 : 444 : writeromdata_byte(start+0, 'S');
252 : 444 : writeromdata_byte(start+1, 'T');
253 : 444 : writeromdata_byte(start+2, 'A');
254 : 444 : writeromdata_byte(start+3, 'R');
255 : 444 : writeromdata_byte(start+4, (unsigned char)(size&0xFF));
256 : 444 : writeromdata_byte(start+5, (unsigned char)((size>>8)&0xFF));
257 : 444 : writeromdata_byte(start+6, (unsigned char)((size&0xFF)^0xFF));
258 : 444 : writeromdata_byte(start+7, (unsigned char)(((size>>8)&0xFF)^0xFF));
259 : 444 : return start+8;
260 : : }
261 : : else
262 : : {
263 : : // not sure if needs to be done here,
264 : : // but it needs to be done somewhere
265 : 10 : addromwrite(start, size);
266 : 10 : return start;
267 : : }
268 : : }
269 : 15 : return -1;
270 : : }
271 : :
272 : : //This function finds a block of freespace. -1 means "no freespace found", anything else is a PC address.
273 : : //isforcode=false tells it to favor banks 40+, true tells it to avoid them entirely.
274 : : //It automatically adds a RATS tag.
275 : :
276 : 496 : int getpcfreespace(int size, int target_bank, bool autoexpand, bool respectbankborders, bool align, unsigned char freespacebyte, bool write_rats)
277 : : {
278 : : // TODO: probably should error if specifying target_bank and align, or target_bank and respectbankborders
279 [ + + ]: 496 : if (!size) return 0x1234;//in case someone protects zero bytes for some dumb reason.
280 : : //You can write zero bytes to anywhere, so I'll just return something that removerats will ignore.
281 [ - + ]: 454 : if (size>0x10000) return -1;
282 : 454 : bool isforcode = target_bank == -2;
283 [ + + ]: 454 : if(write_rats)
284 : 444 : size+=8;
285 : :
286 : 6 : auto find_for_fixed_bank = [&](int banksize, bool force_high_half = false) {
287 [ + - - + ]: 6 : if(!force_high_half && (target_bank & 0x40) == 0x40)
288 : 0 : return trypcfreespace(snestopc(target_bank<<16), min(romlen, snestopc(target_bank<<16)+0x10000), size, banksize, 0, freespacebyte, write_rats);
289 : : else
290 : 6 : return trypcfreespace(snestopc(target_bank<<16 | 0x8000), min(romlen, snestopc(target_bank<<16 | 0x8000)+0x8000), size, banksize, 0, freespacebyte, write_rats);
291 : 454 : };
292 : :
293 [ + + ]: 454 : if (mapper==lorom)
294 : : {
295 [ + + + - ]: 450 : if(target_bank >= 0) return find_for_fixed_bank(0x7fff);
296 [ + + + + ]: 444 : if (size>0x8008 && respectbankborders) return -1;
297 : 444 : rebootlorom:
298 [ + + + - ]: 474 : if (romlen>0x200000 && !isforcode)
299 : : {
300 [ - + + - : 56 : int pos=trypcfreespace(0x200000-8, (romlen<0x400000)?romlen:0x400000, size,
+ - ]
301 [ - + - - ]: 28 : respectbankborders?0x7FFF:0xFFFFFF, align?0x7FFF:(respectbankborders || size<32768)?0:0x7FFF, freespacebyte, write_rats);
302 [ + - ]: 28 : if (pos>=0) return pos;
303 : : }
304 [ + + + + : 891 : int pos=trypcfreespace(0x80000, (romlen<0x200000)?romlen:0x200000, size,
+ - ]
305 [ + + + + ]: 440 : respectbankborders?0x7FFF:0xFFFFFF, align?0x7FFF:(respectbankborders || size<32768)?0:0x7FFF, freespacebyte, write_rats);
306 [ + + ]: 446 : if (pos>=0) return pos;
307 [ + - ]: 30 : if (autoexpand)
308 : : {
309 : : if(0);
310 [ + + ]: 30 : else if (romlen==0x080000)
311 : : {
312 : 26 : romlen=0x100000;
313 : 26 : writeromdata_byte(snestopc(0x00FFD7), 0x0A);
314 : : }
315 [ + + ]: 4 : else if (romlen==0x100000)
316 : : {
317 : 2 : romlen=0x200000;
318 : 2 : writeromdata_byte(snestopc(0x00FFD7), 0x0B);
319 : : }
320 [ - + ]: 2 : else if (isforcode) return -1;//no point creating freespace that can't be used
321 [ - + - - ]: 2 : else if (romlen==0x200000 || romlen==0x300000)
322 : : {
323 : 2 : romlen=0x400000;
324 : 2 : writeromdata_byte(snestopc(0x00FFD7), 0x0C);
325 : : }
326 : 0 : else return -1;
327 : 15 : autoexpand=false;
328 : 30 : goto rebootlorom;
329 : : }
330 : : }
331 [ - + ]: 4 : if (mapper==hirom)
332 : : {
333 [ # # # # ]: 0 : if(target_bank >= 0) return find_for_fixed_bank(0xffff);
334 [ # # ]: 0 : if (isforcode)
335 : : {
336 [ # # ]: 0 : for(int i = 0x8000; i < min(romlen, 0x400000); i += 0x10000){
337 [ # # # # ]: 0 : int space = trypcfreespace(i, min(i+0x7FFF, romlen), size, 0x7FFF, align?0xFFFF:0, freespacebyte, write_rats);
338 [ # # ]: 0 : if(space != -1) return space;
339 : : }
340 : 0 : return -1;
341 : : }
342 [ # # # # ]: 0 : return trypcfreespace(0, romlen, size, 0xFFFF, align?0xFFFF:0, freespacebyte, write_rats);
343 : : }
344 [ - + ]: 4 : if (mapper==exlorom)
345 : : {
346 [ # # # # ]: 0 : if(target_bank >= 0) return find_for_fixed_bank(0x7fff);
347 : : // RPG Hacker: Not really 100% sure what to do here, but I suppose this simplified code will do
348 : : // and we won't need all the complicated stuff from LoROM above?
349 [ # # ]: 0 : if (isforcode)
350 : : {
351 [ # # # # ]: 0 : trypcfreespace(0, min(romlen, 0x200000), size, 0x7FFF, align?0x7FFF:0, freespacebyte, write_rats);
352 : : }
353 [ # # # # ]: 0 : return trypcfreespace(0, romlen, size, 0x7FFF, align ? 0x7FFF : 0, freespacebyte, write_rats);
354 : : }
355 [ - + ]: 4 : if (mapper==exhirom)
356 : : {
357 [ # # # # ]: 0 : if(target_bank >= 0) return find_for_fixed_bank(0xffff);
358 [ # # ]: 0 : if (isforcode)
359 : : {
360 [ # # # # ]: 0 : for(int i = 0x8000; i < romlen && i < 0x400000; i += 0x10000){
361 [ # # # # ]: 0 : int space = trypcfreespace(i, min(i+0x7FFF, romlen), size, 0x7FFF, align?0xFFFF:0, freespacebyte, write_rats);
362 [ # # ]: 0 : if(space != -1) return space;
363 : : }
364 : 0 : return -1;
365 : : }
366 [ # # # # ]: 0 : return trypcfreespace(0, romlen, size, 0xFFFF, align?0xFFFF:0, freespacebyte, write_rats);
367 : : }
368 [ + + ]: 4 : if (mapper==sfxrom)
369 : : {
370 [ - + - - ]: 2 : if(target_bank >= 0) return find_for_fixed_bank(0xffff);
371 [ - + ]: 2 : if (!isforcode) return -1;
372 : : // try not to overwrite smw stuff
373 [ + + + - ]: 3 : return trypcfreespace(0x80000, romlen, size, 0x7FFF, align?0x7FFF:0, freespacebyte, write_rats);
374 : : }
375 [ + - ]: 2 : if (mapper==sa1rom)
376 : : {
377 [ + + - - ]: 2 : if(target_bank >= 0) return find_for_fixed_bank(0xffff);
378 : 2 : rebootsa1rom:
379 : 2 : int nextbank=-1;
380 [ + + ]: 20 : for (int i=0;i<8;i++)
381 : : {
382 [ + + ]: 18 : if (i&2) continue;
383 [ + + ]: 10 : if (sa1banks[i]+0x100000>romlen)
384 : : {
385 [ + + + - ]: 8 : if (sa1banks[i]<=romlen && sa1banks[i]+0x100000>romlen) nextbank=sa1banks[i];
386 : 8 : continue;
387 : : }
388 [ + + - + : 3 : int pos=trypcfreespace(sa1banks[i]?sa1banks[i]:0x80000, sa1banks[i]+0x100000, size, 0x7FFF, align?0x7FFF:0, freespacebyte, write_rats);
+ - ]
389 [ + - ]: 2 : if (pos>=0) return pos;
390 : : }
391 [ + - + - ]: 2 : if (autoexpand && nextbank>=0)
392 : : {
393 : 2 : unsigned char x7FD7[]={0, 0x0A, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D};
394 : 2 : romlen=nextbank+0x100000;
395 : 2 : writeromdata_byte(0x7FD7, x7FD7[romlen>>20]);
396 : 1 : autoexpand=false;
397 : 2 : goto rebootsa1rom;
398 : : }
399 : : }
400 [ # # ]: 0 : if (mapper==bigsa1rom)
401 : : {
402 [ # # # # ]: 0 : if(target_bank >= 0) return find_for_fixed_bank(0xffff);
403 [ # # # # ]: 0 : if(!isforcode && romlen > 0x400000)
404 : : {
405 [ # # # # ]: 0 : int pos=trypcfreespace(0x400000, romlen, size, 0xFFFF, align?0xFFFF:0, freespacebyte, write_rats);
406 [ # # ]: 0 : if(pos>=0) return pos;
407 : : }
408 [ # # # # ]: 0 : int pos=trypcfreespace(0x080000, romlen, size, 0x7FFF, align?0x7FFF:0, freespacebyte, write_rats);
409 [ # # ]: 0 : if(pos>=0) return pos;
410 : : }
411 : 0 : return -1;
412 : : }
413 : :
414 : 14 : void WalkMetadata(int loc, void(*func)(int loc, char * name, int len, const unsigned char * contents))
415 : : {
416 : 14 : int pcoff=snestopc(loc);
417 [ + + ]: 14 : if (strncmp((const char*)romdata+pcoff-8, "STAR", 4)) return;
418 : 14 : const unsigned char * metadata=romdata+pcoff;
419 [ + + + - : 20 : while (is_upper(metadata[0]) && is_upper(metadata[1]) && is_upper(metadata[2]) && is_upper(metadata[3]))
+ - + - +
+ ]
420 : : {
421 [ + + ]: 10 : if (!strncmp((const char*)metadata, "STOP", 4))
422 : : {
423 : 2 : metadata=romdata+pcoff;
424 [ + - + - : 10 : while (is_upper(metadata[0]) && is_upper(metadata[1]) && is_upper(metadata[2]) && is_upper(metadata[3]))
+ - + - +
- ]
425 : : {
426 [ + + ]: 10 : if (!strncmp((const char*)metadata, "STOP", 4))
427 : : {
428 : 2 : break;
429 : : }
430 : 6 : func(pctosnes((int)(metadata-romdata)), (char*)const_cast<unsigned char*>(metadata), metadata[4], metadata+5);
431 : 6 : metadata+=5+metadata[4];
432 : : }
433 : 2 : break;
434 : : }
435 : 6 : metadata+=5+metadata[4];
436 : : }
437 : : }
438 : :
439 : 494 : int getsnesfreespace(int size, int target_bank, bool autoexpand, bool respectbankborders, bool align, unsigned char freespacebyte, bool write_rats)
440 : : {
441 : 494 : return pctosnes(getpcfreespace(size, target_bank, autoexpand, respectbankborders, align, freespacebyte, write_rats));
442 : : }
443 : :
444 : 230 : bool openrom(const char * filename, bool confirm)
445 : : {
446 : 230 : closerom();
447 : 230 : thisfile = open_file(filename, FileOpenMode_ReadWrite);
448 [ - + ]: 230 : if (thisfile == InvalidFileHandle)
449 : : {
450 : 0 : openromerror = error_id_open_rom_failed;
451 : 0 : return false;
452 : : }
453 : 230 : header=false;
454 [ + - ]: 230 : if (strlen(filename)>4)
455 : : {
456 : 230 : const char * fnameend=strchr(filename, '\0')-4;
457 : 230 : header=(!stricmp(fnameend, ".smc"));
458 : : }
459 : 230 : romlen=(int)get_file_size(thisfile)-(header*512);
460 [ - + ]: 230 : if (romlen<0) romlen=0;
461 : 230 : set_file_pos(thisfile, header*512);
462 : 230 : romdata=(unsigned char*)malloc(sizeof(unsigned char)*16*1024*1024);
463 : 230 : int truelen=(int)read_file(thisfile, (void*)romdata, (uint32_t)romlen);
464 [ - + ]: 230 : if (truelen!=romlen)
465 : : {
466 : 0 : openromerror = error_id_open_rom_failed;
467 : 0 : free(const_cast<unsigned char*>(romdata));
468 : 0 : return false;
469 : : }
470 : 230 : memset(const_cast<unsigned char*>(romdata)+romlen, 0x00, (size_t)(16*1024*1024-romlen));
471 [ - + - - : 230 : if (confirm && snestopc(0x00FFC0)+21<(int)romlen && strncmp((const char*)romdata+snestopc(0x00FFC0), "SUPER MARIOWORLD ", 21))
- - - + ]
472 : : {
473 : 0 : closerom(false);
474 [ # # ]: 0 : openromerror = header ? error_id_open_rom_not_smw_extension : error_id_open_rom_not_smw_header;
475 : 0 : return false;
476 : : }
477 : :
478 : 230 : romdata_r=(unsigned char*)malloc((size_t)romlen);
479 : 230 : romlen_r=romlen;
480 : 230 : memcpy((void*)romdata_r, romdata, (size_t)romlen);//recently allocated, dead
481 : :
482 : 230 : return true;
483 : : }
484 : :
485 : 460 : uint32_t closerom(bool save)
486 : : {
487 : 230 : uint32_t romCrc = 0;
488 [ + + + + : 460 : if (thisfile != InvalidFileHandle && save && romlen)
+ + ]
489 : : {
490 : 156 : set_file_pos(thisfile, header*512);
491 : 156 : write_file(thisfile, romdata, (uint32_t)romlen);
492 : :
493 : : // do a quick re-read of the header, and include that in the crc32 calculation if necessary
494 : : {
495 : 156 : uint8_t* filedata = (uint8_t*)malloc(sizeof(uint8_t) * (romlen + header * 512));
496 [ - + ]: 156 : if (header)
497 : : {
498 : 0 : set_file_pos(thisfile, 0u);
499 : 0 : read_file(thisfile, filedata, 512);
500 : : }
501 : 156 : memcpy(filedata + (header * 512), romdata, sizeof(uint8_t) * (size_t)romlen);
502 : 156 : romCrc = crc32(filedata, (unsigned int)(romlen + header * 512));
503 : 156 : free(filedata);
504 : : }
505 : : }
506 [ + + ]: 460 : if (thisfile != InvalidFileHandle) close_file(thisfile);
507 [ + + ]: 460 : if (romdata) free(const_cast<unsigned char*>(romdata));
508 : 460 : thisfile= InvalidFileHandle;
509 : 460 : romdata= nullptr;
510 : 460 : romdata_r = nullptr;
511 : 460 : romlen=0;
512 : 460 : return romCrc;
513 : : }
514 : :
515 : 222 : static unsigned int getchecksum()
516 : : {
517 : 111 : unsigned int checksum=0;
518 [ + + ]: 222 : if((romlen & (romlen-1)) == 0)
519 : : {
520 : : // romlen is a power of 2, just add up all the bytes
521 [ + + ]: 59769054 : for (int i=0;i<romlen;i++) checksum+=romdata[i];
522 : : }
523 : : else
524 : : {
525 : : // assume romlen is the sum of 2 powers of 2 - i haven't seen any real rom that isn't,
526 : : // and if you make such a rom, fixing its checksum is your problem.
527 : 84 : int firstpart = bitround(romlen) >> 1;
528 : 84 : int secondpart = romlen - firstpart;
529 : 84 : int repeatcount = firstpart / secondpart;
530 : 42 : unsigned int secondpart_sum = 0;
531 [ + + ]: 16849600 : for(int i = 0; i < firstpart; i++) checksum += romdata[i];
532 [ + + ]: 14681316 : for(int i = firstpart; i < romlen; i++) secondpart_sum += romdata[i];
533 : 84 : checksum += secondpart_sum * repeatcount;
534 : : }
535 : 222 : return checksum&0xFFFF;
536 : : }
537 : :
538 : 222 : void fixchecksum()
539 : : {
540 : : // randomdude999: clear out checksum bytes before recalculating checksum, this should make it correct on roms that don't have a checksum yet
541 : 222 : writeromdata(snestopc(0x00FFDC), "\xFF\xFF\0\0", 4);
542 : 222 : int checksum=(int)getchecksum();
543 : 222 : writeromdata_byte(snestopc(0x00FFDE), (unsigned char)(checksum&255));
544 : 222 : writeromdata_byte(snestopc(0x00FFDF), (unsigned char)((checksum>>8)&255));
545 : 222 : writeromdata_byte(snestopc(0x00FFDC), (unsigned char)((checksum&255)^255));
546 : 222 : writeromdata_byte(snestopc(0x00FFDD), (unsigned char)(((checksum>>8)&255)^255));
547 : 222 : }
|