openMSX
RomFactory.cc
Go to the documentation of this file.
1#include "RomFactory.hh"
2
3#include "RomTypes.hh"
4#include "RomInfo.hh"
5#include "RomPageNN.hh"
6#include "RomPlain.hh"
7#include "RomDRAM.hh"
8#include "RomGeneric8kB.hh"
9#include "RomGeneric16kB.hh"
10#include "RomKonami.hh"
11#include "RomKonamiSCC.hh"
13#include "RomAscii8kB.hh"
14#include "RomAscii8_8.hh"
15#include "RomAscii16kB.hh"
16#include "RomAscii16X.hh"
17#include "RomMSXWrite.hh"
18#include "RomPadial8kB.hh"
19#include "RomPadial16kB.hh"
20#include "RomSuperLodeRunner.hh"
21#include "RomSuperSwangi.hh"
22#include "RomMitsubishiMLTS2.hh"
23#include "RomMSXDOS2.hh"
24#include "RomAscii16_2.hh"
25#include "RomRType.hh"
26#include "RomCrossBlaim.hh"
27#include "RomHarryFox.hh"
28#include "RomPanasonic.hh"
29#include "RomNational.hh"
30#include "RomNeo8.hh"
31#include "RomNeo16.hh"
32#include "RomMajutsushi.hh"
33#include "RomSynthesizer.hh"
34#include "RomPlayBall.hh"
35#include "RomNettouYakyuu.hh"
36#include "RomGameMaster2.hh"
37#include "RomHalnote.hh"
38#include "RomZemina25in1.hh"
39#include "RomZemina80in1.hh"
40#include "RomZemina90in1.hh"
41#include "RomZemina126in1.hh"
42#include "RomHolyQuran.hh"
43#include "RomHolyQuran2.hh"
44#include "RomFSA1FM.hh"
45#include "RomManbow2.hh"
46#include "RomMatraInk.hh"
48#include "RomArc.hh"
49#include "RomAlAlamiah30in1.hh"
50#include "RomRetroHard31in1.hh"
51#include "ROMHunterMk2.hh"
53#include "ReproCartridgeV1.hh"
54#include "ReproCartridgeV2.hh"
56#include "RomDooly.hh"
57#include "RomMSXtra.hh"
58#include "RomRamFile.hh"
59#include "RomColecoMegaCart.hh"
60#include "RomMultiRom.hh"
61#include "Rom.hh"
62#include "Reactor.hh"
63#include "MSXMotherBoard.hh"
64#include "RomDatabase.hh"
65#include "DeviceConfig.hh"
66#include "XMLElement.hh"
67#include "Yamanooto.hh"
68#include "MSXException.hh"
69
70#include "enumerate.hh"
71#include "one_of.hh"
72#include "xrange.hh"
73
74#include <bit>
75#include <memory>
76
78
79using std::make_unique;
80using enum RomType;
81
82[[nodiscard]] static RomType guessRomType(const Rom& rom)
83{
84 auto size = rom.size();
85 if (size == 0) {
86 return NORMAL;
87 }
88 //std::span data = rom; // TODO error with clang-13/libc++
89 std::span data{std::to_address(rom.begin()), size};
90
91 if (const size_t signatureOffset = 16, signatureSize = 8; size >= (signatureOffset + signatureSize)) {
92 auto signature = std::string_view(std::bit_cast<const char*>(data.data()) + signatureOffset, signatureSize);
93 if (signature == std::string_view("ASCII16X")) return ASCII16X;
94 if (signature == std::string_view("ROM_NEO8")) return NEO8;
95 if (signature == std::string_view("ROM_NE16")) return NEO16;
96 }
97 if (size < 0x10000) {
98 if ((size <= 0x4000) &&
99 (data[0] == 'A') && (data[1] == 'B')) {
100 auto initAddr = word(data[2] + 256 * data[3]);
101 auto textAddr = word(data[8] + 256 * data[9]);
102 if ((textAddr & 0xC000) == 0x8000) {
103 if ((initAddr == 0) ||
104 (((initAddr & 0xC000) == 0x8000) &&
105 (data[initAddr & (size - 1)] == 0xC9))) {
106 return PAGE2;
107 }
108 }
109 }
110 // not correct for Konami-DAC, but does this really need
111 // to be correct for _every_ rom?
112 return MIRRORED;
113 } else if (size == 0x10000 && !((data[0] == 'A') && (data[1] == 'B'))) {
114 // 64 kB ROMs can be plain or memory mapped...
115 // check here for plain, if not, try the auto detection
116 // (thanks for the hint, hap)
117 return MIRRORED;
118 } else {
119 // GameCartridges do their bank switching by using the Z80
120 // instruction ld(nn),a in the middle of program code. The
121 // address nn depends upon the GameCartridge mapper type used.
122 // To guess which mapper it is, we will look how much writes
123 // with this instruction to the mapper-registers-addresses
124 // occur.
125
126 array_with_enum_index<RomType, unsigned> typeGuess = {}; // 0-initialized
127 for (auto i : xrange(size - 3)) {
128 if (data[i] == 0x32) {
129 auto value = word(data[i + 1] + (data[i + 2] << 8));
130 switch (value) {
131 case 0x5000:
132 case 0x9000:
133 case 0xb000:
134 typeGuess[KONAMI_SCC]++;
135 break;
136 case 0x4000:
137 case 0x8000:
138 case 0xa000:
139 typeGuess[KONAMI]++;
140 break;
141 case 0x6800:
142 case 0x7800:
143 typeGuess[ASCII8]++;
144 break;
145 case 0x6000:
146 typeGuess[KONAMI]++;
147 typeGuess[ASCII8]++;
148 typeGuess[ASCII16]++;
149 break;
150 case 0x7000:
151 typeGuess[KONAMI_SCC]++;
152 typeGuess[ASCII8]++;
153 typeGuess[ASCII16]++;
154 break;
155 case 0x77ff:
156 typeGuess[ASCII16]++;
157 break;
158 }
159 }
160 }
161 if (typeGuess[ASCII8]) typeGuess[ASCII8]--; // -1 -> max_int
162 RomType type = GENERIC_8KB;
163 for (auto [i, tg] : enumerate(typeGuess)) {
164 if (tg && (tg >= typeGuess[type])) {
165 type = static_cast<RomType>(i);
166 }
167 }
168 return type;
169 }
170}
171
172std::unique_ptr<MSXDevice> create(const DeviceConfig& config)
173{
174 Rom rom(std::string(config.getAttributeValue("id")), "rom", config);
175
176 // Get specified mapper type from the config.
177 RomType type = [&] {
178 // if no type is mentioned, we assume 'mirrored' which works for most
179 // plain ROMs...
180 std::string_view typeStr = config.getChildData("mappertype", "Mirrored");
181 if (typeStr == "auto") {
182 // First check whether the (possibly patched) SHA1 is in the DB
183 const RomInfo* romInfo = config.getReactor().getSoftwareDatabase().fetchRomInfo(rom.getSHA1());
184 // If not found, try the original SHA1 in the DB
185 if (!romInfo) {
187 }
188 // If still not found, guess the mapper type
189 if (!romInfo) {
190 auto machineType = config.getMotherBoard().getMachineType();
191 if (machineType == "Coleco") {
192 if (rom.size() == one_of(128*1024u, 256*1024u, 512*1024u, 1024*1024u)) {
193 return COLECOMEGACART;
194 } else {
195 return PAGE23;
196 }
197 } else {
198 return guessRomType(rom);
199 }
200 } else {
201 return romInfo->getRomType();
202 }
203 } else {
204 // Use mapper type from config, even if this overrides DB.
205 auto t = RomInfo::nameToRomType(typeStr);
206 if (t == RomType::UNKNOWN) {
207 throw MSXException("Unknown mappertype: ", typeStr);
208 }
209 return t;
210 }
211 }();
212
213 // Store actual detected mapper type in config (override the possible
214 // 'auto' value). This way we're sure that on savestate/loadstate we're
215 // using the same mapper type (for example when the user's rom-database
216 // was updated).
217 // We do it at this point so that constructors used below can use this
218 // information for warning messages etc.
219 auto& doc = const_cast<DeviceConfig&>(config).getXMLDocument();
220 doc.setChildData(const_cast<XMLElement&>(*config.getXML()),
221 "mappertype", RomInfo::romTypeToName(type).data());
222
223 std::unique_ptr<MSXRom> result;
224 switch (type) {
225 case MIRRORED:
226 case MIRRORED0000:
227 case MIRRORED4000:
228 case MIRRORED8000:
229 case MIRROREDC000:
230 case NORMAL:
231 case NORMAL0000:
232 case NORMAL4000:
233 case NORMAL8000:
234 case NORMALC000:
235 result = make_unique<RomPlain>(config, std::move(rom), type);
236 break;
237 case PAGE0:
238 case PAGE1:
239 case PAGE01:
240 case PAGE2:
241 case PAGE12:
242 case PAGE012:
243 case PAGE3:
244 case PAGE23:
245 case PAGE123:
246 case PAGE0123:
247 result = make_unique<RomPageNN>(config, std::move(rom), type);
248 break;
249 case DRAM:
250 result = make_unique<RomDRAM>(config, std::move(rom));
251 break;
252 case GENERIC_8KB:
253 result = make_unique<RomGeneric8kB>(config, std::move(rom));
254 break;
255 case GENERIC_16KB:
256 result = make_unique<RomGeneric16kB>(config, std::move(rom));
257 break;
258 case KONAMI_SCC:
259 result = make_unique<RomKonamiSCC>(config, std::move(rom));
260 break;
261 case KONAMI:
262 result = make_unique<RomKonami>(config, std::move(rom));
263 break;
264 case KBDMASTER:
265 result = make_unique<RomKonamiKeyboardMaster>(config, std::move(rom));
266 break;
267 case ASCII8:
268 result = make_unique<RomAscii8kB>(config, std::move(rom));
269 break;
270 case ASCII16:
271 result = make_unique<RomAscii16kB>(config, std::move(rom));
272 break;
273 case ASCII16X:
274 result = make_unique<RomAscii16X>(config, std::move(rom));
275 break;
276 case MSXWRITE:
277 result = make_unique<RomMSXWrite>(config, std::move(rom));
278 break;
279 case PADIAL8:
280 result = make_unique<RomPadial8kB>(config, std::move(rom));
281 break;
282 case PADIAL16:
283 result = make_unique<RomPadial16kB>(config, std::move(rom));
284 break;
285 case SUPERLODERUNNER:
286 result = make_unique<RomSuperLodeRunner>(config, std::move(rom));
287 break;
288 case SUPERSWANGI:
289 result = make_unique<RomSuperSwangi>(config, std::move(rom));
290 break;
291 case MITSUBISHIMLTS2:
292 result = make_unique<RomMitsubishiMLTS2>(config, std::move(rom));
293 break;
294 case MSXDOS2:
295 result = make_unique<RomMSXDOS2>(config, std::move(rom));
296 break;
297 case R_TYPE:
298 result = make_unique<RomRType>(config, std::move(rom));
299 break;
300 case CROSS_BLAIM:
301 result = make_unique<RomCrossBlaim>(config, std::move(rom));
302 break;
303 case HARRY_FOX:
304 result = make_unique<RomHarryFox>(config, std::move(rom));
305 break;
306 case ASCII8_8:
307 result = make_unique<RomAscii8_8>(
308 config, std::move(rom), RomAscii8_8::SubType::ASCII8_8);
309 break;
310 case ASCII8_32:
311 result = make_unique<RomAscii8_8>(
312 config, std::move(rom), RomAscii8_8::SubType::ASCII8_32);
313 break;
314 case ASCII8_2:
315 result = make_unique<RomAscii8_8>(
316 config, std::move(rom), RomAscii8_8::SubType::ASCII8_2);
317 break;
318 case KOEI_8:
319 result = make_unique<RomAscii8_8>(
320 config, std::move(rom), RomAscii8_8::SubType::KOEI_8);
321 break;
322 case KOEI_32:
323 result = make_unique<RomAscii8_8>(
324 config, std::move(rom), RomAscii8_8::SubType::KOEI_32);
325 break;
326 case WIZARDRY:
327 result = make_unique<RomAscii8_8>(
328 config, std::move(rom), RomAscii8_8::SubType::WIZARDRY);
329 break;
330 case ASCII16_2:
331 result = make_unique<RomAscii16_2>(config, std::move(rom), RomAscii16_2::SubType::ASCII16_2);
332 break;
333 case ASCII16_8:
334 result = make_unique<RomAscii16_2>(config, std::move(rom), RomAscii16_2::SubType::ASCII16_8);
335 break;
336 case GAME_MASTER2:
337 result = make_unique<RomGameMaster2>(config, std::move(rom));
338 break;
339 case PANASONIC:
340 result = make_unique<RomPanasonic>(config, std::move(rom));
341 break;
342 case NATIONAL:
343 result = make_unique<RomNational>(config, std::move(rom));
344 break;
345 case NEO8:
346 result = make_unique<RomNeo8>(config, std::move(rom));
347 break;
348 case NEO16:
349 result = make_unique<RomNeo16>(config, std::move(rom));
350 break;
351 case MAJUTSUSHI:
352 result = make_unique<RomMajutsushi>(config, std::move(rom));
353 break;
354 case SYNTHESIZER:
355 result = make_unique<RomSynthesizer>(config, std::move(rom));
356 break;
357 case PLAYBALL:
358 result = make_unique<RomPlayBall>(config, std::move(rom));
359 break;
360 case NETTOU_YAKYUU:
361 result = make_unique<RomNettouYakyuu>(config, std::move(rom));
362 break;
363 case HALNOTE:
364 result = make_unique<RomHalnote>(config, std::move(rom));
365 break;
366 case ZEMINA25IN1:
367 result = make_unique<RomZemina25in1>(config, std::move(rom));
368 break;
369 case ZEMINA80IN1:
370 result = make_unique<RomZemina80in1>(config, std::move(rom));
371 break;
372 case ZEMINA90IN1:
373 result = make_unique<RomZemina90in1>(config, std::move(rom));
374 break;
375 case ZEMINA126IN1:
376 result = make_unique<RomZemina126in1>(config, std::move(rom));
377 break;
378 case HOLY_QURAN:
379 result = make_unique<RomHolyQuran>(config, std::move(rom));
380 break;
381 case HOLY_QURAN2:
382 result = make_unique<RomHolyQuran2>(config, std::move(rom));
383 break;
384 case FSA1FM1:
385 result = make_unique<RomFSA1FM1>(config, std::move(rom));
386 break;
387 case FSA1FM2:
388 result = make_unique<RomFSA1FM2>(config, std::move(rom));
389 break;
390 case MANBOW2:
391 case MANBOW2_2:
392 case HAMARAJANIGHT:
393 case MEGAFLASHROMSCC:
395 result = make_unique<RomManbow2>(config, std::move(rom), type);
396 break;
397 case MATRAINK:
398 result = make_unique<RomMatraInk>(config, std::move(rom));
399 break;
400 case MATRACOMPILATION:
401 result = make_unique<RomMatraCompilation>(config, std::move(rom));
402 break;
403 case ARC:
404 result = make_unique<RomArc>(config, std::move(rom));
405 break;
406 case ALALAMIAH30IN1:
407 result = make_unique<RomAlAlamiah30in1>(config, std::move(rom));
408 break;
409 case RETROHARD31IN1:
410 result = make_unique<RomRetroHard31in1>(config, std::move(rom));
411 break;
412 case ROMHUNTERMK2:
413 result = make_unique<ROMHunterMk2>(config, std::move(rom));
414 break;
416 result = make_unique<MegaFlashRomSCCPlus>(config, std::move(rom));
417 break;
418 case REPRO_CARTRIDGE1:
419 result = make_unique<ReproCartridgeV1>(config, std::move(rom));
420 break;
421 case REPRO_CARTRIDGE2:
422 result = make_unique<ReproCartridgeV2>(config, std::move(rom));
423 break;
424 case YAMANOOTO:
425 result = make_unique<Yamanooto>(config, std::move(rom));
426 break;
428 result = make_unique<KonamiUltimateCollection>(config, std::move(rom));
429 break;
430 case DOOLY:
431 result = make_unique<RomDooly>(config, std::move(rom));
432 break;
433 case MSXTRA:
434 result = make_unique<RomMSXtra>(config, std::move(rom));
435 break;
436 case MULTIROM:
437 result = make_unique<RomMultiRom>(config, std::move(rom));
438 break;
439 case RAMFILE:
440 result = make_unique<RomRamFile>(config, std::move(rom));
441 break;
442 case COLECOMEGACART:
443 result = make_unique<RomColecoMegaCart>(config, std::move(rom));
444 break;
445 default:
446 throw MSXException("Unknown ROM type");
447 }
448
449 return result;
450}
451
452} // namespace openmsx::RomFactory
TclObject t
Reactor & getReactor() const
MSXMotherBoard & getMotherBoard() const
std::string_view getChildData(std::string_view name) const
const XMLElement * getXML() const
std::string_view getAttributeValue(std::string_view attName) const
std::string_view getMachineType() const
RomDatabase & getSoftwareDatabase()
Definition Reactor.cc:315
const RomInfo * fetchRomInfo(const Sha1Sum &sha1sum) const
Lookup an entry in the database by sha1sum.
static zstring_view romTypeToName(RomType type)
Definition RomInfo.cc:192
RomType getRomType() const
Definition RomInfo.hh:62
static RomType nameToRomType(std::string_view name)
Definition RomInfo.cc:182
const Sha1Sum & getSHA1() const
Definition Rom.cc:358
auto begin() const
Definition Rom.hh:38
const Sha1Sum & getOriginalSHA1() const
Definition Rom.cc:350
auto size() const
Definition Rom.hh:37
constexpr const char * data() const
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
Definition enumerate.hh:28
std::unique_ptr< MSXDevice > create(const DeviceConfig &config)
uint16_t word
16 bit unsigned integer
Definition openmsx.hh:29
constexpr auto xrange(T e)
Definition xrange.hh:132