cdrcimg: support .bz/.znx/eboot formats
[pcsx_rearmed.git] / plugins / cdrcimg / cdrcimg.c
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
24 enum {
25         CDRC_ZLIB,
26         CDRC_ZLIB2,
27         CDRC_BZ,
28 };
29
30 static const char *cd_fname;
31 static unsigned int *cd_index_table;
32 static unsigned int  cd_index_len;
33 static unsigned int  cd_sectors_per_blk;
34 static int cd_compression;
35 static FILE *cd_file;
36
37 static struct {
38         unsigned char raw[16][CD_FRAMESIZE_RAW];
39         unsigned char compressed[CD_FRAMESIZE_RAW * 16 + 100];
40 } *cdbuffer;
41 static int current_block, current_sect_in_blk;
42
43 struct CdrStat;
44 extern long CDR__getStatus(struct CdrStat *stat);
45
46 struct CdrStat
47 {
48         unsigned long Type;
49         unsigned long Status;
50         unsigned char Time[3]; // current playing time
51 };
52
53 struct 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
61 static 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
70 static 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
83 static 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
92 int 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
126 static 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
219 static 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
227 static long CDRplay(unsigned char *time)
228 {
229         return 0;
230 }
231
232 // stops cdda audio
233 static long CDRstop(void)
234 {
235         return 0;
236 }
237
238 // gets subchannel data
239 static unsigned char* CDRgetBufferSub(void)
240 {
241         return NULL;
242 }
243
244 static long CDRgetStatus(struct CdrStat *stat) {
245         CDR__getStatus(stat);
246
247         stat->Type = 0x01;
248
249         return 0;
250 }
251
252 static 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
265 static long CDRshutdown(void)
266 {
267         return CDRclose();
268 }
269
270 static 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
282 static 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
364 fail_index:
365         free(cd_index_table);
366         cd_index_table = NULL;
367 fail_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
374 static 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
511 fail_img:
512 fail_table_io_read:
513         free(cd_index_table);
514         cd_index_table = NULL;
515 fail_table_io:
516         fclose(f);
517         return -1;
518 }
519
520 #define FUNC(n) { #n, n }
521
522 static 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
541 void cdrcimg_set_fname(const char *fname)
542 {
543         cd_fname = fname;
544 }
545
546 void *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