Commit | Line | Data |
---|---|---|
cff531af | 1 | /*\r |
2 | * PicoDrive\r | |
3 | * (c) Copyright Dave, 2004\r | |
4 | * (C) notaz, 2006-2010\r | |
7bf552b5 | 5 | * (C) irixxxx, 2020-2024\r |
cff531af | 6 | *\r |
7 | * This work is licensed under the terms of MAME license.\r | |
8 | * See COPYING file in the top-level directory.\r | |
9 | */\r | |
cc68a136 | 10 | \r |
efcba75f | 11 | #include "pico_int.h"\r |
f821bb70 | 12 | #include <cpu/debug.h>\r |
83bd0b76 | 13 | \r |
15ca7152 | 14 | #if defined(USE_LIBCHDR)\r |
15 | #include "libchdr/chd.h"\r | |
16 | #include "libchdr/cdrom.h"\r | |
17 | #endif\r | |
18 | \r | |
4bb0b70e | 19 | #include <unzip/unzip.h>\r |
20 | #include <zlib.h>\r | |
21 | \r | |
b081408f | 22 | static int rom_alloc_size;\r |
6ce10a43 | 23 | static const char *rom_exts[] = { "bin", "gen", "smd", "md", "32x", "pco", "iso", "sms", "gg", "sg", "sc" };\r |
83bd0b76 | 24 | \r |
45f2f245 | 25 | void (*PicoCartUnloadHook)(void);\r |
26 | void (*PicoCartMemSetup)(void);\r | |
e807ac75 | 27 | \r |
a9b3ffd3 | 28 | void (*PicoCartLoadProgressCB)(int percent) = NULL;\r |
b2451ac6 | 29 | void (*PicoCDLoadProgressCB)(const char *fname, int percent) = NULL; // handled in Pico/cd/cd_file.c\r |
a9b3ffd3 | 30 | \r |
4c2e3554 | 31 | int PicoGameLoaded;\r |
32 | \r | |
ae87bffa | 33 | static void PicoCartDetect(const char *carthw_cfg);\r |
b784d4a5 | 34 | static void PicoCartDetectMS(void);\r |
d6114368 | 35 | \r |
71de3cd9 | 36 | /* cso struct */\r |
37 | typedef struct _cso_struct\r | |
38 | {\r | |
39 | unsigned char in_buff[2*2048];\r | |
40 | unsigned char out_buff[2048];\r | |
41 | struct {\r | |
42 | char magic[4];\r | |
43 | unsigned int unused;\r | |
44 | unsigned int total_bytes;\r | |
45 | unsigned int total_bytes_high; // ignored here\r | |
46 | unsigned int block_size; // 10h\r | |
47 | unsigned char ver;\r | |
48 | unsigned char align;\r | |
49 | unsigned char reserved[2];\r | |
50 | } header;\r | |
51 | unsigned int fpos_in; // input file read pointer\r | |
52 | unsigned int fpos_out; // pos in virtual decompressed file\r | |
53 | int block_in_buff; // block which we have read in in_buff\r | |
54 | int pad;\r | |
55 | int index[0];\r | |
56 | }\r | |
57 | cso_struct;\r | |
58 | \r | |
ad949800 | 59 | static int uncompress_buf(void *dest, int destLen, void *source, int sourceLen)\r |
71de3cd9 | 60 | {\r |
61 | z_stream stream;\r | |
62 | int err;\r | |
63 | \r | |
64 | stream.next_in = (Bytef*)source;\r | |
65 | stream.avail_in = (uInt)sourceLen;\r | |
66 | stream.next_out = dest;\r | |
67 | stream.avail_out = (uInt)destLen;\r | |
68 | \r | |
69 | stream.zalloc = NULL;\r | |
70 | stream.zfree = NULL;\r | |
71 | \r | |
72 | err = inflateInit2(&stream, -15);\r | |
73 | if (err != Z_OK) return err;\r | |
74 | \r | |
75 | err = inflate(&stream, Z_FINISH);\r | |
76 | if (err != Z_STREAM_END) {\r | |
77 | inflateEnd(&stream);\r | |
78 | return err;\r | |
79 | }\r | |
80 | //*destLen = stream.total_out;\r | |
81 | \r | |
82 | return inflateEnd(&stream);\r | |
83 | }\r | |
83bd0b76 | 84 | \r |
87b0845f | 85 | static const char *get_ext(const char *path)\r |
86 | {\r | |
87 | const char *ext;\r | |
88 | if (strlen(path) < 4)\r | |
89 | return ""; // no ext\r | |
90 | \r | |
91 | // allow 2 or 3 char extensions for now\r | |
92 | ext = path + strlen(path) - 2;\r | |
93 | if (ext[-1] != '.') ext--;\r | |
94 | if (ext[-1] != '.')\r | |
95 | return "";\r | |
96 | return ext;\r | |
97 | }\r | |
98 | \r | |
b4c2331e | 99 | struct zip_file {\r |
100 | pm_file file;\r | |
101 | ZIP *zip;\r | |
102 | struct zipent *entry;\r | |
103 | z_stream stream;\r | |
104 | unsigned char inbuf[16384];\r | |
105 | long start;\r | |
106 | unsigned int pos;\r | |
107 | };\r | |
108 | \r | |
15ca7152 | 109 | #if defined(USE_LIBCHDR)\r |
110 | struct chd_struct {\r | |
111 | pm_file file;\r | |
112 | int fpos;\r | |
113 | int sectorsize;\r | |
114 | chd_file *chd;\r | |
115 | int unitbytes;\r | |
116 | int hunkunits;\r | |
117 | u8 *hunk;\r | |
118 | int hunknum;\r | |
119 | };\r | |
120 | #endif\r | |
121 | \r | |
83bd0b76 | 122 | pm_file *pm_open(const char *path)\r |
123 | {\r | |
124 | pm_file *file = NULL;\r | |
125 | const char *ext;\r | |
126 | FILE *f;\r | |
127 | \r | |
87b0845f | 128 | if (path == NULL)\r |
129 | return NULL;\r | |
83bd0b76 | 130 | \r |
87b0845f | 131 | ext = get_ext(path);\r |
132 | if (strcasecmp(ext, "zip") == 0)\r | |
83bd0b76 | 133 | {\r |
b4c2331e | 134 | struct zip_file *zfile = NULL;\r |
83bd0b76 | 135 | struct zipent *zipentry;\r |
83bd0b76 | 136 | ZIP *zipfile;\r |
b4c2331e | 137 | int i, ret;\r |
83bd0b76 | 138 | \r |
139 | zipfile = openzip(path);\r | |
83bd0b76 | 140 | if (zipfile != NULL)\r |
141 | {\r | |
142 | /* search for suitable file (right extension or large enough file) */\r | |
143 | while ((zipentry = readzip(zipfile)) != NULL)\r | |
144 | {\r | |
87b0845f | 145 | ext = get_ext(zipentry->name);\r |
83bd0b76 | 146 | \r |
87b0845f | 147 | if (zipentry->uncompressed_size >= 32*1024)\r |
148 | goto found_rom_zip;\r | |
3e49ffd0 | 149 | \r |
83bd0b76 | 150 | for (i = 0; i < sizeof(rom_exts)/sizeof(rom_exts[0]); i++)\r |
87b0845f | 151 | if (strcasecmp(ext, rom_exts[i]) == 0)\r |
152 | goto found_rom_zip;\r | |
83bd0b76 | 153 | }\r |
154 | \r | |
155 | /* zipfile given, but nothing found suitable for us inside */\r | |
156 | goto zip_failed;\r | |
157 | \r | |
158 | found_rom_zip:\r | |
b4c2331e | 159 | zfile = calloc(1, sizeof(*zfile));\r |
160 | if (zfile == NULL)\r | |
161 | goto zip_failed;\r | |
162 | ret = seekcompresszip(zipfile, zipentry);\r | |
163 | if (ret != 0)\r | |
164 | goto zip_failed;\r | |
165 | ret = inflateInit2(&zfile->stream, -15);\r | |
166 | if (ret != Z_OK) {\r | |
167 | elprintf(EL_STATUS, "zip: inflateInit2 %d", ret);\r | |
168 | goto zip_failed;\r | |
169 | }\r | |
170 | zfile->zip = zipfile;\r | |
171 | zfile->entry = zipentry;\r | |
172 | zfile->start = ftell(zipfile->fp);\r | |
173 | zfile->file.file = zfile;\r | |
174 | zfile->file.size = zipentry->uncompressed_size;\r | |
175 | zfile->file.type = PMT_ZIP;\r | |
176 | strncpy(zfile->file.ext, ext, sizeof(zfile->file.ext) - 1);\r | |
177 | return &zfile->file;\r | |
83bd0b76 | 178 | \r |
179 | zip_failed:\r | |
83bd0b76 | 180 | closezip(zipfile);\r |
b4c2331e | 181 | free(zfile);\r |
83bd0b76 | 182 | return NULL;\r |
183 | }\r | |
184 | }\r | |
87b0845f | 185 | else if (strcasecmp(ext, "cso") == 0)\r |
71de3cd9 | 186 | {\r |
187 | cso_struct *cso = NULL, *tmp = NULL;\r | |
0d8d97f8 | 188 | int i, size;\r |
71de3cd9 | 189 | f = fopen(path, "rb");\r |
190 | if (f == NULL)\r | |
191 | goto cso_failed;\r | |
192 | \r | |
274fcc35 | 193 | #ifdef __GP2X__\r |
2d2247c2 | 194 | /* we use our own buffering */\r |
195 | setvbuf(f, NULL, _IONBF, 0);\r | |
f8af9634 | 196 | #endif\r |
2d2247c2 | 197 | \r |
71de3cd9 | 198 | cso = malloc(sizeof(*cso));\r |
199 | if (cso == NULL)\r | |
200 | goto cso_failed;\r | |
201 | \r | |
202 | if (fread(&cso->header, 1, sizeof(cso->header), f) != sizeof(cso->header))\r | |
203 | goto cso_failed;\r | |
57c5a5e5 | 204 | cso->header.block_size = CPU_LE4(cso->header.block_size);\r |
205 | cso->header.total_bytes = CPU_LE4(cso->header.total_bytes);\r | |
206 | cso->header.total_bytes_high = CPU_LE4(cso->header.total_bytes_high);\r | |
71de3cd9 | 207 | \r |
208 | if (strncmp(cso->header.magic, "CISO", 4) != 0) {\r | |
209 | elprintf(EL_STATUS, "cso: bad header");\r | |
210 | goto cso_failed;\r | |
211 | }\r | |
212 | \r | |
213 | if (cso->header.block_size != 2048) {\r | |
214 | elprintf(EL_STATUS, "cso: bad block size (%u)", cso->header.block_size);\r | |
215 | goto cso_failed;\r | |
216 | }\r | |
217 | \r | |
218 | size = ((cso->header.total_bytes >> 11) + 1)*4 + sizeof(*cso);\r | |
219 | tmp = realloc(cso, size);\r | |
220 | if (tmp == NULL)\r | |
221 | goto cso_failed;\r | |
222 | cso = tmp;\r | |
223 | elprintf(EL_STATUS, "allocated %i bytes for CSO struct", size);\r | |
224 | \r | |
225 | size -= sizeof(*cso); // index size\r | |
226 | if (fread(cso->index, 1, size, f) != size) {\r | |
227 | elprintf(EL_STATUS, "cso: premature EOF");\r | |
228 | goto cso_failed;\r | |
229 | }\r | |
0d8d97f8 | 230 | for (i = 0; i < size/4; i++)\r |
231 | cso->index[i] = CPU_LE4(cso->index[i]);\r | |
71de3cd9 | 232 | \r |
233 | // all ok\r | |
234 | cso->fpos_in = ftell(f);\r | |
235 | cso->fpos_out = 0;\r | |
236 | cso->block_in_buff = -1;\r | |
87b0845f | 237 | file = calloc(1, sizeof(*file));\r |
71de3cd9 | 238 | if (file == NULL) goto cso_failed;\r |
239 | file->file = f;\r | |
240 | file->param = cso;\r | |
241 | file->size = cso->header.total_bytes;\r | |
242 | file->type = PMT_CSO;\r | |
14cd01be | 243 | strncpy(file->ext, ext, sizeof(file->ext) - 1);\r |
71de3cd9 | 244 | return file;\r |
245 | \r | |
246 | cso_failed:\r | |
247 | if (cso != NULL) free(cso);\r | |
248 | if (f != NULL) fclose(f);\r | |
249 | return NULL;\r | |
250 | }\r | |
15ca7152 | 251 | #if defined(USE_LIBCHDR)\r |
252 | else if (strcasecmp(ext, "chd") == 0)\r | |
253 | {\r | |
254 | struct chd_struct *chd = NULL;\r | |
255 | chd_file *cf = NULL;\r | |
256 | const chd_header *head;\r | |
257 | \r | |
258 | if (chd_open(path, CHD_OPEN_READ, NULL, &cf) != CHDERR_NONE)\r | |
259 | goto chd_failed;\r | |
260 | \r | |
261 | // sanity check\r | |
262 | head = chd_get_header(cf);\r | |
263 | if ((head->hunkbytes == 0) || (head->hunkbytes % CD_FRAME_SIZE))\r | |
264 | goto chd_failed;\r | |
265 | \r | |
266 | chd = calloc(1, sizeof(*chd));\r | |
267 | if (chd == NULL)\r | |
268 | goto chd_failed;\r | |
269 | chd->hunk = (u8 *)malloc(head->hunkbytes);\r | |
270 | if (!chd->hunk)\r | |
271 | goto chd_failed;\r | |
272 | \r | |
273 | chd->chd = cf;\r | |
274 | chd->unitbytes = head->unitbytes;\r | |
275 | chd->hunkunits = head->hunkbytes / head->unitbytes;\r | |
276 | chd->sectorsize = CD_MAX_SECTOR_DATA; // default to RAW mode\r | |
277 | \r | |
278 | chd->fpos = 0;\r | |
279 | chd->hunknum = -1;\r | |
280 | \r | |
281 | chd->file.file = chd;\r | |
282 | chd->file.type = PMT_CHD;\r | |
283 | // subchannel data is skipped, remove it from total size\r | |
0d8d97f8 | 284 | chd->file.size = head->logicalbytes / CD_FRAME_SIZE * CD_MAX_SECTOR_DATA;\r |
15ca7152 | 285 | strncpy(chd->file.ext, ext, sizeof(chd->file.ext) - 1);\r |
286 | return &chd->file;\r | |
287 | \r | |
288 | chd_failed:\r | |
289 | /* invalid CHD file */\r | |
290 | if (chd != NULL) free(chd);\r | |
291 | if (cf != NULL) chd_close(cf);\r | |
292 | return NULL;\r | |
293 | }\r | |
294 | #endif\r | |
83bd0b76 | 295 | \r |
296 | /* not a zip, treat as uncompressed file */\r | |
297 | f = fopen(path, "rb");\r | |
298 | if (f == NULL) return NULL;\r | |
299 | \r | |
87b0845f | 300 | file = calloc(1, sizeof(*file));\r |
83bd0b76 | 301 | if (file == NULL) {\r |
302 | fclose(f);\r | |
303 | return NULL;\r | |
304 | }\r | |
305 | fseek(f, 0, SEEK_END);\r | |
306 | file->file = f;\r | |
307 | file->param = NULL;\r | |
308 | file->size = ftell(f);\r | |
309 | file->type = PMT_UNCOMPRESSED;\r | |
87b0845f | 310 | strncpy(file->ext, ext, sizeof(file->ext) - 1);\r |
83bd0b76 | 311 | fseek(f, 0, SEEK_SET);\r |
d6114368 | 312 | \r |
274fcc35 | 313 | #ifdef __GP2X__\r |
f8af9634 | 314 | if (file->size > 0x400000)\r |
315 | /* we use our own buffering */\r | |
316 | setvbuf(f, NULL, _IONBF, 0);\r | |
317 | #endif\r | |
318 | \r | |
83bd0b76 | 319 | return file;\r |
320 | }\r | |
321 | \r | |
15ca7152 | 322 | void pm_sectorsize(int length, pm_file *stream)\r |
323 | {\r | |
324 | // CHD reading needs to know how much binary data is in one data sector(=unit)\r | |
325 | #if defined(USE_LIBCHDR)\r | |
326 | if (stream->type == PMT_CHD) {\r | |
327 | struct chd_struct *chd = stream->file;\r | |
328 | chd->sectorsize = length;\r | |
329 | if (chd->sectorsize > chd->unitbytes)\r | |
330 | elprintf(EL_STATUS|EL_ANOMALY, "cd: sector size %d too large for unit %d", chd->sectorsize, chd->unitbytes);\r | |
331 | }\r | |
332 | #endif\r | |
333 | }\r | |
334 | \r | |
335 | #if defined(USE_LIBCHDR)\r | |
336 | static size_t _pm_read_chd(void *ptr, size_t bytes, pm_file *stream, int is_audio)\r | |
337 | {\r | |
338 | int ret = 0;\r | |
339 | \r | |
340 | if (stream->type == PMT_CHD) {\r | |
341 | struct chd_struct *chd = stream->file;\r | |
342 | // calculate sector and offset in sector\r | |
343 | int sectsz = is_audio ? CD_MAX_SECTOR_DATA : chd->sectorsize;\r | |
344 | int sector = chd->fpos / sectsz;\r | |
345 | int offset = chd->fpos - (sector * sectsz);\r | |
346 | // calculate hunk and sector offset in hunk\r | |
347 | int hunknum = sector / chd->hunkunits;\r | |
348 | int hunksec = sector - (hunknum * chd->hunkunits);\r | |
349 | int hunkofs = hunksec * chd->unitbytes;\r | |
350 | \r | |
351 | while (bytes != 0) {\r | |
352 | // data left in current sector\r | |
353 | int len = sectsz - offset;\r | |
354 | \r | |
355 | // update hunk cache if needed\r | |
356 | if (hunknum != chd->hunknum) {\r | |
357 | chd_read(chd->chd, hunknum, chd->hunk);\r | |
358 | chd->hunknum = hunknum;\r | |
359 | }\r | |
360 | if (len > bytes)\r | |
361 | len = bytes;\r | |
362 | \r | |
0d8d97f8 | 363 | #if CPU_IS_LE\r |
15ca7152 | 364 | if (is_audio) {\r |
365 | // convert big endian audio samples\r | |
366 | u16 *dst = ptr, v;\r | |
367 | u8 *src = chd->hunk + hunkofs + offset;\r | |
368 | int i;\r | |
369 | \r | |
370 | for (i = 0; i < len; i += 4) {\r | |
371 | v = *src++ << 8; *dst++ = v | *src++;\r | |
372 | v = *src++ << 8; *dst++ = v | *src++;\r | |
373 | }\r | |
374 | } else\r | |
375 | #endif\r | |
376 | memcpy(ptr, chd->hunk + hunkofs + offset, len);\r | |
377 | \r | |
378 | // house keeping\r | |
379 | ret += len;\r | |
380 | chd->fpos += len;\r | |
381 | bytes -= len;\r | |
382 | \r | |
383 | // no need to advance internals if there's no more data to read\r | |
384 | if (bytes) {\r | |
385 | ptr += len;\r | |
386 | offset = 0;\r | |
387 | \r | |
388 | sector ++;\r | |
389 | hunksec ++;\r | |
390 | hunkofs += chd->unitbytes;\r | |
391 | if (hunksec >= chd->hunkunits) {\r | |
392 | hunksec = 0;\r | |
393 | hunkofs = 0;\r | |
394 | hunknum ++;\r | |
395 | }\r | |
396 | }\r | |
397 | }\r | |
398 | }\r | |
399 | \r | |
400 | return ret;\r | |
401 | }\r | |
402 | #endif\r | |
403 | \r | |
83bd0b76 | 404 | size_t pm_read(void *ptr, size_t bytes, pm_file *stream)\r |
405 | {\r | |
406 | int ret;\r | |
407 | \r | |
178a9b68 | 408 | if (stream == NULL)\r |
409 | return -1;\r | |
410 | else if (stream->type == PMT_UNCOMPRESSED)\r | |
83bd0b76 | 411 | {\r |
412 | ret = fread(ptr, 1, bytes, stream->file);\r | |
413 | }\r | |
414 | else if (stream->type == PMT_ZIP)\r | |
415 | {\r | |
b4c2331e | 416 | struct zip_file *z = stream->file;\r |
417 | \r | |
418 | if (z->entry->compression_method == 0) {\r | |
419 | int ret = fread(ptr, 1, bytes, z->zip->fp);\r | |
420 | z->pos += ret;\r | |
421 | return ret;\r | |
422 | }\r | |
423 | \r | |
424 | z->stream.next_out = ptr;\r | |
425 | z->stream.avail_out = bytes;\r | |
426 | while (z->stream.avail_out != 0) {\r | |
427 | if (z->stream.avail_in == 0) {\r | |
428 | z->stream.avail_in = fread(z->inbuf, 1, sizeof(z->inbuf), z->zip->fp);\r | |
429 | if (z->stream.avail_in == 0)\r | |
430 | break;\r | |
431 | z->stream.next_in = z->inbuf;\r | |
432 | }\r | |
433 | ret = inflate(&z->stream, Z_NO_FLUSH);\r | |
434 | if (ret == Z_STREAM_END)\r | |
435 | break;\r | |
436 | if (ret != Z_OK) {\r | |
437 | elprintf(EL_STATUS, "zip: inflate: %d", ret);\r | |
438 | return 0;\r | |
439 | }\r | |
440 | }\r | |
441 | z->pos += bytes - z->stream.avail_out;\r | |
442 | return bytes - z->stream.avail_out;\r | |
83bd0b76 | 443 | }\r |
71de3cd9 | 444 | else if (stream->type == PMT_CSO)\r |
445 | {\r | |
446 | cso_struct *cso = stream->param;\r | |
447 | int read_pos, read_len, out_offs, rret;\r | |
448 | int block = cso->fpos_out >> 11;\r | |
449 | int index = cso->index[block];\r | |
450 | int index_end = cso->index[block+1];\r | |
451 | unsigned char *out = ptr, *tmp_dst;\r | |
452 | \r | |
453 | ret = 0;\r | |
454 | while (bytes != 0)\r | |
455 | {\r | |
456 | out_offs = cso->fpos_out&0x7ff;\r | |
457 | if (out_offs == 0 && bytes >= 2048)\r | |
458 | tmp_dst = out;\r | |
459 | else tmp_dst = cso->out_buff;\r | |
460 | \r | |
461 | read_pos = (index&0x7fffffff) << cso->header.align;\r | |
462 | \r | |
463 | if (index < 0) {\r | |
464 | if (read_pos != cso->fpos_in)\r | |
465 | fseek(stream->file, read_pos, SEEK_SET);\r | |
466 | rret = fread(tmp_dst, 1, 2048, stream->file);\r | |
467 | cso->fpos_in = read_pos + rret;\r | |
468 | if (rret != 2048) break;\r | |
469 | } else {\r | |
470 | read_len = (((index_end&0x7fffffff) << cso->header.align) - read_pos) & 0xfff;\r | |
471 | if (block != cso->block_in_buff)\r | |
472 | {\r | |
473 | if (read_pos != cso->fpos_in)\r | |
ff6b7429 | 474 | fseek(stream->file, read_pos, SEEK_SET);\r |
71de3cd9 | 475 | rret = fread(cso->in_buff, 1, read_len, stream->file);\r |
476 | cso->fpos_in = read_pos + rret;\r | |
477 | if (rret != read_len) {\r | |
478 | elprintf(EL_STATUS, "cso: read failed @ %08x", read_pos);\r | |
479 | break;\r | |
480 | }\r | |
481 | cso->block_in_buff = block;\r | |
482 | }\r | |
ad949800 | 483 | rret = uncompress_buf(tmp_dst, 2048, cso->in_buff, read_len);\r |
71de3cd9 | 484 | if (rret != 0) {\r |
485 | elprintf(EL_STATUS, "cso: uncompress failed @ %08x with %i", read_pos, rret);\r | |
486 | break;\r | |
487 | }\r | |
488 | }\r | |
489 | \r | |
490 | rret = 2048;\r | |
491 | if (out_offs != 0 || bytes < 2048) {\r | |
492 | //elprintf(EL_STATUS, "cso: unaligned/nonfull @ %08x, offs=%i, len=%u", cso->fpos_out, out_offs, bytes);\r | |
493 | if (bytes < rret) rret = bytes;\r | |
494 | if (2048 - out_offs < rret) rret = 2048 - out_offs;\r | |
495 | memcpy(out, tmp_dst + out_offs, rret);\r | |
496 | }\r | |
497 | ret += rret;\r | |
498 | out += rret;\r | |
499 | cso->fpos_out += rret;\r | |
500 | bytes -= rret;\r | |
501 | block++;\r | |
502 | index = index_end;\r | |
503 | index_end = cso->index[block+1];\r | |
504 | }\r | |
505 | }\r | |
15ca7152 | 506 | #if defined(USE_LIBCHDR)\r |
507 | else if (stream->type == PMT_CHD)\r | |
508 | {\r | |
509 | ret = _pm_read_chd(ptr, bytes, stream, 0);\r | |
510 | }\r | |
511 | #endif\r | |
83bd0b76 | 512 | else\r |
513 | ret = 0;\r | |
514 | \r | |
515 | return ret;\r | |
516 | }\r | |
517 | \r | |
15ca7152 | 518 | size_t pm_read_audio(void *ptr, size_t bytes, pm_file *stream)\r |
519 | {\r | |
178a9b68 | 520 | if (stream == NULL)\r |
521 | return -1;\r | |
15ca7152 | 522 | #if !(CPU_IS_LE)\r |
178a9b68 | 523 | else if (stream->type == PMT_UNCOMPRESSED)\r |
15ca7152 | 524 | {\r |
525 | // convert little endian audio samples from WAV file\r | |
526 | int ret = pm_read(ptr, bytes, stream);\r | |
527 | u16 *dst = ptr, v;\r | |
528 | u8 *src = ptr;\r | |
529 | int i;\r | |
530 | \r | |
531 | for (i = 0; i < ret; i += 4) {\r | |
532 | v = *src++; *dst++ = v | (*src++ << 8);\r | |
533 | v = *src++; *dst++ = v | (*src++ << 8);\r | |
534 | }\r | |
535 | return ret;\r | |
536 | }\r | |
537 | else\r | |
538 | #endif\r | |
539 | #if defined(USE_LIBCHDR)\r | |
540 | if (stream->type == PMT_CHD)\r | |
541 | {\r | |
542 | return _pm_read_chd(ptr, bytes, stream, 1);\r | |
543 | }\r | |
544 | #endif\r | |
545 | return pm_read(ptr, bytes, stream);\r | |
546 | }\r | |
547 | \r | |
83bd0b76 | 548 | int pm_seek(pm_file *stream, long offset, int whence)\r |
549 | {\r | |
178a9b68 | 550 | if (stream == NULL)\r |
551 | return -1;\r | |
552 | else if (stream->type == PMT_UNCOMPRESSED)\r | |
83bd0b76 | 553 | {\r |
7336a99a | 554 | fseek(stream->file, offset, whence);\r |
555 | return ftell(stream->file);\r | |
83bd0b76 | 556 | }\r |
557 | else if (stream->type == PMT_ZIP)\r | |
558 | {\r | |
b4c2331e | 559 | struct zip_file *z = stream->file;\r |
560 | unsigned int pos = z->pos;\r | |
561 | int ret;\r | |
562 | \r | |
563 | switch (whence)\r | |
564 | {\r | |
565 | case SEEK_CUR: pos += offset; break;\r | |
566 | case SEEK_SET: pos = offset; break;\r | |
567 | case SEEK_END: pos = stream->size - offset; break;\r | |
568 | }\r | |
569 | if (z->entry->compression_method == 0) {\r | |
570 | ret = fseek(z->zip->fp, z->start + pos, SEEK_SET);\r | |
571 | if (ret == 0)\r | |
572 | return (z->pos = pos);\r | |
573 | return -1;\r | |
574 | }\r | |
575 | offset = pos - z->pos;\r | |
576 | if (pos < z->pos) {\r | |
577 | // full decompress from the start\r | |
578 | fseek(z->zip->fp, z->start, SEEK_SET);\r | |
579 | z->stream.avail_in = 0;\r | |
580 | z->stream.next_in = z->inbuf;\r | |
581 | inflateReset(&z->stream);\r | |
582 | z->pos = 0;\r | |
583 | offset = pos;\r | |
584 | }\r | |
585 | \r | |
6311a3ba | 586 | if (PicoIn.osdMessage != NULL && offset > 4 * 1024 * 1024)\r |
587 | PicoIn.osdMessage("Decompressing data...");\r | |
b4c2331e | 588 | \r |
589 | while (offset > 0) {\r | |
590 | char buf[16 * 1024];\r | |
591 | size_t l = offset > sizeof(buf) ? sizeof(buf) : offset;\r | |
592 | ret = pm_read(buf, l, stream);\r | |
593 | if (ret != l)\r | |
594 | break;\r | |
595 | offset -= l;\r | |
66fdc0f0 | 596 | }\r |
b4c2331e | 597 | return z->pos;\r |
83bd0b76 | 598 | }\r |
71de3cd9 | 599 | else if (stream->type == PMT_CSO)\r |
600 | {\r | |
601 | cso_struct *cso = stream->param;\r | |
602 | switch (whence)\r | |
603 | {\r | |
604 | case SEEK_CUR: cso->fpos_out += offset; break;\r | |
605 | case SEEK_SET: cso->fpos_out = offset; break;\r | |
606 | case SEEK_END: cso->fpos_out = cso->header.total_bytes - offset; break;\r | |
607 | }\r | |
608 | return cso->fpos_out;\r | |
609 | }\r | |
15ca7152 | 610 | #if defined(USE_LIBCHDR)\r |
611 | else if (stream->type == PMT_CHD)\r | |
612 | {\r | |
613 | struct chd_struct *chd = stream->file;\r | |
614 | switch (whence)\r | |
615 | {\r | |
616 | case SEEK_CUR: chd->fpos += offset; break;\r | |
617 | case SEEK_SET: chd->fpos = offset; break;\r | |
618 | case SEEK_END: chd->fpos = stream->size - offset; break;\r | |
619 | }\r | |
620 | return chd->fpos;\r | |
621 | }\r | |
622 | #endif\r | |
83bd0b76 | 623 | else\r |
624 | return -1;\r | |
625 | }\r | |
626 | \r | |
627 | int pm_close(pm_file *fp)\r | |
628 | {\r | |
629 | int ret = 0;\r | |
630 | \r | |
631 | if (fp == NULL) return EOF;\r | |
632 | \r | |
633 | if (fp->type == PMT_UNCOMPRESSED)\r | |
634 | {\r | |
635 | fclose(fp->file);\r | |
636 | }\r | |
637 | else if (fp->type == PMT_ZIP)\r | |
638 | {\r | |
b4c2331e | 639 | struct zip_file *z = fp->file;\r |
640 | inflateEnd(&z->stream);\r | |
641 | closezip(z->zip);\r | |
83bd0b76 | 642 | }\r |
71de3cd9 | 643 | else if (fp->type == PMT_CSO)\r |
644 | {\r | |
645 | free(fp->param);\r | |
646 | fclose(fp->file);\r | |
647 | }\r | |
15ca7152 | 648 | #if defined(USE_LIBCHDR)\r |
649 | else if (fp->type == PMT_CHD)\r | |
650 | {\r | |
651 | struct chd_struct *chd = fp->file;\r | |
652 | chd_close(chd->chd);\r | |
653 | if (chd->hunk)\r | |
654 | free(chd->hunk);\r | |
655 | }\r | |
656 | #endif\r | |
83bd0b76 | 657 | else\r |
658 | ret = EOF;\r | |
659 | \r | |
660 | free(fp);\r | |
661 | return ret;\r | |
662 | }\r | |
663 | \r | |
83ff19ec | 664 | // byteswap, data needs to be int aligned, src can match dst\r |
665 | void Byteswap(void *dst, const void *src, int len)\r | |
cc68a136 | 666 | {\r |
57c5a5e5 | 667 | #if CPU_IS_LE\r |
83ff19ec | 668 | const unsigned int *ps = src;\r |
669 | unsigned int *pd = dst;\r | |
670 | int i, m;\r | |
cc68a136 | 671 | \r |
83ff19ec | 672 | if (len < 2)\r |
673 | return;\r | |
cc68a136 | 674 | \r |
83ff19ec | 675 | m = 0x00ff00ff;\r |
676 | for (i = 0; i < len / 4; i++) {\r | |
677 | unsigned int t = ps[i];\r | |
678 | pd[i] = ((t & m) << 8) | ((t & ~m) >> 8);\r | |
cc68a136 | 679 | }\r |
57c5a5e5 | 680 | #endif\r |
cc68a136 | 681 | }\r |
682 | \r | |
683 | // Interleve a 16k block and byteswap\r | |
684 | static int InterleveBlock(unsigned char *dest,unsigned char *src)\r | |
685 | {\r | |
686 | int i=0;\r | |
57c5a5e5 | 687 | for (i=0;i<0x2000;i++) dest[(i<<1)+MEM_BE2(1)]=src[ i]; // Odd\r |
688 | for (i=0;i<0x2000;i++) dest[(i<<1)+MEM_BE2(0)]=src[0x2000+i]; // Even\r | |
cc68a136 | 689 | return 0;\r |
690 | }\r | |
691 | \r | |
692 | // Decode a SMD file\r | |
693 | static int DecodeSmd(unsigned char *data,int len)\r | |
694 | {\r | |
695 | unsigned char *temp=NULL;\r | |
696 | int i=0;\r | |
697 | \r | |
698 | temp=(unsigned char *)malloc(0x4000);\r | |
699 | if (temp==NULL) return 1;\r | |
700 | memset(temp,0,0x4000);\r | |
701 | \r | |
702 | // Interleve each 16k block and shift down by 0x200:\r | |
703 | for (i=0; i+0x4200<=len; i+=0x4000)\r | |
704 | {\r | |
705 | InterleveBlock(temp,data+0x200+i); // Interleve 16k to temporary buffer\r | |
706 | memcpy(data+i,temp,0x4000); // Copy back in\r | |
707 | }\r | |
708 | \r | |
709 | free(temp);\r | |
710 | return 0;\r | |
711 | }\r | |
712 | \r | |
87b0845f | 713 | static unsigned char *PicoCartAlloc(int filesize, int is_sms)\r |
cc68a136 | 714 | {\r |
cc68a136 | 715 | unsigned char *rom;\r |
716 | \r | |
28165a19 | 717 | // make size power of 2 for easier banking handling\r |
718 | int s = 0, tmp = filesize;\r | |
719 | while ((tmp >>= 1) != 0)\r | |
720 | s++;\r | |
721 | if (filesize > (1 << s))\r | |
722 | s++;\r | |
723 | rom_alloc_size = 1 << s;\r | |
724 | \r | |
87b0845f | 725 | if (is_sms) {\r |
b4db550e | 726 | // be sure we can cover all address space\r |
727 | if (rom_alloc_size < 0x10000)\r | |
728 | rom_alloc_size = 0x10000;\r | |
87b0845f | 729 | }\r |
730 | else {\r | |
731 | // align to 512K for memhandlers\r | |
28165a19 | 732 | rom_alloc_size = (rom_alloc_size + 0x7ffff) & ~0x7ffff;\r |
87b0845f | 733 | }\r |
cc68a136 | 734 | \r |
b081408f | 735 | if (rom_alloc_size - filesize < 4)\r |
736 | rom_alloc_size += 4; // padding for out-of-bound exec protection\r | |
cc68a136 | 737 | \r |
738 | // Allocate space for the rom plus padding\r | |
b081408f | 739 | // use special address for 32x dynarec\r |
e743be20 | 740 | rom = plat_mmap(0x02000000, rom_alloc_size, 0, 0);\r |
cc68a136 | 741 | return rom;\r |
742 | }\r | |
743 | \r | |
a5085db3 | 744 | int PicoCartLoad(pm_file *f, const unsigned char *rom, unsigned int romsize,\r |
745 | unsigned char **prom, unsigned int *psize, int is_sms)\r | |
cc68a136 | 746 | {\r |
a5085db3 | 747 | unsigned char *rom_data = NULL;\r |
87b0845f | 748 | int size, bytes_read;\r |
cc68a136 | 749 | \r |
a5085db3 | 750 | if (!f && !rom)\r |
87b0845f | 751 | return 1;\r |
752 | \r | |
a5085db3 | 753 | if (!rom)\r |
754 | size = f->size;\r | |
755 | else\r | |
756 | size = romsize;\r | |
757 | \r | |
cc68a136 | 758 | if (size <= 0) return 1;\r |
87b0845f | 759 | size = (size+3)&~3; // Round up to a multiple of 4\r |
cc68a136 | 760 | \r |
761 | // Allocate space for the rom plus padding\r | |
a5085db3 | 762 | rom_data = PicoCartAlloc(size, is_sms);\r |
763 | if (rom_data == NULL) {\r | |
c6196c0f | 764 | elprintf(EL_STATUS, "out of memory (wanted %i)", size);\r |
f8af9634 | 765 | return 2;\r |
7336a99a | 766 | }\r |
cc68a136 | 767 | \r |
a5085db3 | 768 | if (!rom) {\r |
769 | if (PicoCartLoadProgressCB != NULL)\r | |
a9b3ffd3 | 770 | {\r |
a5085db3 | 771 | // read ROM in blocks, just for fun\r |
772 | int ret;\r | |
773 | unsigned char *p = rom_data;\r | |
774 | bytes_read=0;\r | |
775 | do\r | |
776 | {\r | |
777 | int todo = size - bytes_read;\r | |
778 | if (todo > 256*1024) todo = 256*1024;\r | |
779 | ret = pm_read(p,todo,f);\r | |
780 | bytes_read += ret;\r | |
781 | p += ret;\r | |
782 | PicoCartLoadProgressCB(bytes_read * 100LL / size);\r | |
783 | }\r | |
784 | while (ret > 0);\r | |
785 | }\r | |
786 | else\r | |
787 | bytes_read = pm_read(rom_data,size,f); // Load up the rom\r | |
788 | \r | |
789 | if (bytes_read <= 0) {\r | |
790 | elprintf(EL_STATUS, "read failed");\r | |
791 | plat_munmap(rom_data, rom_alloc_size);\r | |
792 | return 3;\r | |
a9b3ffd3 | 793 | }\r |
a9b3ffd3 | 794 | }\r |
795 | else\r | |
a5085db3 | 796 | memcpy(rom_data, rom, romsize);\r |
bf098bc5 | 797 | \r |
3e49ffd0 | 798 | if (!is_sms)\r |
799 | {\r | |
800 | // maybe we are loading MegaCD BIOS?\r | |
a5085db3 | 801 | if (!(PicoIn.AHW & PAHW_MCD) && size == 0x20000 && (!strncmp((char *)rom_data+0x124, "BOOT", 4) ||\r |
802 | !strncmp((char *)rom_data+0x128, "BOOT", 4))) {\r | |
93f9619e | 803 | PicoIn.AHW |= PAHW_MCD;\r |
3e49ffd0 | 804 | }\r |
cc68a136 | 805 | \r |
3e49ffd0 | 806 | // Check for SMD:\r |
87b0845f | 807 | if (size >= 0x4200 && (size&0x3fff) == 0x200 &&\r |
a5085db3 | 808 | ((rom_data[0x2280] == 'S' && rom_data[0x280] == 'E') || (rom_data[0x280] == 'S' && rom_data[0x2281] == 'E'))) {\r |
87b0845f | 809 | elprintf(EL_STATUS, "SMD format detected.");\r |
a5085db3 | 810 | DecodeSmd(rom_data,size); size-=0x200; // Decode and byteswap SMD\r |
3e49ffd0 | 811 | }\r |
a5085db3 | 812 | else Byteswap(rom_data, rom_data, size); // Just byteswap\r |
21395e38 | 813 | }\r |
87b0845f | 814 | else\r |
815 | {\r | |
816 | if (size >= 0x4200 && (size&0x3fff) == 0x200) {\r | |
817 | elprintf(EL_STATUS, "SMD format detected.");\r | |
818 | // at least here it's not interleaved\r | |
819 | size -= 0x200;\r | |
a5085db3 | 820 | memmove(rom_data, rom_data + 0x200, size);\r |
87b0845f | 821 | }\r |
822 | }\r | |
cc68a136 | 823 | \r |
a5085db3 | 824 | if (prom) *prom = rom_data;\r |
87b0845f | 825 | if (psize) *psize = size;\r |
cc68a136 | 826 | \r |
827 | return 0;\r | |
828 | }\r | |
829 | \r | |
f8ef8ff7 | 830 | // Insert a cartridge:\r |
ae87bffa | 831 | int PicoCartInsert(unsigned char *rom, unsigned int romsize, const char *carthw_cfg)\r |
cc68a136 | 832 | {\r |
833 | // notaz: add a 68k "jump one op back" opcode to the end of ROM.\r | |
834 | // This will hang the emu, but will prevent nasty crashes.\r | |
835 | // note: 4 bytes are padded to every ROM\r | |
d6114368 | 836 | if (rom != NULL)\r |
91ea9406 | 837 | *(u32 *)(rom+romsize) = CPU_BE2(0x4EFAFFFE);\r |
cc68a136 | 838 | \r |
cc68a136 | 839 | Pico.rom=rom;\r |
840 | Pico.romsize=romsize;\r | |
841 | \r | |
88fd63ad | 842 | if (Pico.sv.data) {\r |
843 | free(Pico.sv.data);\r | |
844 | Pico.sv.data = NULL;\r | |
d6114368 | 845 | }\r |
846 | \r | |
e807ac75 | 847 | if (PicoCartUnloadHook != NULL) {\r |
848 | PicoCartUnloadHook();\r | |
849 | PicoCartUnloadHook = NULL;\r | |
850 | }\r | |
a2b8c5a5 | 851 | pdb_cleanup();\r |
e807ac75 | 852 | \r |
0aa63fce | 853 | PicoIn.AHW &= ~(PAHW_32X|PAHW_SVP);\r |
602133e1 | 854 | \r |
45f2f245 | 855 | PicoCartMemSetup = NULL;\r |
f8ef8ff7 | 856 | PicoDmaHook = NULL;\r |
857 | PicoResetHook = NULL;\r | |
017512f2 | 858 | PicoLineHook = NULL;\r |
fad24893 | 859 | PicoLoadStateHook = NULL;\r |
945c2fdc | 860 | carthw_chunks = NULL;\r |
f8ef8ff7 | 861 | \r |
4fc85c80 | 862 | if (!(PicoIn.AHW & (PAHW_MCD|PAHW_SMS|PAHW_PICO)))\r |
ae87bffa | 863 | PicoCartDetect(carthw_cfg);\r |
4fc85c80 | 864 | if (PicoIn.AHW & PAHW_SMS)\r |
b784d4a5 | 865 | PicoCartDetectMS();\r |
f1e31ac6 VS |
866 | if (PicoIn.AHW & PAHW_SVP)\r |
867 | PicoSVPStartup();\r | |
868 | if (PicoIn.AHW & PAHW_PICO)\r | |
869 | PicoInitPico();\r | |
f8ef8ff7 | 870 | \r |
1dceadae | 871 | // setup correct memory map for loaded ROM\r |
0aa63fce | 872 | switch (PicoIn.AHW & ~(PAHW_GG|PAHW_SG|PAHW_SC)) {\r |
9037e45d | 873 | default:\r |
93f9619e | 874 | elprintf(EL_STATUS|EL_ANOMALY, "starting in unknown hw configuration: %x", PicoIn.AHW);\r |
9037e45d | 875 | case 0:\r |
876 | case PAHW_SVP: PicoMemSetup(); break;\r | |
877 | case PAHW_MCD: PicoMemSetupCD(); break;\r | |
878 | case PAHW_PICO: PicoMemSetupPico(); break;\r | |
3e49ffd0 | 879 | case PAHW_SMS: PicoMemSetupMS(); break;\r |
9037e45d | 880 | }\r |
1dceadae | 881 | \r |
45f2f245 | 882 | if (PicoCartMemSetup != NULL)\r |
883 | PicoCartMemSetup();\r | |
884 | \r | |
93f9619e | 885 | if (PicoIn.AHW & PAHW_SMS)\r |
3e49ffd0 | 886 | PicoPowerMS();\r |
887 | else\r | |
888 | PicoPower();\r | |
889 | \r | |
4c2e3554 | 890 | PicoGameLoaded = 1;\r |
1cb1584b | 891 | return 0;\r |
cc68a136 | 892 | }\r |
893 | \r | |
a736af3e | 894 | int PicoCartResize(int newsize)\r |
895 | {\r | |
896 | void *tmp = plat_mremap(Pico.rom, rom_alloc_size, newsize);\r | |
897 | if (tmp == NULL)\r | |
898 | return -1;\r | |
899 | \r | |
900 | Pico.rom = tmp;\r | |
901 | rom_alloc_size = newsize;\r | |
902 | return 0;\r | |
903 | }\r | |
904 | \r | |
2aa27095 | 905 | void PicoCartUnload(void)\r |
cc68a136 | 906 | {\r |
3e49ffd0 | 907 | if (PicoCartUnloadHook != NULL) {\r |
908 | PicoCartUnloadHook();\r | |
909 | PicoCartUnloadHook = NULL;\r | |
910 | }\r | |
911 | \r | |
9961d9fd | 912 | PicoUnload32x();\r |
5e49c3a8 | 913 | \r |
a12b1b29 | 914 | if (Pico.rom != NULL) {\r |
053fd9b4 | 915 | SekFinishIdleDet();\r |
b081408f | 916 | plat_munmap(Pico.rom, rom_alloc_size);\r |
917 | Pico.rom = NULL;\r | |
a12b1b29 | 918 | }\r |
4c2e3554 | 919 | PicoGameLoaded = 0;\r |
cc68a136 | 920 | }\r |
921 | \r | |
e1e8ca17 | 922 | static unsigned int rom_crc32(int size)\r |
000f5335 | 923 | {\r |
924 | unsigned int crc;\r | |
925 | elprintf(EL_STATUS, "caclulating CRC32..");\r | |
e1e8ca17 | 926 | if (size <= 0 || size > Pico.romsize) size = Pico.romsize;\r |
000f5335 | 927 | \r |
928 | // have to unbyteswap for calculation..\r | |
e1e8ca17 | 929 | Byteswap(Pico.rom, Pico.rom, size);\r |
930 | crc = crc32(0, Pico.rom, size);\r | |
931 | Byteswap(Pico.rom, Pico.rom, size);\r | |
000f5335 | 932 | return crc;\r |
933 | }\r | |
934 | \r | |
1f4e9f14 | 935 | static int rom_strcmp(int rom_offset, const char *s1)\r |
1dceadae | 936 | {\r |
1f4e9f14 | 937 | int i, len = strlen(s1);\r |
45f2f245 | 938 | const char *s_rom = (const char *)Pico.rom;\r |
a76fad41 | 939 | if (rom_offset + len > Pico.romsize)\r |
940 | return 0;\r | |
1dceadae | 941 | for (i = 0; i < len; i++)\r |
57c5a5e5 | 942 | if (s1[i] != s_rom[MEM_BE2(i + rom_offset)])\r |
1dceadae | 943 | return 1;\r |
944 | return 0;\r | |
945 | }\r | |
946 | \r | |
af37bca8 | 947 | static unsigned int rom_read32(int addr)\r |
948 | {\r | |
949 | unsigned short *m = (unsigned short *)(Pico.rom + addr);\r | |
950 | return (m[0] << 16) | m[1];\r | |
951 | }\r | |
952 | \r | |
45f2f245 | 953 | static char *sskip(char *s)\r |
954 | {\r | |
955 | while (*s && isspace_(*s))\r | |
956 | s++;\r | |
957 | return s;\r | |
958 | }\r | |
959 | \r | |
960 | static void rstrip(char *s)\r | |
961 | {\r | |
962 | char *p;\r | |
963 | for (p = s + strlen(s) - 1; p >= s; p--)\r | |
964 | if (isspace_(*p))\r | |
965 | *p = 0;\r | |
966 | }\r | |
967 | \r | |
000f5335 | 968 | static int parse_3_vals(char *p, int *val0, int *val1, int *val2)\r |
969 | {\r | |
970 | char *r;\r | |
971 | *val0 = strtoul(p, &r, 0);\r | |
972 | if (r == p)\r | |
973 | goto bad;\r | |
974 | p = sskip(r);\r | |
975 | if (*p++ != ',')\r | |
976 | goto bad;\r | |
977 | *val1 = strtoul(p, &r, 0);\r | |
978 | if (r == p)\r | |
979 | goto bad;\r | |
980 | p = sskip(r);\r | |
981 | if (*p++ != ',')\r | |
982 | goto bad;\r | |
983 | *val2 = strtoul(p, &r, 0);\r | |
984 | if (r == p)\r | |
985 | goto bad;\r | |
986 | \r | |
987 | return 1;\r | |
988 | bad:\r | |
989 | return 0;\r | |
990 | }\r | |
991 | \r | |
45f2f245 | 992 | static int is_expr(const char *expr, char **pr)\r |
993 | {\r | |
994 | int len = strlen(expr);\r | |
995 | char *p = *pr;\r | |
996 | \r | |
997 | if (strncmp(expr, p, len) != 0)\r | |
998 | return 0;\r | |
999 | p = sskip(p + len);\r | |
1000 | if (*p != '=')\r | |
1001 | return 0; // wrong or malformed\r | |
1002 | \r | |
1003 | *pr = sskip(p + 1);\r | |
1004 | return 1;\r | |
1005 | }\r | |
1006 | \r | |
005651ce | 1007 | #include "carthw_cfg.c"\r |
1008 | \r | |
8b9dbcde | 1009 | static void parse_carthw(const char *carthw_cfg, int *fill_sram,\r |
1010 | int *hw_detected)\r | |
45f2f245 | 1011 | {\r |
1012 | int line = 0, any_checks_passed = 0, skip_sect = 0;\r | |
005651ce | 1013 | const char *s, *builtin = builtin_carthw_cfg;\r |
000f5335 | 1014 | int tmp, rom_crc = 0;\r |
45f2f245 | 1015 | char buff[256], *p, *r;\r |
1016 | FILE *f;\r | |
1017 | \r | |
ae87bffa | 1018 | f = fopen(carthw_cfg, "r");\r |
005651ce | 1019 | if (f == NULL)\r |
1020 | f = fopen("pico/carthw.cfg", "r");\r | |
1021 | if (f == NULL)\r | |
ae87bffa | 1022 | elprintf(EL_STATUS, "couldn't open carthw.cfg!");\r |
45f2f245 | 1023 | \r |
005651ce | 1024 | for (;;)\r |
45f2f245 | 1025 | {\r |
005651ce | 1026 | if (f != NULL) {\r |
1027 | p = fgets(buff, sizeof(buff), f);\r | |
1028 | if (p == NULL)\r | |
1029 | break;\r | |
1030 | }\r | |
1031 | else {\r | |
1032 | if (*builtin == 0)\r | |
1033 | break;\r | |
1034 | for (s = builtin; *s != 0 && *s != '\n'; s++)\r | |
1035 | ;\r | |
1036 | while (*s == '\n')\r | |
1037 | s++;\r | |
1038 | tmp = s - builtin;\r | |
1039 | if (tmp > sizeof(buff) - 1)\r | |
1040 | tmp = sizeof(buff) - 1;\r | |
1041 | memcpy(buff, builtin, tmp);\r | |
1042 | buff[tmp] = 0;\r | |
1043 | p = buff;\r | |
1044 | builtin = s;\r | |
1045 | }\r | |
1046 | \r | |
45f2f245 | 1047 | line++;\r |
1048 | p = sskip(p);\r | |
1049 | if (*p == 0 || *p == '#')\r | |
1050 | continue;\r | |
1051 | \r | |
1052 | if (*p == '[') {\r | |
1053 | any_checks_passed = 0;\r | |
1054 | skip_sect = 0;\r | |
1055 | continue;\r | |
1056 | }\r | |
1057 | \r | |
1058 | if (skip_sect)\r | |
1059 | continue;\r | |
1060 | \r | |
1061 | /* look for checks */\r | |
1062 | if (is_expr("check_str", &p))\r | |
1063 | {\r | |
1064 | int offs;\r | |
1065 | offs = strtoul(p, &r, 0);\r | |
1066 | if (offs < 0 || offs > Pico.romsize) {\r | |
1067 | elprintf(EL_STATUS, "carthw:%d: check_str offs out of range: %d\n", line, offs);\r | |
1068 | goto bad;\r | |
1069 | }\r | |
1070 | p = sskip(r);\r | |
1071 | if (*p != ',')\r | |
1072 | goto bad;\r | |
1073 | p = sskip(p + 1);\r | |
1074 | if (*p != '"')\r | |
1075 | goto bad;\r | |
1076 | p++;\r | |
1077 | r = strchr(p, '"');\r | |
1078 | if (r == NULL)\r | |
1079 | goto bad;\r | |
1080 | *r = 0;\r | |
1081 | \r | |
1082 | if (rom_strcmp(offs, p) == 0)\r | |
1083 | any_checks_passed = 1;\r | |
1084 | else\r | |
1085 | skip_sect = 1;\r | |
1086 | continue;\r | |
1087 | }\r | |
1088 | else if (is_expr("check_size_gt", &p))\r | |
1089 | {\r | |
1090 | int size;\r | |
1091 | size = strtoul(p, &r, 0);\r | |
1092 | if (r == p || size < 0)\r | |
1093 | goto bad;\r | |
1094 | \r | |
1095 | if (Pico.romsize > size)\r | |
1096 | any_checks_passed = 1;\r | |
1097 | else\r | |
1098 | skip_sect = 1;\r | |
1099 | continue;\r | |
1100 | }\r | |
1101 | else if (is_expr("check_csum", &p))\r | |
1102 | {\r | |
1103 | int csum;\r | |
1104 | csum = strtoul(p, &r, 0);\r | |
1105 | if (r == p || (csum & 0xffff0000))\r | |
1106 | goto bad;\r | |
1107 | \r | |
1108 | if (csum == (rom_read32(0x18c) & 0xffff))\r | |
1109 | any_checks_passed = 1;\r | |
1110 | else\r | |
1111 | skip_sect = 1;\r | |
1112 | continue;\r | |
1113 | }\r | |
000f5335 | 1114 | else if (is_expr("check_crc32", &p))\r |
1115 | {\r | |
1116 | unsigned int crc;\r | |
1117 | crc = strtoul(p, &r, 0);\r | |
1118 | if (r == p)\r | |
1119 | goto bad;\r | |
1120 | \r | |
1121 | if (rom_crc == 0)\r | |
e1e8ca17 | 1122 | rom_crc = rom_crc32(64*1024);\r |
000f5335 | 1123 | if (crc == rom_crc)\r |
1124 | any_checks_passed = 1;\r | |
1125 | else\r | |
1126 | skip_sect = 1;\r | |
1127 | continue;\r | |
1128 | }\r | |
45f2f245 | 1129 | \r |
1130 | /* now time for actions */\r | |
1131 | if (is_expr("hw", &p)) {\r | |
1132 | if (!any_checks_passed)\r | |
1133 | goto no_checks;\r | |
8b9dbcde | 1134 | *hw_detected = 1;\r |
45f2f245 | 1135 | rstrip(p);\r |
1136 | \r | |
1137 | if (strcmp(p, "svp") == 0)\r | |
f1e31ac6 | 1138 | PicoIn.AHW = PAHW_SVP;\r |
45f2f245 | 1139 | else if (strcmp(p, "pico") == 0)\r |
f1e31ac6 | 1140 | PicoIn.AHW = PAHW_PICO;\r |
000f5335 | 1141 | else if (strcmp(p, "prot") == 0)\r |
1142 | carthw_sprot_startup();\r | |
1143 | else if (strcmp(p, "ssf2_mapper") == 0)\r | |
1144 | carthw_ssf2_startup();\r | |
45f2f245 | 1145 | else if (strcmp(p, "x_in_1_mapper") == 0)\r |
1146 | carthw_Xin1_startup();\r | |
1147 | else if (strcmp(p, "realtec_mapper") == 0)\r | |
1148 | carthw_realtec_startup();\r | |
1149 | else if (strcmp(p, "radica_mapper") == 0)\r | |
1150 | carthw_radica_startup();\r | |
30f0fdd4 | 1151 | else if (strcmp(p, "piersolar_mapper") == 0)\r |
1152 | carthw_pier_startup();\r | |
60392bf4 | 1153 | else if (strcmp(p, "sf001_mapper") == 0)\r |
1154 | carthw_sf001_startup();\r | |
1155 | else if (strcmp(p, "sf002_mapper") == 0)\r | |
1156 | carthw_sf002_startup();\r | |
b7697ca3 | 1157 | else if (strcmp(p, "sf004_mapper") == 0)\r |
1158 | carthw_sf004_startup();\r | |
e1e8ca17 | 1159 | else if (strcmp(p, "lk3_mapper") == 0)\r |
1160 | carthw_lk3_startup();\r | |
1161 | else if (strcmp(p, "smw64_mapper") == 0)\r | |
1162 | carthw_smw64_startup();\r | |
45f2f245 | 1163 | else {\r |
1164 | elprintf(EL_STATUS, "carthw:%d: unsupported mapper: %s", line, p);\r | |
1165 | skip_sect = 1;\r | |
8b9dbcde | 1166 | *hw_detected = 0;\r |
45f2f245 | 1167 | }\r |
1168 | continue;\r | |
1169 | }\r | |
1170 | if (is_expr("sram_range", &p)) {\r | |
1171 | int start, end;\r | |
1172 | \r | |
1173 | if (!any_checks_passed)\r | |
1174 | goto no_checks;\r | |
1175 | rstrip(p);\r | |
1176 | \r | |
1177 | start = strtoul(p, &r, 0);\r | |
1178 | if (r == p)\r | |
1179 | goto bad;\r | |
1180 | p = sskip(r);\r | |
1181 | if (*p != ',')\r | |
1182 | goto bad;\r | |
1183 | p = sskip(p + 1);\r | |
1184 | end = strtoul(p, &r, 0);\r | |
1185 | if (r == p)\r | |
1186 | goto bad;\r | |
1187 | if (((start | end) & 0xff000000) || start > end) {\r | |
1188 | elprintf(EL_STATUS, "carthw:%d: bad sram_range: %08x - %08x", line, start, end);\r | |
1189 | goto bad_nomsg;\r | |
1190 | }\r | |
88fd63ad | 1191 | Pico.sv.start = start;\r |
1192 | Pico.sv.end = end;\r | |
45f2f245 | 1193 | continue;\r |
1194 | }\r | |
1195 | else if (is_expr("prop", &p)) {\r | |
1196 | if (!any_checks_passed)\r | |
1197 | goto no_checks;\r | |
1198 | rstrip(p);\r | |
1199 | \r | |
1200 | if (strcmp(p, "no_sram") == 0)\r | |
88fd63ad | 1201 | Pico.sv.flags &= ~SRF_ENABLED;\r |
45f2f245 | 1202 | else if (strcmp(p, "no_eeprom") == 0)\r |
88fd63ad | 1203 | Pico.sv.flags &= ~SRF_EEPROM;\r |
45f2f245 | 1204 | else if (strcmp(p, "filled_sram") == 0)\r |
1205 | *fill_sram = 1;\r | |
fe8f2d96 | 1206 | else if (strcmp(p, "wwfraw_hack") == 0)\r |
1207 | PicoIn.quirks |= PQUIRK_WWFRAW_HACK;\r | |
1208 | else if (strcmp(p, "blackthorne_hack") == 0)\r | |
1209 | PicoIn.quirks |= PQUIRK_BLACKTHORNE_HACK;\r | |
1210 | else if (strcmp(p, "marscheck_hack") == 0)\r | |
1211 | PicoIn.quirks |= PQUIRK_MARSCHECK_HACK;\r | |
a76fad41 | 1212 | else if (strcmp(p, "force_6btn") == 0)\r |
93f9619e | 1213 | PicoIn.quirks |= PQUIRK_FORCE_6BTN;\r |
a67db32a | 1214 | else if (strcmp(p, "no_z80_bus_lock") == 0)\r |
1215 | PicoIn.quirks |= PQUIRK_NO_Z80_BUS_LOCK;\r | |
45f2f245 | 1216 | else {\r |
1217 | elprintf(EL_STATUS, "carthw:%d: unsupported prop: %s", line, p);\r | |
1218 | goto bad_nomsg;\r | |
1219 | }\r | |
a76fad41 | 1220 | elprintf(EL_STATUS, "game prop: %s", p);\r |
45f2f245 | 1221 | continue;\r |
1222 | }\r | |
1223 | else if (is_expr("eeprom_type", &p)) {\r | |
1224 | int type;\r | |
1225 | if (!any_checks_passed)\r | |
1226 | goto no_checks;\r | |
1227 | rstrip(p);\r | |
1228 | \r | |
1229 | type = strtoul(p, &r, 0);\r | |
1230 | if (r == p || type < 0)\r | |
1231 | goto bad;\r | |
88fd63ad | 1232 | Pico.sv.eeprom_type = type;\r |
1233 | Pico.sv.flags |= SRF_EEPROM;\r | |
45f2f245 | 1234 | continue;\r |
1235 | }\r | |
1236 | else if (is_expr("eeprom_lines", &p)) {\r | |
1237 | int scl, sda_in, sda_out;\r | |
1238 | if (!any_checks_passed)\r | |
1239 | goto no_checks;\r | |
1240 | rstrip(p);\r | |
1241 | \r | |
000f5335 | 1242 | if (!parse_3_vals(p, &scl, &sda_in, &sda_out))\r |
45f2f245 | 1243 | goto bad;\r |
000f5335 | 1244 | if (scl < 0 || scl > 15 || sda_in < 0 || sda_in > 15 ||\r |
1245 | sda_out < 0 || sda_out > 15)\r | |
45f2f245 | 1246 | goto bad;\r |
1247 | \r | |
88fd63ad | 1248 | Pico.sv.eeprom_bit_cl = scl;\r |
1249 | Pico.sv.eeprom_bit_in = sda_in;\r | |
1250 | Pico.sv.eeprom_bit_out= sda_out;\r | |
45f2f245 | 1251 | continue;\r |
1252 | }\r | |
000f5335 | 1253 | else if ((tmp = is_expr("prot_ro_value16", &p)) || is_expr("prot_rw_value16", &p)) {\r |
1254 | int addr, mask, val;\r | |
1255 | if (!any_checks_passed)\r | |
1256 | goto no_checks;\r | |
1257 | rstrip(p);\r | |
1258 | \r | |
1259 | if (!parse_3_vals(p, &addr, &mask, &val))\r | |
1260 | goto bad;\r | |
1261 | \r | |
1262 | carthw_sprot_new_location(addr, mask, val, tmp ? 1 : 0);\r | |
1263 | continue;\r | |
1264 | }\r | |
45f2f245 | 1265 | \r |
1266 | \r | |
1267 | bad:\r | |
1268 | elprintf(EL_STATUS, "carthw:%d: unrecognized expression: %s", line, buff);\r | |
1269 | bad_nomsg:\r | |
1270 | skip_sect = 1;\r | |
1271 | continue;\r | |
1272 | \r | |
1273 | no_checks:\r | |
1274 | elprintf(EL_STATUS, "carthw:%d: command without any checks before it: %s", line, buff);\r | |
1275 | skip_sect = 1;\r | |
1276 | continue;\r | |
1277 | }\r | |
005651ce | 1278 | \r |
1279 | if (f != NULL)\r | |
1280 | fclose(f);\r | |
45f2f245 | 1281 | }\r |
1282 | \r | |
a12b1b29 | 1283 | /*\r |
1284 | * various cart-specific things, which can't be handled by generic code\r | |
a12b1b29 | 1285 | */\r |
ae87bffa | 1286 | static void PicoCartDetect(const char *carthw_cfg)\r |
1dceadae | 1287 | {\r |
8b9dbcde | 1288 | int carthw_detected = 0;\r |
000f5335 | 1289 | int fill_sram = 0;\r |
1dceadae | 1290 | \r |
88fd63ad | 1291 | memset(&Pico.sv, 0, sizeof(Pico.sv));\r |
78c2237a | 1292 | if (Pico.rom[MEM_BE2(0x1B0)] == 'R' && Pico.rom[MEM_BE2(0x1B1)] == 'A')\r |
1dceadae | 1293 | {\r |
88fd63ad | 1294 | Pico.sv.start = rom_read32(0x1B4) & ~0xff000001; // align\r |
1295 | Pico.sv.end = (rom_read32(0x1B8) & ~0xff000000) | 1;\r | |
78c2237a | 1296 | if (Pico.rom[MEM_BE2(0x1B3)] & 0x40)\r |
1dceadae | 1297 | // EEPROM\r |
88fd63ad | 1298 | Pico.sv.flags |= SRF_EEPROM;\r |
1299 | Pico.sv.flags |= SRF_ENABLED;\r | |
1dceadae | 1300 | }\r |
88fd63ad | 1301 | if (Pico.sv.end == 0 || Pico.sv.start > Pico.sv.end)\r |
1dceadae | 1302 | {\r |
1303 | // some games may have bad headers, like S&K and Sonic3\r | |
1304 | // note: majority games use 0x200000 as starting address, but there are some which\r | |
1305 | // use something else (0x300000 by HardBall '95). Luckily they have good headers.\r | |
88fd63ad | 1306 | Pico.sv.start = 0x200000;\r |
1307 | Pico.sv.end = 0x203FFF;\r | |
1308 | Pico.sv.flags |= SRF_ENABLED;\r | |
1dceadae | 1309 | }\r |
1dceadae | 1310 | \r |
1311 | // set EEPROM defaults, in case it gets detected\r | |
88fd63ad | 1312 | Pico.sv.eeprom_type = 0; // 7bit (24C01)\r |
1313 | Pico.sv.eeprom_bit_cl = 1;\r | |
1314 | Pico.sv.eeprom_bit_in = 0;\r | |
1315 | Pico.sv.eeprom_bit_out= 0;\r | |
1dceadae | 1316 | \r |
ae87bffa | 1317 | if (carthw_cfg != NULL)\r |
8b9dbcde | 1318 | parse_carthw(carthw_cfg, &fill_sram, &carthw_detected);\r |
1319 | \r | |
1320 | // assume the standard mapper for large roms\r | |
1321 | if (!carthw_detected && Pico.romsize > 0x400000)\r | |
1322 | carthw_ssf2_startup();\r | |
9037e45d | 1323 | \r |
88fd63ad | 1324 | if (Pico.sv.flags & SRF_ENABLED)\r |
757f8dae | 1325 | {\r |
88fd63ad | 1326 | if (Pico.sv.flags & SRF_EEPROM)\r |
1327 | Pico.sv.size = 0x2000;\r | |
45f2f245 | 1328 | else\r |
88fd63ad | 1329 | Pico.sv.size = Pico.sv.end - Pico.sv.start + 1;\r |
757f8dae | 1330 | \r |
88fd63ad | 1331 | Pico.sv.data = calloc(Pico.sv.size, 1);\r |
1332 | if (Pico.sv.data == NULL)\r | |
1333 | Pico.sv.flags &= ~SRF_ENABLED;\r | |
a12b1b29 | 1334 | \r |
88fd63ad | 1335 | if (Pico.sv.eeprom_type == 1) // 1 == 0 in PD EEPROM code\r |
1336 | Pico.sv.eeprom_type = 0;\r | |
0b35350d | 1337 | }\r |
1338 | \r | |
88fd63ad | 1339 | if ((Pico.sv.flags & SRF_ENABLED) && fill_sram)\r |
757f8dae | 1340 | {\r |
45f2f245 | 1341 | elprintf(EL_STATUS, "SRAM fill");\r |
88fd63ad | 1342 | memset(Pico.sv.data, 0xff, Pico.sv.size);\r |
757f8dae | 1343 | }\r |
1f4e9f14 | 1344 | \r |
fe8f2d96 | 1345 | // tweak for Blackthorne: master SH2 overwrites stack of slave SH2 being in PWM\r |
1346 | // interrupt. On real hardware, nothing happens since slave fetches the values\r | |
1347 | // it has written from its cache, but picodrive doesn't emulate caching.\r | |
1348 | // move master memory area down by 0x100 bytes.\r | |
1349 | // XXX replace this abominable hack. It might cause other problems in the game!\r | |
1350 | if (PicoIn.quirks & PQUIRK_BLACKTHORNE_HACK) {\r | |
1351 | int i;\r | |
1352 | unsigned a = 0;\r | |
1353 | for (i = 0; i < Pico.romsize; i += 4) {\r | |
1354 | unsigned v = CPU_BE2(*(u32 *) (Pico.rom + i));\r | |
1355 | if (a && v == a + 0x400) { // patch if 2 pointers with offset 0x400 are found\r | |
0aa63fce | 1356 | elprintf(EL_STATUS, "auto-patching @%06x: %08x->%08x\n", i, v, v - 0x100);\r |
fe8f2d96 | 1357 | *(u32 *) (Pico.rom + i) = CPU_BE2(v - 0x100);\r |
1358 | }\r | |
1359 | // detect a pointer into the incriminating area\r | |
1360 | a = 0;\r | |
1361 | if (v >> 12 == 0x0603f000 >> 12 && !(v & 3))\r | |
1362 | a = v;\r | |
1363 | }\r | |
1364 | }\r | |
1365 | \r | |
1366 | // tweak for Mars Check Program: copies 32K longwords (128KB) from a 64KB buffer\r | |
1367 | // in ROM or DRAM to SDRAM with DMA in 4-longword mode, overwriting an SDRAM comm\r | |
1368 | // area in turn. This crashes the test on emulators without CPU cache emulation.\r | |
1369 | // This may be a bug in Mars Check, since it's only checking for the 64KB result.\r | |
1370 | // Patch the DMA transfers so that they transfer only 64KB.\r | |
1371 | if (PicoIn.quirks & PQUIRK_MARSCHECK_HACK) {\r | |
1372 | int i;\r | |
1373 | unsigned a = 0;\r | |
1374 | for (i = 0; i < Pico.romsize; i += 4) {\r | |
1375 | unsigned v = CPU_BE2(*(u32 *) (Pico.rom + i));\r | |
1376 | if (a == 0xffffff8c && v == 0x5ee1) { // patch if 4-long xfer written to CHCR\r | |
0aa63fce | 1377 | elprintf(EL_STATUS, "auto-patching @%06x: %08x->%08x\n", i, v, v & ~0x800);\r |
fe8f2d96 | 1378 | *(u32 *) (Pico.rom + i) = CPU_BE2(v & ~0x800); // change to half-sized xfer\r |
1379 | }\r | |
1380 | a = v;\r | |
1381 | }\r | |
1382 | }\r | |
1dceadae | 1383 | }\r |
1384 | \r | |
b784d4a5 | 1385 | static void PicoCartDetectMS(void)\r |
1386 | {\r | |
1387 | memset(&Pico.sv, 0, sizeof(Pico.sv));\r | |
1388 | \r | |
1389 | // Always map SRAM, since there's no indicator in ROM if it's needed or not\r | |
1390 | // TODO: this should somehow be coming from a cart database!\r | |
1391 | \r | |
1392 | Pico.sv.size = 0x8000; // Sega mapper, 2 banks of 16 KB each\r | |
1393 | Pico.sv.flags |= SRF_ENABLED;\r | |
1394 | Pico.sv.data = calloc(Pico.sv.size, 1);\r | |
1395 | if (Pico.sv.data == NULL)\r | |
1396 | Pico.sv.flags &= ~SRF_ENABLED;\r | |
1397 | }\r | |
005651ce | 1398 | // vim:shiftwidth=2:expandtab\r |