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