cdrcimg: support .bz/.znx/eboot formats
[pcsx_rearmed.git] / plugins / cdrcimg / cdrcimg.c
... / ...
CommitLineData
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>
15#include <bzlib.h>
16
17#include "cdrcimg.h"
18
19#define PFX "cdrcimg: "
20#define err(f, ...) fprintf(stderr, PFX f, ##__VA_ARGS__)
21
22#define CD_FRAMESIZE_RAW 2352
23
24enum {
25 CDRC_ZLIB,
26 CDRC_ZLIB2,
27 CDRC_BZ,
28};
29
30static const char *cd_fname;
31static unsigned int *cd_index_table;
32static unsigned int cd_index_len;
33static unsigned int cd_sectors_per_blk;
34static int cd_compression;
35static FILE *cd_file;
36
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;
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
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
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;
130 int ret, sector, block;
131
132 if (cd_file == NULL)
133 return -1;
134
135 sector = MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2]));
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) {
157 // it's already there, nothing to do
158 //printf("hit sect %d\n", sector);
159 return 0;
160 }
161
162 if (sector >= cd_index_len * cd_sectors_per_blk) {
163 err("sector %d is past track end\n", sector);
164 return -1;
165 }
166
167 start_byte = cd_index_table[block];
168 if (fseek(cd_file, start_byte, SEEK_SET) != 0) {
169 err("seek error for block %d at %x: ",
170 block, start_byte);
171 perror(NULL);
172 return -1;
173 }
174
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);
178 return -1;
179 }
180
181 if (fread(cdbuffer->compressed, 1, size, cd_file) != size) {
182 err("read error for block %d at %x: ", block, start_byte);
183 perror(NULL);
184 return -1;
185 }
186
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
204 if (ret != 0) {
205 err("uncompress failed with %d for block %d, sector %d\n",
206 ret, block, sector);
207 return -1;
208 }
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);
212
213 // done at last!
214 current_block = block;
215 return 0;
216}
217
218// return read track
219static unsigned char *CDRgetBuffer(void)
220{
221 return cdbuffer->raw[current_sect_in_blk] + 12;
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{
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;
370}
371
372// This function is invoked by the front-end when opening an ISO
373// file for playback
374static long CDRopen(void)
375{
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;
389 char table_fname[256];
390 long table_size;
391 int i, ret;
392 char *ext;
393 FILE *f = NULL;
394
395 if (cd_file != NULL)
396 return 0; // it's already open
397
398 numtracks = 0;
399 current_block = -1;
400 current_sect_in_blk = 0;
401
402 if (cd_fname == NULL)
403 return -1;
404
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
434 f = fopen(table_fname, "rb");
435 if (f == NULL) {
436 err("missing file: %s: ", table_fname);
437 perror(NULL);
438 return -1;
439 }
440
441 ret = fseek(f, 0, SEEK_END);
442 if (ret != 0) {
443 err("failed to seek\n");
444 goto fail_table_io;
445 }
446 table_size = ftell(f);
447 fseek(f, 0, SEEK_SET);
448
449 if (table_size > 4 * 1024 * 1024) {
450 err(".table too large\n");
451 goto fail_table_io;
452 }
453
454 cd_index_len = table_size / tabentry_size;
455
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
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;
494 }
495 cd_sectors_per_blk = 10;
496 break;
497 }
498
499 cd_file = fopen(cd_fname, "rb");
500 if (cd_file == NULL) {
501 err("failed to open: %s: ", table_fname);
502 perror(NULL);
503 goto fail_img;
504 }
505 fclose(f);
506
507 printf(PFX "Loaded compressed CD Image: %s.\n", cd_fname);
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