1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include "bspf.hxx"
19#include "Cart.hxx"
20#include "Cart0840.hxx"
21#include "Cart2K.hxx"
22#include "Cart3E.hxx"
23#include "Cart3EPlus.hxx"
24#include "Cart3F.hxx"
25#include "Cart4A50.hxx"
26#include "Cart4K.hxx"
27#include "Cart4KSC.hxx"
28#include "CartAR.hxx"
29#include "CartBF.hxx"
30#include "CartBFSC.hxx"
31#include "CartBUS.hxx"
32#include "CartCDF.hxx"
33#include "CartCM.hxx"
34#include "CartCTY.hxx"
35#include "CartCV.hxx"
36#include "CartCVPlus.hxx"
37#include "CartDASH.hxx"
38#include "CartDF.hxx"
39#include "CartDFSC.hxx"
40#include "CartDPC.hxx"
41#include "CartDPCPlus.hxx"
42#include "CartE0.hxx"
43#include "CartE7.hxx"
44#include "CartE78K.hxx"
45#include "CartEF.hxx"
46#include "CartEFSC.hxx"
47#include "CartF0.hxx"
48#include "CartF4.hxx"
49#include "CartF4SC.hxx"
50#include "CartF6.hxx"
51#include "CartF6SC.hxx"
52#include "CartF8.hxx"
53#include "CartF8SC.hxx"
54#include "CartFA.hxx"
55#include "CartFA2.hxx"
56#include "CartFC.hxx"
57#include "CartFE.hxx"
58#include "CartMDM.hxx"
59#include "CartSB.hxx"
60#include "CartUA.hxx"
61#include "CartWD.hxx"
62#include "CartX07.hxx"
63#include "MD5.hxx"
64#include "Props.hxx"
65
66#include "CartDetector.hxx"
67
68// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
69unique_ptr<Cartridge> CartDetector::create(const FilesystemNode& file,
70 const ByteBuffer& image, size_t size, string& md5,
71 const string& propertiesType, Settings& settings)
72{
73 unique_ptr<Cartridge> cartridge;
74 Bankswitch::Type type = Bankswitch::nameToType(propertiesType),
75 detectedType = type;
76 string id;
77
78 // Collect some info about the ROM
79 ostringstream buf;
80
81 // First inspect the file extension itself
82 // If a valid type is found, it will override the one passed into this method
83 Bankswitch::Type typeByName = Bankswitch::typeFromExtension(file);
84 if(typeByName != Bankswitch::Type::_AUTO)
85 type = detectedType = typeByName;
86
87 // See if we should try to auto-detect the cartridge type
88 // If we ask for extended info, always do an autodetect
89 if(type == Bankswitch::Type::_AUTO || settings.getBool("rominfo"))
90 {
91 detectedType = autodetectType(image, size);
92 if(type != Bankswitch::Type::_AUTO && type != detectedType)
93 cerr << "Auto-detection not consistent: "
94 << Bankswitch::typeToName(type) << ", "
95 << Bankswitch::typeToName(detectedType) << endl;
96
97 type = detectedType;
98 buf << Bankswitch::typeToName(type) << "*";
99 }
100 else
101 buf << Bankswitch::typeToName(type);
102
103 // Check for multicart first; if found, get the correct part of the image
104 switch(type)
105 {
106 case Bankswitch::Type::_2IN1:
107 // Make sure we have a valid sized image
108 if(size == 2*2_KB || size == 2*4_KB || size == 2*8_KB || size == 2*16_KB)
109 {
110 cartridge =
111 createFromMultiCart(image, size, 2, md5, detectedType, id, settings);
112 buf << id;
113 }
114 else
115 throw runtime_error("Invalid cart size for type '" +
116 Bankswitch::typeToName(type) + "'");
117 break;
118
119 case Bankswitch::Type::_4IN1:
120 // Make sure we have a valid sized image
121 if(size == 4*2_KB || size == 4*4_KB || size == 4*8_KB)
122 {
123 cartridge =
124 createFromMultiCart(image, size, 4, md5, detectedType, id, settings);
125 buf << id;
126 }
127 else
128 throw runtime_error("Invalid cart size for type '" +
129 Bankswitch::typeToName(type) + "'");
130 break;
131
132 case Bankswitch::Type::_8IN1:
133 // Make sure we have a valid sized image
134 if(size == 8*2_KB || size == 8*4_KB || size == 8*8_KB)
135 {
136 cartridge =
137 createFromMultiCart(image, size, 8, md5, detectedType, id, settings);
138 buf << id;
139 }
140 else
141 throw runtime_error("Invalid cart size for type '" +
142 Bankswitch::typeToName(type) + "'");
143 break;
144
145 case Bankswitch::Type::_16IN1:
146 // Make sure we have a valid sized image
147 if(size == 16*2_KB || size == 16*4_KB || size == 16*8_KB)
148 {
149 cartridge =
150 createFromMultiCart(image, size, 16, md5, detectedType, id, settings);
151 buf << id;
152 }
153 else
154 throw runtime_error("Invalid cart size for type '" +
155 Bankswitch::typeToName(type) + "'");
156 break;
157
158 case Bankswitch::Type::_32IN1:
159 // Make sure we have a valid sized image
160 if(size == 32*2_KB || size == 32*4_KB)
161 {
162 cartridge =
163 createFromMultiCart(image, size, 32, md5, detectedType, id, settings);
164 buf << id;
165 }
166 else
167 throw runtime_error("Invalid cart size for type '" +
168 Bankswitch::typeToName(type) + "'");
169 break;
170
171 case Bankswitch::Type::_64IN1:
172 // Make sure we have a valid sized image
173 if(size == 64*2_KB || size == 64*4_KB)
174 {
175 cartridge =
176 createFromMultiCart(image, size, 64, md5, detectedType, id, settings);
177 buf << id;
178 }
179 else
180 throw runtime_error("Invalid cart size for type '" +
181 Bankswitch::typeToName(type) + "'");
182 break;
183
184 case Bankswitch::Type::_128IN1:
185 // Make sure we have a valid sized image
186 if(size == 128*2_KB || size == 128*4_KB)
187 {
188 cartridge =
189 createFromMultiCart(image, size, 128, md5, detectedType, id, settings);
190 buf << id;
191 }
192 else
193 throw runtime_error("Invalid cart size for type '" +
194 Bankswitch::typeToName(type) + "'");
195 break;
196
197 default:
198 cartridge = createFromImage(image, size, detectedType, md5, settings);
199 break;
200 }
201
202 if(size < 1_KB)
203 buf << " (" << size << "B) ";
204 else
205 buf << " (" << (size/1_KB) << "K) ";
206
207 cartridge->setAbout(buf.str(), Bankswitch::typeToName(type), id);
208
209 return cartridge;
210}
211
212// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
213unique_ptr<Cartridge>
214CartDetector::createFromMultiCart(const ByteBuffer& image, size_t& size,
215 uInt32 numroms, string& md5, Bankswitch::Type type, string& id, Settings& settings)
216{
217 // Get a piece of the larger image
218 uInt32 i = settings.getInt("romloadcount");
219 size /= numroms;
220 ByteBuffer slice = make_unique<uInt8[]>(size);
221 std::copy_n(image.get()+i*size, size, slice.get());
222
223 // We need a new md5 and name
224 md5 = MD5::hash(slice, uInt32(size)); // FIXME
225 ostringstream buf;
226 buf << " [G" << (i+1) << "]";
227 id = buf.str();
228
229 // Move to the next game the next time this ROM is loaded
230 settings.setValue("romloadcount", (i+1)%numroms);
231
232 if(size <= 2_KB) type = Bankswitch::Type::_2K;
233 else if(size == 4_KB) type = Bankswitch::Type::_4K;
234 else if(size == 8_KB) type = Bankswitch::Type::_F8;
235 else /* default */ type = Bankswitch::Type::_4K;
236
237 return createFromImage(slice, size, type, md5, settings);
238}
239
240// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
241unique_ptr<Cartridge>
242CartDetector::createFromImage(const ByteBuffer& image, size_t size, Bankswitch::Type type,
243 const string& md5, Settings& settings)
244{
245 // We should know the cart's type by now so let's create it
246 switch(type)
247 {
248 case Bankswitch::Type::_0840:
249 return make_unique<Cartridge0840>(image, size, md5, settings);
250 case Bankswitch::Type::_2K:
251 return make_unique<Cartridge2K>(image, size, md5, settings);
252 case Bankswitch::Type::_3E:
253 return make_unique<Cartridge3E>(image, size, md5, settings);
254 case Bankswitch::Type::_3EP:
255 return make_unique<Cartridge3EPlus>(image, size, md5, settings);
256 case Bankswitch::Type::_3F:
257 return make_unique<Cartridge3F>(image, size, md5, settings);
258 case Bankswitch::Type::_4A50:
259 return make_unique<Cartridge4A50>(image, size, md5, settings);
260 case Bankswitch::Type::_4K:
261 return make_unique<Cartridge4K>(image, size, md5, settings);
262 case Bankswitch::Type::_4KSC:
263 return make_unique<Cartridge4KSC>(image, size, md5, settings);
264 case Bankswitch::Type::_AR:
265 return make_unique<CartridgeAR>(image, size, md5, settings);
266 case Bankswitch::Type::_BF:
267 return make_unique<CartridgeBF>(image, size, md5, settings);
268 case Bankswitch::Type::_BFSC:
269 return make_unique<CartridgeBFSC>(image, size, md5, settings);
270 case Bankswitch::Type::_BUS:
271 return make_unique<CartridgeBUS>(image, size, md5, settings);
272 case Bankswitch::Type::_CDF:
273 return make_unique<CartridgeCDF>(image, size, md5, settings);
274 case Bankswitch::Type::_CM:
275 return make_unique<CartridgeCM>(image, size, md5, settings);
276 case Bankswitch::Type::_CTY:
277 return make_unique<CartridgeCTY>(image, size, md5, settings);
278 case Bankswitch::Type::_CV:
279 return make_unique<CartridgeCV>(image, size, md5, settings);
280 case Bankswitch::Type::_CVP:
281 return make_unique<CartridgeCVPlus>(image, size, md5, settings);
282 case Bankswitch::Type::_DASH:
283 return make_unique<CartridgeDASH>(image, size, md5, settings);
284 case Bankswitch::Type::_DF:
285 return make_unique<CartridgeDF>(image, size, md5, settings);
286 case Bankswitch::Type::_DFSC:
287 return make_unique<CartridgeDFSC>(image, size, md5, settings);
288 case Bankswitch::Type::_DPC:
289 return make_unique<CartridgeDPC>(image, size, md5, settings);
290 case Bankswitch::Type::_DPCP:
291 return make_unique<CartridgeDPCPlus>(image, size, md5, settings);
292 case Bankswitch::Type::_E0:
293 return make_unique<CartridgeE0>(image, size, md5, settings);
294 case Bankswitch::Type::_E7:
295 return make_unique<CartridgeE7>(image, size, md5, settings);
296 case Bankswitch::Type::_E78K:
297 return make_unique<CartridgeE78K>(image, size, md5, settings);
298 case Bankswitch::Type::_EF:
299 return make_unique<CartridgeEF>(image, size, md5, settings);
300 case Bankswitch::Type::_EFSC:
301 return make_unique<CartridgeEFSC>(image, size, md5, settings);
302 case Bankswitch::Type::_F0:
303 return make_unique<CartridgeF0>(image, size, md5, settings);
304 case Bankswitch::Type::_F4:
305 return make_unique<CartridgeF4>(image, size, md5, settings);
306 case Bankswitch::Type::_F4SC:
307 return make_unique<CartridgeF4SC>(image, size, md5, settings);
308 case Bankswitch::Type::_F6:
309 return make_unique<CartridgeF6>(image, size, md5, settings);
310 case Bankswitch::Type::_F6SC:
311 return make_unique<CartridgeF6SC>(image, size, md5, settings);
312 case Bankswitch::Type::_F8:
313 return make_unique<CartridgeF8>(image, size, md5, settings);
314 case Bankswitch::Type::_F8SC:
315 return make_unique<CartridgeF8SC>(image, size, md5, settings);
316 case Bankswitch::Type::_FA:
317 return make_unique<CartridgeFA>(image, size, md5, settings);
318 case Bankswitch::Type::_FA2:
319 return make_unique<CartridgeFA2>(image, size, md5, settings);
320 case Bankswitch::Type::_FC:
321 return make_unique<CartridgeFC>(image, size, md5, settings);
322 case Bankswitch::Type::_FE:
323 return make_unique<CartridgeFE>(image, size, md5, settings);
324 case Bankswitch::Type::_MDM:
325 return make_unique<CartridgeMDM>(image, size, md5, settings);
326 case Bankswitch::Type::_UA:
327 return make_unique<CartridgeUA>(image, size, md5, settings);
328 case Bankswitch::Type::_UASW:
329 return make_unique<CartridgeUA>(image, size, md5, settings, true);
330 case Bankswitch::Type::_SB:
331 return make_unique<CartridgeSB>(image, size, md5, settings);
332 case Bankswitch::Type::_WD:
333 case Bankswitch::Type::_WDSW:
334 return make_unique<CartridgeWD>(image, size, md5, settings);
335 case Bankswitch::Type::_X07:
336 return make_unique<CartridgeX07>(image, size, md5, settings);
337 default:
338 return nullptr; // The remaining types have already been handled
339 }
340}
341
342// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
343Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t size)
344{
345 // Guess type based on size
346 Bankswitch::Type type = Bankswitch::Type::_AUTO;
347
348 if(isProbablyCVPlus(image, size))
349 {
350 type = Bankswitch::Type::_CVP;
351 }
352 else if((size % 8448) == 0 || size == 6144)
353 {
354 type = Bankswitch::Type::_AR;
355 }
356 else if(size < 2_KB) // Sub2K images
357 {
358 type = Bankswitch::Type::_2K;
359 }
360 else if((size == 2_KB) ||
361 (size == 4_KB && std::memcmp(image.get(), image.get() + 2_KB, 2_KB) == 0))
362 {
363 type = isProbablyCV(image, size) ? Bankswitch::Type::_CV : Bankswitch::Type::_2K;
364 }
365 else if(size == 4_KB)
366 {
367 if(isProbablyCV(image, size))
368 type = Bankswitch::Type::_CV;
369 else if(isProbably4KSC(image, size))
370 type = Bankswitch::Type::_4KSC;
371 else if (isProbablyFC(image, size))
372 type = Bankswitch::Type::_FC;
373 else
374 type = Bankswitch::Type::_4K;
375 }
376 else if(size == 8_KB)
377 {
378 // First check for *potential* F8
379 uInt8 signature[2][3] = {
380 { 0x8D, 0xF9, 0x1F }, // STA $1FF9
381 { 0x8D, 0xF9, 0xFF } // STA $FFF9
382 };
383 bool f8 = searchForBytes(image.get(), size, signature[0], 3, 2) ||
384 searchForBytes(image.get(), size, signature[1], 3, 2);
385
386 if(isProbablySC(image, size))
387 type = Bankswitch::Type::_F8SC;
388 else if(std::memcmp(image.get(), image.get() + 4_KB, 4_KB) == 0)
389 type = Bankswitch::Type::_4K;
390 else if(isProbablyE0(image, size))
391 type = Bankswitch::Type::_E0;
392 else if(isProbably3E(image, size))
393 type = Bankswitch::Type::_3E;
394 else if(isProbably3F(image, size))
395 type = Bankswitch::Type::_3F;
396 else if(isProbablyUA(image, size))
397 type = Bankswitch::Type::_UA;
398 else if(isProbablyFE(image, size) && !f8)
399 type = Bankswitch::Type::_FE;
400 else if(isProbably0840(image, size))
401 type = Bankswitch::Type::_0840;
402 else if(isProbablyE78K(image, size))
403 type = Bankswitch::Type::_E78K;
404 else if (isProbablyWD(image,size))
405 type = Bankswitch::Type::_WD;
406 else if (isProbablyFC(image, size))
407 type = Bankswitch::Type::_FC;
408 else
409 type = Bankswitch::Type::_F8;
410 }
411 else if(size == 8_KB + 3) // 8195 bytes (Experimental)
412 {
413 type = Bankswitch::Type::_WDSW;
414 }
415 else if(size >= 10_KB && size <= 10_KB + 256) // ~10K - Pitfall2
416 {
417 type = Bankswitch::Type::_DPC;
418 }
419 else if(size == 12_KB)
420 {
421 type = Bankswitch::Type::_FA;
422 }
423 else if(size == 16_KB)
424 {
425 if(isProbablySC(image, size))
426 type = Bankswitch::Type::_F6SC;
427 else if(isProbablyE7(image, size))
428 type = Bankswitch::Type::_E7;
429 else if (isProbablyFC(image, size))
430 type = Bankswitch::Type::_FC;
431 else if(isProbably3E(image, size))
432 type = Bankswitch::Type::_3E;
433 /* no known 16K 3F ROMS
434 else if(isProbably3F(image, size))
435 type = Bankswitch::Type::_3F;
436 */
437 else
438 type = Bankswitch::Type::_F6;
439 }
440 else if(size == 24_KB || size == 28_KB)
441 {
442 type = Bankswitch::Type::_FA2;
443 }
444 else if(size == 29_KB)
445 {
446 if(isProbablyARM(image, size))
447 type = Bankswitch::Type::_FA2;
448 else /*if(isProbablyDPCplus(image, size))*/
449 type = Bankswitch::Type::_DPCP;
450 }
451 else if(size == 32_KB)
452 {
453 if (isProbablyCTY(image, size))
454 type = Bankswitch::Type::_CTY;
455 else if(isProbablySC(image, size))
456 type = Bankswitch::Type::_F4SC;
457 else if(isProbably3E(image, size))
458 type = Bankswitch::Type::_3E;
459 else if(isProbably3F(image, size))
460 type = Bankswitch::Type::_3F;
461 else if (isProbablyBUS(image, size))
462 type = Bankswitch::Type::_BUS;
463 else if (isProbablyCDF(image, size))
464 type = Bankswitch::Type::_CDF;
465 else if(isProbablyDPCplus(image, size))
466 type = Bankswitch::Type::_DPCP;
467 else if(isProbablyFA2(image, size))
468 type = Bankswitch::Type::_FA2;
469 else if (isProbablyFC(image, size))
470 type = Bankswitch::Type::_FC;
471 else
472 type = Bankswitch::Type::_F4;
473 }
474 else if(size == 60_KB)
475 {
476 if(isProbablyCTY(image, size))
477 type = Bankswitch::Type::_CTY;
478 else
479 type = Bankswitch::Type::_F4;
480 }
481 else if(size == 64_KB)
482 {
483 if(isProbably3E(image, size))
484 type = Bankswitch::Type::_3E;
485 else if(isProbably3F(image, size))
486 type = Bankswitch::Type::_3F;
487 else if(isProbably4A50(image, size))
488 type = Bankswitch::Type::_4A50;
489 else if(isProbablyEF(image, size, type))
490 ; // type has been set directly in the function
491 else if(isProbablyX07(image, size))
492 type = Bankswitch::Type::_X07;
493 else
494 type = Bankswitch::Type::_F0;
495 }
496 else if(size == 128_KB)
497 {
498 if(isProbably3E(image, size))
499 type = Bankswitch::Type::_3E;
500 else if(isProbablyDF(image, size, type))
501 ; // type has been set directly in the function
502 else if(isProbably3F(image, size))
503 type = Bankswitch::Type::_3F;
504 else if(isProbably4A50(image, size))
505 type = Bankswitch::Type::_4A50;
506 else if(isProbablySB(image, size))
507 type = Bankswitch::Type::_SB;
508 }
509 else if(size == 256_KB)
510 {
511 if(isProbably3E(image, size))
512 type = Bankswitch::Type::_3E;
513 else if(isProbablyBF(image, size, type))
514 ; // type has been set directly in the function
515 else if(isProbably3F(image, size))
516 type = Bankswitch::Type::_3F;
517 else /*if(isProbablySB(image, size))*/
518 type = Bankswitch::Type::_SB;
519 }
520 else // what else can we do?
521 {
522 if(isProbably3E(image, size))
523 type = Bankswitch::Type::_3E;
524 else if(isProbably3F(image, size))
525 type = Bankswitch::Type::_3F;
526 else
527 type = Bankswitch::Type::_4K; // Most common bankswitching type
528 }
529
530 // Variable sized ROM formats are independent of image size and come last
531 if(isProbablyDASH(image, size))
532 type = Bankswitch::Type::_DASH;
533 else if(isProbably3EPlus(image, size))
534 type = Bankswitch::Type::_3EP;
535 else if(isProbablyMDM(image, size))
536 type = Bankswitch::Type::_MDM;
537
538 return type;
539}
540
541// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
542bool CartDetector::searchForBytes(const uInt8* image, size_t imagesize,
543 const uInt8* signature, uInt32 sigsize,
544 uInt32 minhits)
545{
546 uInt32 count = 0;
547 for(uInt32 i = 0; i < imagesize - sigsize; ++i)
548 {
549 uInt32 matches = 0;
550 for(uInt32 j = 0; j < sigsize; ++j)
551 {
552 if(image[i+j] == signature[j])
553 ++matches;
554 else
555 break;
556 }
557 if(matches == sigsize)
558 {
559 ++count;
560 i += sigsize; // skip past this signature 'window' entirely
561 }
562 if(count >= minhits)
563 break;
564 }
565
566 return (count >= minhits);
567}
568
569// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
570bool CartDetector::isProbablySC(const ByteBuffer& image, size_t size)
571{
572 // We assume a Superchip cart repeats the first 128 bytes for the second
573 // 128 bytes in the RAM area, which is the first 256 bytes of each 4K bank
574 const uInt8* ptr = image.get();
575 while(size)
576 {
577 if(std::memcmp(ptr, ptr + 128, 128) != 0)
578 return false;
579
580 ptr += 4_KB;
581 size -= 4_KB;
582 }
583 return true;
584}
585
586// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
587bool CartDetector::isProbablyARM(const ByteBuffer& image, size_t size)
588{
589 // ARM code contains the following 'loader' patterns in the first 1K
590 // Thanks to Thomas Jentzsch of AtariAge for this advice
591 uInt8 signature[2][4] = {
592 { 0xA0, 0xC1, 0x1F, 0xE0 },
593 { 0x00, 0x80, 0x02, 0xE0 }
594 };
595 if(searchForBytes(image.get(), std::min<size_t>(size, 1_KB), signature[0], 4, 1))
596 return true;
597 else
598 return searchForBytes(image.get(), std::min<size_t>(size, 1_KB), signature[1], 4, 1);
599}
600
601// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
602bool CartDetector::isProbably0840(const ByteBuffer& image, size_t size)
603{
604 // 0840 cart bankswitching is triggered by accessing addresses 0x0800
605 // or 0x0840 at least twice
606 uInt8 signature1[3][3] = {
607 { 0xAD, 0x00, 0x08 }, // LDA $0800
608 { 0xAD, 0x40, 0x08 }, // LDA $0840
609 { 0x2C, 0x00, 0x08 } // BIT $0800
610 };
611 for(uInt32 i = 0; i < 3; ++i)
612 if(searchForBytes(image.get(), size, signature1[i], 3, 2))
613 return true;
614
615 uInt8 signature2[2][4] = {
616 { 0x0C, 0x00, 0x08, 0x4C }, // NOP $0800; JMP ...
617 { 0x0C, 0xFF, 0x0F, 0x4C } // NOP $0FFF; JMP ...
618 };
619 for(uInt32 i = 0; i < 2; ++i)
620 if(searchForBytes(image.get(), size, signature2[i], 4, 2))
621 return true;
622
623 return false;
624}
625
626// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
627bool CartDetector::isProbably3E(const ByteBuffer& image, size_t size)
628{
629 // 3E cart bankswitching is triggered by storing the bank number
630 // in address 3E using 'STA $3E', commonly followed by an
631 // immediate mode LDA
632 uInt8 signature[] = { 0x85, 0x3E, 0xA9, 0x00 }; // STA $3E; LDA #$00
633 return searchForBytes(image.get(), size, signature, 4, 1);
634}
635
636// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
637bool CartDetector::isProbably3EPlus(const ByteBuffer& image, size_t size)
638{
639 // 3E+ cart is identified key 'TJ3E' in the ROM
640 uInt8 tj3e[] = { 'T', 'J', '3', 'E' };
641 return searchForBytes(image.get(), size, tj3e, 4, 1);
642}
643
644// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
645bool CartDetector::isProbably3F(const ByteBuffer& image, size_t size)
646{
647 // 3F cart bankswitching is triggered by storing the bank number
648 // in address 3F using 'STA $3F'
649 // We expect it will be present at least 2 times, since there are
650 // at least two banks
651 uInt8 signature[] = { 0x85, 0x3F }; // STA $3F
652 return searchForBytes(image.get(), size, signature, 2, 2);
653}
654
655// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
656bool CartDetector::isProbably4A50(const ByteBuffer& image, size_t size)
657{
658 // 4A50 carts store address $4A50 at the NMI vector, which
659 // in this scheme is always in the last page of ROM at
660 // $1FFA - $1FFB (at least this is true in rev 1 of the format)
661 if(image[size-6] == 0x50 && image[size-5] == 0x4A)
662 return true;
663
664 // Program starts at $1Fxx with NOP $6Exx or NOP $6Fxx?
665 if(((image[0xfffd] & 0x1f) == 0x1f) &&
666 (image[image[0xfffd] * 256 + image[0xfffc]] == 0x0c) &&
667 ((image[image[0xfffd] * 256 + image[0xfffc] + 2] & 0xfe) == 0x6e))
668 return true;
669
670 return false;
671}
672
673// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
674bool CartDetector::isProbably4KSC(const ByteBuffer& image, size_t size)
675{
676 // We check if the first 256 bytes are identical *and* if there's
677 // an "SC" signature for one of our larger SC types at 1FFA.
678
679 uInt8 first = image[0];
680 for(uInt32 i = 1; i < 256; ++i)
681 if(image[i] != first)
682 return false;
683
684 if((image[size-6]=='S') && (image[size-5]=='C'))
685 return true;
686
687 return false;
688}
689
690// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
691bool CartDetector::isProbablyBF(const ByteBuffer& image, size_t size,
692 Bankswitch::Type& type)
693{
694 // BF carts store strings 'BFBF' and 'BFSC' starting at address $FFF8
695 // This signature is attributed to "RevEng" of AtariAge
696 uInt8 bf[] = { 'B', 'F', 'B', 'F' };
697 uInt8 bfsc[] = { 'B', 'F', 'S', 'C' };
698 if(searchForBytes(image.get()+size-8, 8, bf, 4, 1))
699 {
700 type = Bankswitch::Type::_BF;
701 return true;
702 }
703 else if(searchForBytes(image.get()+size-8, 8, bfsc, 4, 1))
704 {
705 type = Bankswitch::Type::_BFSC;
706 return true;
707 }
708
709 return false;
710}
711
712// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
713bool CartDetector::isProbablyBUS(const ByteBuffer& image, size_t size)
714{
715 // BUS ARM code has 2 occurrences of the string BUS
716 // Note: all Harmony/Melody custom drivers also contain the value
717 // 0x10adab1e (LOADABLE) if needed for future improvement
718 uInt8 bus[] = { 'B', 'U', 'S'};
719 return searchForBytes(image.get(), size, bus, 3, 2);
720}
721
722// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
723bool CartDetector::isProbablyCDF(const ByteBuffer& image, size_t size)
724{
725 // CDF ARM code has 3 occurrences of the string CDF
726 // Note: all Harmony/Melody custom drivers also contain the value
727 // 0x10adab1e (LOADABLE) if needed for future improvement
728 uInt8 cdf[] = { 'C', 'D', 'F' };
729 return searchForBytes(image.get(), size, cdf, 3, 3);
730}
731
732// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
733bool CartDetector::isProbablyCTY(const ByteBuffer& image, size_t size)
734{
735 uInt8 lenin[] = { 'L', 'E', 'N', 'I', 'N' };
736 return searchForBytes(image.get(), size, lenin, 5, 1);
737}
738
739// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
740bool CartDetector::isProbablyCV(const ByteBuffer& image, size_t size)
741{
742 // CV RAM access occurs at addresses $f3ff and $f400
743 // These signatures are attributed to the MESS project
744 uInt8 signature[2][3] = {
745 { 0x9D, 0xFF, 0xF3 }, // STA $F3FF.X
746 { 0x99, 0x00, 0xF4 } // STA $F400.Y
747 };
748 if(searchForBytes(image.get(), size, signature[0], 3, 1))
749 return true;
750 else
751 return searchForBytes(image.get(), size, signature[1], 3, 1);
752}
753
754// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
755bool CartDetector::isProbablyCVPlus(const ByteBuffer& image, size_t)
756{
757 // CV+ cart is identified key 'commavidplus' @ $04 in the ROM
758 // We inspect only this area to speed up the search
759 uInt8 cvp[12] = { 'c', 'o', 'm', 'm', 'a', 'v', 'i', 'd',
760 'p', 'l', 'u', 's' };
761 return searchForBytes(image.get()+4, 24, cvp, 12, 1);
762}
763
764// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
765bool CartDetector::isProbablyDASH(const ByteBuffer& image, size_t size)
766{
767 // DASH cart is identified key 'TJAD' in the ROM
768 uInt8 tjad[] = { 'T', 'J', 'A', 'D' };
769 return searchForBytes(image.get(), size, tjad, 4, 1);
770}
771
772// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
773bool CartDetector::isProbablyDF(const ByteBuffer& image, size_t size,
774 Bankswitch::Type& type)
775{
776
777 // DF carts store strings 'DFDF' and 'DFSC' starting at address $FFF8
778 // This signature is attributed to "RevEng" of AtariAge
779 uInt8 df[] = { 'D', 'F', 'D', 'F' };
780 uInt8 dfsc[] = { 'D', 'F', 'S', 'C' };
781 if(searchForBytes(image.get()+size-8, 8, df, 4, 1))
782 {
783 type = Bankswitch::Type::_DF;
784 return true;
785 }
786 else if(searchForBytes(image.get()+size-8, 8, dfsc, 4, 1))
787 {
788 type = Bankswitch::Type::_DFSC;
789 return true;
790 }
791
792 return false;
793}
794
795// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
796bool CartDetector::isProbablyDPCplus(const ByteBuffer& image, size_t size)
797{
798 // DPC+ ARM code has 2 occurrences of the string DPC+
799 // Note: all Harmony/Melody custom drivers also contain the value
800 // 0x10adab1e (LOADABLE) if needed for future improvement
801 uInt8 dpcp[] = { 'D', 'P', 'C', '+' };
802 return searchForBytes(image.get(), size, dpcp, 4, 2);
803}
804
805// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
806bool CartDetector::isProbablyE0(const ByteBuffer& image, size_t size)
807{
808 // E0 cart bankswitching is triggered by accessing addresses
809 // $FE0 to $FF9 using absolute non-indexed addressing
810 // To eliminate false positives (and speed up processing), we
811 // search for only certain known signatures
812 // Thanks to "stella@casperkitty.com" for this advice
813 // These signatures are attributed to the MESS project
814 uInt8 signature[8][3] = {
815 { 0x8D, 0xE0, 0x1F }, // STA $1FE0
816 { 0x8D, 0xE0, 0x5F }, // STA $5FE0
817 { 0x8D, 0xE9, 0xFF }, // STA $FFE9
818 { 0x0C, 0xE0, 0x1F }, // NOP $1FE0
819 { 0xAD, 0xE0, 0x1F }, // LDA $1FE0
820 { 0xAD, 0xE9, 0xFF }, // LDA $FFE9
821 { 0xAD, 0xED, 0xFF }, // LDA $FFED
822 { 0xAD, 0xF3, 0xBF } // LDA $BFF3
823 };
824 for(uInt32 i = 0; i < 8; ++i)
825 if(searchForBytes(image.get(), size, signature[i], 3, 1))
826 return true;
827
828 return false;
829}
830
831// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
832bool CartDetector::isProbablyE7(const ByteBuffer& image, size_t size)
833{
834 // E7 cart bankswitching is triggered by accessing addresses
835 // $FE0 to $FE6 using absolute non-indexed addressing
836 // To eliminate false positives (and speed up processing), we
837 // search for only certain known signatures
838 // Thanks to "stella@casperkitty.com" for this advice
839 // These signatures are attributed to the MESS project
840 uInt8 signature[7][3] = {
841 { 0xAD, 0xE2, 0xFF }, // LDA $FFE2
842 { 0xAD, 0xE5, 0xFF }, // LDA $FFE5
843 { 0xAD, 0xE5, 0x1F }, // LDA $1FE5
844 { 0xAD, 0xE7, 0x1F }, // LDA $1FE7
845 { 0x0C, 0xE7, 0x1F }, // NOP $1FE7
846 { 0x8D, 0xE7, 0xFF }, // STA $FFE7
847 { 0x8D, 0xE7, 0x1F } // STA $1FE7
848 };
849 for(uInt32 i = 0; i < 7; ++i)
850 if(searchForBytes(image.get(), size, signature[i], 3, 1))
851 return true;
852
853 return false;
854}
855
856// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
857bool CartDetector::isProbablyE78K(const ByteBuffer& image, size_t size)
858{
859 // E78K cart bankswitching is triggered by accessing addresses
860 // $FE4 to $FE6 using absolute non-indexed addressing
861 // To eliminate false positives (and speed up processing), we
862 // search for only certain known signatures
863 uInt8 signature[3][3] = {
864 { 0xAD, 0xE4, 0xFF }, // LDA $FFE4
865 { 0xAD, 0xE5, 0xFF }, // LDA $FFE5
866 { 0xAD, 0xE6, 0xFF }, // LDA $FFE6
867 };
868 for(uInt32 i = 0; i < 3; ++i)
869 if(searchForBytes(image.get(), size, signature[i], 3, 1))
870 return true;
871
872 return false;
873}
874
875// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
876bool CartDetector::isProbablyEF(const ByteBuffer& image, size_t size,
877 Bankswitch::Type& type)
878{
879 // Newer EF carts store strings 'EFEF' and 'EFSC' starting at address $FFF8
880 // This signature is attributed to "RevEng" of AtariAge
881 uInt8 efef[] = { 'E', 'F', 'E', 'F' };
882 uInt8 efsc[] = { 'E', 'F', 'S', 'C' };
883 if(searchForBytes(image.get()+size-8, 8, efef, 4, 1))
884 {
885 type = Bankswitch::Type::_EF;
886 return true;
887 }
888 else if(searchForBytes(image.get()+size-8, 8, efsc, 4, 1))
889 {
890 type = Bankswitch::Type::_EFSC;
891 return true;
892 }
893
894 // Otherwise, EF cart bankswitching switches banks by accessing addresses
895 // 0xFE0 to 0xFEF, usually with either a NOP or LDA
896 // It's likely that the code will switch to bank 0, so that's what is tested
897 bool isEF = false;
898 uInt8 signature[4][3] = {
899 { 0x0C, 0xE0, 0xFF }, // NOP $FFE0
900 { 0xAD, 0xE0, 0xFF }, // LDA $FFE0
901 { 0x0C, 0xE0, 0x1F }, // NOP $1FE0
902 { 0xAD, 0xE0, 0x1F } // LDA $1FE0
903 };
904 for(uInt32 i = 0; i < 4; ++i)
905 {
906 if(searchForBytes(image.get(), size, signature[i], 3, 1))
907 {
908 isEF = true;
909 break;
910 }
911 }
912
913 // Now that we know that the ROM is EF, we need to check if it's
914 // the SC variant
915 if(isEF)
916 {
917 type = isProbablySC(image, size) ? Bankswitch::Type::_EFSC : Bankswitch::Type::_EF;
918 return true;
919 }
920
921 return false;
922}
923
924// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
925bool CartDetector::isProbablyFA2(const ByteBuffer& image, size_t)
926{
927 // This currently tests only the 32K version of FA2; the 24 and 28K
928 // versions are easy, in that they're the only possibility with those
929 // file sizes
930
931 // 32K version has all zeros in 29K-32K area
932 for(uInt32 i = 29_KB; i < 32_KB; ++i)
933 if(image[i] != 0)
934 return false;
935
936 return true;
937}
938
939// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
940bool CartDetector::isProbablyFC(const ByteBuffer& image, size_t size)
941{
942 // FC bankswitching uses consecutive writes to 3 hotspots
943 uInt8 signature[2][6] = {
944 { 0x8d, 0xf8, 0x1f, 0x4a, 0x4a, 0x8d }, // STA $1FF8, LSR, LSR, STA... Power Play Arcade Menus, 3-D Ghost Attack
945 { 0x8d, 0xf8, 0xff, 0x8d, 0xfc, 0xff }, // STA $FFF8, STA $FFFC Surf's Up (4K)
946 //{ 0x8c, 0xf9, 0xff, 0xad, 0xfc, 0xff } // STY $FFF9, LDA $FFFC 3-D Havoc (patched for F8, ignore!)
947 };
948 for (uInt32 i = 0; i < 2; ++i)
949 if (searchForBytes(image.get(), size, signature[i], 6, 1))
950 return true;
951
952 return false;
953}
954
955
956// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
957bool CartDetector::isProbablyFE(const ByteBuffer& image, size_t size)
958{
959 // FE bankswitching is very weird, but always seems to include a
960 // 'JSR $xxxx'
961 // These signatures are attributed to the MESS project
962 uInt8 signature[4][5] = {
963 { 0x20, 0x00, 0xD0, 0xC6, 0xC5 }, // JSR $D000; DEC $C5
964 { 0x20, 0xC3, 0xF8, 0xA5, 0x82 }, // JSR $F8C3; LDA $82
965 { 0xD0, 0xFB, 0x20, 0x73, 0xFE }, // BNE $FB; JSR $FE73
966 { 0x20, 0x00, 0xF0, 0x84, 0xD6 } // JSR $F000; $84, $D6
967 };
968 for(uInt32 i = 0; i < 4; ++i)
969 if(searchForBytes(image.get(), size, signature[i], 5, 1))
970 return true;
971
972 return false;
973}
974
975// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
976bool CartDetector::isProbablyMDM(const ByteBuffer& image, size_t size)
977{
978 // MDM cart is identified key 'MDMC' in the first 8K of ROM
979 uInt8 mdmc[] = { 'M', 'D', 'M', 'C' };
980 return searchForBytes(image.get(), std::min<size_t>(size, 8_KB), mdmc, 4, 1);
981}
982
983// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
984bool CartDetector::isProbablySB(const ByteBuffer& image, size_t size)
985{
986 // SB cart bankswitching switches banks by accessing address 0x0800
987 uInt8 signature[2][3] = {
988 { 0xBD, 0x00, 0x08 }, // LDA $0800,x
989 { 0xAD, 0x00, 0x08 } // LDA $0800
990 };
991 if(searchForBytes(image.get(), size, signature[0], 3, 1))
992 return true;
993 else
994 return searchForBytes(image.get(), size, signature[1], 3, 1);
995}
996
997// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
998bool CartDetector::isProbablyUA(const ByteBuffer& image, size_t size)
999{
1000 // UA cart bankswitching switches to bank 1 by accessing address 0x240
1001 // using 'STA $240' or 'LDA $240'
1002 // Similar Brazilian cart bankswitching switches to bank 1 by accessing address 0x2C0
1003 // using 'BIT $2C0', 'STA $2C0' or 'LDA $2C0'
1004 uInt8 signature[6][3] = {
1005 { 0x8D, 0x40, 0x02 }, // STA $240
1006 { 0xAD, 0x40, 0x02 }, // LDA $240
1007 { 0xBD, 0x1F, 0x02 }, // LDA $21F,X
1008 { 0x2C, 0xC0, 0x02 }, // BIT $2C0
1009 { 0x8D, 0xC0, 0x02 }, // STA $2C0
1010 { 0xAD, 0xC0, 0x02 } // LDA $2C0
1011 };
1012 for(uInt32 i = 0; i < 6; ++i)
1013 if(searchForBytes(image.get(), size, signature[i], 3, 1))
1014 return true;
1015
1016 return false;
1017}
1018
1019// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1020bool CartDetector::isProbablyWD(const ByteBuffer& image, size_t size)
1021{
1022 // WD cart bankswitching switches banks by accessing address 0x30..0x3f
1023 uInt8 signature[1][3] = {
1024 { 0xA5, 0x39, 0x4C } // LDA $39, JMP
1025 };
1026 return searchForBytes(image.get(), size, signature[0], 3, 1);
1027}
1028
1029
1030// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1031bool CartDetector::isProbablyX07(const ByteBuffer& image, size_t size)
1032{
1033 // X07 bankswitching switches to bank 0, 1, 2, etc by accessing address 0x08xd
1034 uInt8 signature[6][3] = {
1035 { 0xAD, 0x0D, 0x08 }, // LDA $080D
1036 { 0xAD, 0x1D, 0x08 }, // LDA $081D
1037 { 0xAD, 0x2D, 0x08 }, // LDA $082D
1038 { 0x0C, 0x0D, 0x08 }, // NOP $080D
1039 { 0x0C, 0x1D, 0x08 }, // NOP $081D
1040 { 0x0C, 0x2D, 0x08 } // NOP $082D
1041 };
1042 for(uInt32 i = 0; i < 6; ++i)
1043 if(searchForBytes(image.get(), size, signature[i], 3, 1))
1044 return true;
1045
1046 return false;
1047}
1048