gpu_neon: basic frameskip, change check, minor stuff
[pcsx_rearmed.git] / plugins / cdrcimg / cdrcimg.c
CommitLineData
47bf65ab 1/*
2 * (C) GraÅžvydas "notaz" Ignotas, 2010
3 *
4 * This work is licensed under the terms of any of these licenses
5 * (at your option):
6 * - GNU GPL, version 2 or later.
7 * - GNU LGPL, version 2.1 or later.
8 * See the COPYING file in the top-level directory.
9 */
10
11#include <stdio.h>
12#include <string.h>
13#include <stdlib.h>
14#include <zlib.h>
33716956 15#include <bzlib.h>
47bf65ab 16
17#include "cdrcimg.h"
18
33716956 19#define PFX "cdrcimg: "
20#define err(f, ...) fprintf(stderr, PFX f, ##__VA_ARGS__)
21
47bf65ab 22#define CD_FRAMESIZE_RAW 2352
23
33716956 24enum {
25 CDRC_ZLIB,
26 CDRC_ZLIB2,
27 CDRC_BZ,
28};
29
47bf65ab 30static const char *cd_fname;
31static unsigned int *cd_index_table;
32static unsigned int cd_index_len;
33716956 33static unsigned int cd_sectors_per_blk;
34static int cd_compression;
47bf65ab 35static FILE *cd_file;
36
33716956 37static struct {
38 unsigned char raw[16][CD_FRAMESIZE_RAW];
39 unsigned char compressed[CD_FRAMESIZE_RAW * 16 + 100];
40} *cdbuffer;
41static int current_block, current_sect_in_blk;
47bf65ab 42
43struct CdrStat;
44extern long CDR__getStatus(struct CdrStat *stat);
45
46struct CdrStat
47{
48 unsigned long Type;
49 unsigned long Status;
50 unsigned char Time[3]; // current playing time
51};
52
53struct trackinfo {
54 enum {DATA, CDDA} type;
55 char start[3]; // MSF-format
56 char length[3]; // MSF-format
57};
58
59#define MAXTRACKS 100 /* How many tracks can a CD hold? */
60
61static int numtracks = 0;
62
63#define btoi(b) ((b) / 16 * 10 + (b) % 16) /* BCD to u_char */
64#define MSF2SECT(m, s, f) (((m) * 60 + (s) - 2) * 75 + (f))
65
66// return Starting and Ending Track
67// buffer:
68// byte 0 - start track
69// byte 1 - end track
70static long CDRgetTN(unsigned char *buffer)
71{
72 buffer[0] = 1;
73 buffer[1] = numtracks > 0 ? numtracks : 1;
74
75 return 0;
76}
77
78// return Track Time
79// buffer:
80// byte 0 - frame
81// byte 1 - second
82// byte 2 - minute
83static long CDRgetTD(unsigned char track, unsigned char *buffer)
84{
85 buffer[2] = 0;
86 buffer[1] = 2;
87 buffer[0] = 0;
88
89 return 0;
90}
91
33716956 92int uncompress2(void *out, unsigned long *out_size, void *in, unsigned long in_size)
93{
94 static z_stream z;
95 int ret = 0;
96
97 if (z.zalloc == NULL) {
98 // XXX: one-time leak here..
99 z.next_in = Z_NULL;
100 z.avail_in = 0;
101 z.zalloc = Z_NULL;
102 z.zfree = Z_NULL;
103 z.opaque = Z_NULL;
104 ret = inflateInit2(&z, -15);
105 }
106 else
107 ret = inflateReset(&z);
108 if (ret != Z_OK)
109 return ret;
110
111 z.next_in = in;
112 z.avail_in = in_size;
113 z.next_out = out;
114 z.avail_out = *out_size;
115
116 ret = inflate(&z, Z_NO_FLUSH);
117 //inflateEnd(&z);
118
119 *out_size -= z.avail_out;
120 return ret == 1 ? 0 : ret;
121}
122
47bf65ab 123// read track
124// time: byte 0 - minute; byte 1 - second; byte 2 - frame
125// uses bcd format
126static long CDRreadTrack(unsigned char *time)
127{
128 unsigned int start_byte, size;
129 unsigned long cdbuffer_size;
33716956 130 int ret, sector, block;
47bf65ab 131
132 if (cd_file == NULL)
133 return -1;
134
135 sector = MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2]));
33716956 136
137 // avoid division if possible
138 switch (cd_sectors_per_blk) {
139 case 1:
140 block = sector;
141 current_sect_in_blk = 0;
142 break;
143 case 10:
144 block = sector / 10;
145 current_sect_in_blk = sector % 10;
146 break;
147 case 16:
148 block = sector >> 4;
149 current_sect_in_blk = sector & 15;
150 break;
151 default:
152 err("unhandled cd_sectors_per_blk: %d\n", cd_sectors_per_blk);
153 return -1;
154 }
155
156 if (block == current_block) {
47bf65ab 157 // it's already there, nothing to do
158 //printf("hit sect %d\n", sector);
159 return 0;
160 }
161
33716956 162 if (sector >= cd_index_len * cd_sectors_per_blk) {
163 err("sector %d is past track end\n", sector);
47bf65ab 164 return -1;
165 }
166
33716956 167 start_byte = cd_index_table[block];
47bf65ab 168 if (fseek(cd_file, start_byte, SEEK_SET) != 0) {
33716956 169 err("seek error for block %d at %x: ",
170 block, start_byte);
47bf65ab 171 perror(NULL);
172 return -1;
173 }
174
33716956 175 size = cd_index_table[block + 1] - start_byte;
176 if (size > sizeof(cdbuffer->compressed)) {
177 err("block %d is too large: %u\n", block, size);
47bf65ab 178 return -1;
179 }
180
33716956 181 if (fread(cdbuffer->compressed, 1, size, cd_file) != size) {
182 err("read error for block %d at %x: ", block, start_byte);
47bf65ab 183 perror(NULL);
184 return -1;
185 }
186
33716956 187 cdbuffer_size = sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk;
188 switch (cd_compression) {
189 case CDRC_ZLIB:
190 ret = uncompress(cdbuffer->raw[0], &cdbuffer_size, cdbuffer->compressed, size);
191 break;
192 case CDRC_ZLIB2:
193 ret = uncompress2(cdbuffer->raw[0], &cdbuffer_size, cdbuffer->compressed, size);
194 break;
195 case CDRC_BZ:
196 ret = BZ2_bzBuffToBuffDecompress((char *)cdbuffer->raw, (unsigned int *)&cdbuffer_size,
197 (char *)cdbuffer->compressed, size, 0, 0);
198 break;
199 default:
200 err("bad cd_compression: %d\n", cd_compression);
201 return -1;
202 }
203
47bf65ab 204 if (ret != 0) {
33716956 205 err("uncompress failed with %d for block %d, sector %d\n",
206 ret, block, sector);
47bf65ab 207 return -1;
208 }
33716956 209 if (cdbuffer_size != sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk)
210 err("cdbuffer_size: %lu != %d, sector %d\n", cdbuffer_size,
211 sizeof(cdbuffer->raw[0]) * cd_sectors_per_blk, sector);
47bf65ab 212
213 // done at last!
33716956 214 current_block = block;
47bf65ab 215 return 0;
216}
217
218// return read track
219static unsigned char *CDRgetBuffer(void)
220{
33716956 221 return cdbuffer->raw[current_sect_in_blk] + 12;
47bf65ab 222}
223
224// plays cdda audio
225// sector: byte 0 - minute; byte 1 - second; byte 2 - frame
226// does NOT uses bcd format
227static long CDRplay(unsigned char *time)
228{
229 return 0;
230}
231
232// stops cdda audio
233static long CDRstop(void)
234{
235 return 0;
236}
237
238// gets subchannel data
239static unsigned char* CDRgetBufferSub(void)
240{
241 return NULL;
242}
243
244static long CDRgetStatus(struct CdrStat *stat) {
245 CDR__getStatus(stat);
246
247 stat->Type = 0x01;
248
249 return 0;
250}
251
252static long CDRclose(void)
253{
254 if (cd_file != NULL) {
255 fclose(cd_file);
256 cd_file = NULL;
257 }
258 if (cd_index_table != NULL) {
259 free(cd_index_table);
260 cd_index_table = NULL;
261 }
262 return 0;
263}
264
265static long CDRshutdown(void)
266{
267 return CDRclose();
268}
269
270static long CDRinit(void)
271{
33716956 272 if (cdbuffer == NULL) {
273 cdbuffer = malloc(sizeof(*cdbuffer));
274 if (cdbuffer == NULL) {
275 err("OOM\n");
276 return -1;
277 }
278 }
279 return 0;
280}
281
282static long handle_eboot(void)
283{
284 struct {
285 unsigned int sig;
286 unsigned int dontcare[8];
287 unsigned int psar_offs;
288 } pbp_hdr;
289 struct {
290 unsigned int offset;
291 unsigned int size;
292 unsigned int dontcare[6];
293 } index_entry;
294 char psar_sig[9];
295 unsigned int cdimg_base;
296 int i, ret;
297 FILE *f;
298
299 f = fopen(cd_fname, "rb");
300 if (f == NULL) {
301 err("missing file: %s: ", cd_fname);
302 perror(NULL);
303 return -1;
304 }
305
306 ret = fread(&pbp_hdr, 1, sizeof(pbp_hdr), f);
307 if (ret != sizeof(pbp_hdr)) {
308 err("failed to read pbp\n");
309 goto fail_io;
310 }
311
312 ret = fseek(f, pbp_hdr.psar_offs, SEEK_SET);
313 if (ret != 0) {
314 err("failed to seek to %x\n", pbp_hdr.psar_offs);
315 goto fail_io;
316 }
317
318 ret = fread(psar_sig, 1, sizeof(psar_sig), f);
319 if (ret != sizeof(psar_sig)) {
320 err("failed to read psar_sig\n");
321 goto fail_io;
322 }
323
324 psar_sig[8] = 0;
325 if (strcmp(psar_sig, "PSISOIMG") != 0) {
326 err("bad psar_sig: %s\n", psar_sig);
327 goto fail_io;
328 }
329
330 // seek to ISO index
331 ret = fseek(f, 0x4000 - sizeof(psar_sig), SEEK_CUR);
332 if (ret != 0) {
333 err("failed to seek to ISO index\n");
334 goto fail_io;
335 }
336
337 cd_index_len = (0x100000 - 0x4000) / sizeof(index_entry);
338 cd_index_table = malloc((cd_index_len + 1) * sizeof(cd_index_table[0]));
339 if (cd_index_table == NULL)
340 goto fail_io;
341
342 cdimg_base = pbp_hdr.psar_offs + 0x100000;
343 for (i = 0; i < cd_index_len; i++) {
344 ret = fread(&index_entry, 1, sizeof(index_entry), f);
345 if (ret != sizeof(index_entry)) {
346 err("failed to read index_entry #%d\n", i);
347 goto fail_index;
348 }
349
350 if (index_entry.size == 0)
351 break;
352
353 cd_index_table[i] = cdimg_base + index_entry.offset;
354 }
355 cd_index_table[i] = cdimg_base + index_entry.offset + index_entry.size;
356
357 cd_compression = CDRC_ZLIB2;
358 cd_sectors_per_blk = 16;
359 cd_file = f;
360
361 printf(PFX "Loaded EBOOT CD Image: %s.\n", cd_fname);
362 return 0;
363
364fail_index:
365 free(cd_index_table);
366 cd_index_table = NULL;
367fail_io:
368 fclose(f);
369 return -1;
47bf65ab 370}
371
372// This function is invoked by the front-end when opening an ISO
373// file for playback
374static long CDRopen(void)
375{
33716956 376 union {
377 struct {
378 unsigned int offset;
379 unsigned short size;
380 } __attribute__((packed)) ztab_entry;
381 struct {
382 unsigned int offset;
383 unsigned short size;
384 unsigned int dontcare;
385 } __attribute__((packed)) znxtab_entry;
386 unsigned int bztab_entry;
387 } u;
388 int tabentry_size;
47bf65ab 389 char table_fname[256];
390 long table_size;
391 int i, ret;
33716956 392 char *ext;
393 FILE *f = NULL;
47bf65ab 394
395 if (cd_file != NULL)
396 return 0; // it's already open
397
398 numtracks = 0;
33716956 399 current_block = -1;
400 current_sect_in_blk = 0;
47bf65ab 401
402 if (cd_fname == NULL)
403 return -1;
404
33716956 405 ext = strrchr(cd_fname, '.');
406 if (ext == NULL)
407 return -1;
408
409 if (strcasecmp(ext, ".pbp") == 0) {
410 return handle_eboot();
411 }
412
413 // pocketiso stuff
414 else if (strcasecmp(ext, ".z") == 0) {
415 cd_compression = CDRC_ZLIB;
416 tabentry_size = sizeof(u.ztab_entry);
417 snprintf(table_fname, sizeof(table_fname), "%s.table", cd_fname);
418 }
419 else if (strcasecmp(ext, ".znx") == 0) {
420 cd_compression = CDRC_ZLIB;
421 tabentry_size = sizeof(u.znxtab_entry);
422 snprintf(table_fname, sizeof(table_fname), "%s.table", cd_fname);
423 }
424 else if (strcasecmp(ext, ".bz") == 0) {
425 cd_compression = CDRC_BZ;
426 tabentry_size = sizeof(u.bztab_entry);
427 snprintf(table_fname, sizeof(table_fname), "%s.index", cd_fname);
428 }
429 else {
430 err("unhandled extension: %s\n", ext);
431 return -1;
432 }
433
47bf65ab 434 f = fopen(table_fname, "rb");
435 if (f == NULL) {
33716956 436 err("missing file: %s: ", table_fname);
47bf65ab 437 perror(NULL);
438 return -1;
439 }
440
441 ret = fseek(f, 0, SEEK_END);
442 if (ret != 0) {
33716956 443 err("failed to seek\n");
47bf65ab 444 goto fail_table_io;
445 }
446 table_size = ftell(f);
447 fseek(f, 0, SEEK_SET);
448
33716956 449 if (table_size > 4 * 1024 * 1024) {
450 err(".table too large\n");
47bf65ab 451 goto fail_table_io;
452 }
453
33716956 454 cd_index_len = table_size / tabentry_size;
455
47bf65ab 456 cd_index_table = malloc((cd_index_len + 1) * sizeof(cd_index_table[0]));
457 if (cd_index_table == NULL)
458 goto fail_table_io;
459
33716956 460 switch (cd_compression) {
461 case CDRC_ZLIB:
462 // a Z.table file is binary where each element represents
463 // one compressed frame.
464 // 4 bytes: the offset of the frame in the .Z file
465 // 2 bytes: the length of the compressed frame
466 // .znx.table has 4 additional bytes (xa header??)
467 u.znxtab_entry.dontcare = 0;
468 for (i = 0; i < cd_index_len; i++) {
469 ret = fread(&u, 1, tabentry_size, f);
470 if (ret != tabentry_size) {
471 err(".table read failed on entry %d/%d\n", i, cd_index_len);
472 goto fail_table_io_read;
473 }
474 cd_index_table[i] = u.ztab_entry.offset;
475 //if (u.znxtab_entry.dontcare != 0)
476 // printf("znx %08x!\n", u.znxtab_entry.dontcare);
477 }
478 // fake entry, so that we know last compressed block size
479 cd_index_table[i] = u.ztab_entry.offset + u.ztab_entry.size;
480 cd_sectors_per_blk = 1;
481 break;
482 case CDRC_BZ:
483 // the .BZ.table file is arranged so that one entry represents
484 // 10 compressed frames. Each element is a 4 byte unsigned integer
485 // representing the offset in the .BZ file. Last entry is the size
486 // of the compressed file.
487 for (i = 0; i < cd_index_len; i++) {
488 ret = fread(&u.bztab_entry, 1, sizeof(u.bztab_entry), f);
489 if (ret != sizeof(u.bztab_entry)) {
490 err(".table read failed on entry %d/%d\n", i, cd_index_len);
491 goto fail_table_io_read;
492 }
493 cd_index_table[i] = u.bztab_entry;
47bf65ab 494 }
33716956 495 cd_sectors_per_blk = 10;
496 break;
47bf65ab 497 }
47bf65ab 498
499 cd_file = fopen(cd_fname, "rb");
500 if (cd_file == NULL) {
33716956 501 err("failed to open: %s: ", table_fname);
47bf65ab 502 perror(NULL);
503 goto fail_img;
504 }
505 fclose(f);
506
33716956 507 printf(PFX "Loaded compressed CD Image: %s.\n", cd_fname);
47bf65ab 508
509 return 0;
510
511fail_img:
512fail_table_io_read:
513 free(cd_index_table);
514 cd_index_table = NULL;
515fail_table_io:
516 fclose(f);
517 return -1;
518}
519
520#define FUNC(n) { #n, n }
521
522static const struct {
523 const char *name;
524 void *func;
525} plugin_funcs[] = {
526 /* CDR */
527 FUNC(CDRinit),
528 FUNC(CDRshutdown),
529 FUNC(CDRopen),
530 FUNC(CDRclose),
531 FUNC(CDRgetTN),
532 FUNC(CDRgetTD),
533 FUNC(CDRreadTrack),
534 FUNC(CDRgetBuffer),
535 FUNC(CDRgetBufferSub),
536 FUNC(CDRplay),
537 FUNC(CDRstop),
538 FUNC(CDRgetStatus),
539};
540
541void cdrcimg_set_fname(const char *fname)
542{
543 cd_fname = fname;
544}
545
546void *cdrcimg_get_sym(const char *sym)
547{
548 int i;
549 for (i = 0; i < sizeof(plugin_funcs) / sizeof(plugin_funcs[0]); i++)
550 if (strcmp(plugin_funcs[i].name, sym) == 0)
551 return plugin_funcs[i].func;
552 return NULL;
553}
554