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