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> |
15 | |
16 | #include "cdrcimg.h" |
17 | |
18 | #define CD_FRAMESIZE_RAW 2352 |
19 | |
20 | static const char *cd_fname; |
21 | static unsigned int *cd_index_table; |
22 | static unsigned int cd_index_len; |
23 | static FILE *cd_file; |
24 | |
25 | static unsigned char cdbuffer[CD_FRAMESIZE_RAW]; |
26 | static unsigned char cdbuffer_compressed[CD_FRAMESIZE_RAW + 100]; |
27 | static int current_sector; |
28 | |
29 | struct CdrStat; |
30 | extern long CDR__getStatus(struct CdrStat *stat); |
31 | |
32 | struct CdrStat |
33 | { |
34 | unsigned long Type; |
35 | unsigned long Status; |
36 | unsigned char Time[3]; // current playing time |
37 | }; |
38 | |
39 | struct trackinfo { |
40 | enum {DATA, CDDA} type; |
41 | char start[3]; // MSF-format |
42 | char length[3]; // MSF-format |
43 | }; |
44 | |
45 | #define MAXTRACKS 100 /* How many tracks can a CD hold? */ |
46 | |
47 | static int numtracks = 0; |
48 | |
49 | #define btoi(b) ((b) / 16 * 10 + (b) % 16) /* BCD to u_char */ |
50 | #define MSF2SECT(m, s, f) (((m) * 60 + (s) - 2) * 75 + (f)) |
51 | |
52 | // return Starting and Ending Track |
53 | // buffer: |
54 | // byte 0 - start track |
55 | // byte 1 - end track |
56 | static long CDRgetTN(unsigned char *buffer) |
57 | { |
58 | buffer[0] = 1; |
59 | buffer[1] = numtracks > 0 ? numtracks : 1; |
60 | |
61 | return 0; |
62 | } |
63 | |
64 | // return Track Time |
65 | // buffer: |
66 | // byte 0 - frame |
67 | // byte 1 - second |
68 | // byte 2 - minute |
69 | static long CDRgetTD(unsigned char track, unsigned char *buffer) |
70 | { |
71 | buffer[2] = 0; |
72 | buffer[1] = 2; |
73 | buffer[0] = 0; |
74 | |
75 | return 0; |
76 | } |
77 | |
78 | // read track |
79 | // time: byte 0 - minute; byte 1 - second; byte 2 - frame |
80 | // uses bcd format |
81 | static long CDRreadTrack(unsigned char *time) |
82 | { |
83 | unsigned int start_byte, size; |
84 | unsigned long cdbuffer_size; |
85 | int ret, sector; |
86 | |
87 | if (cd_file == NULL) |
88 | return -1; |
89 | |
90 | sector = MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2])); |
91 | if (sector == current_sector) { |
92 | // it's already there, nothing to do |
93 | //printf("hit sect %d\n", sector); |
94 | return 0; |
95 | } |
96 | |
97 | if (sector >= cd_index_len) { |
98 | fprintf(stderr, "sector %d is past track end\n", sector); |
99 | return -1; |
100 | } |
101 | |
102 | start_byte = cd_index_table[sector]; |
103 | if (fseek(cd_file, start_byte, SEEK_SET) != 0) { |
104 | fprintf(stderr, "seek error for sector %d at %x: ", |
105 | sector, start_byte); |
106 | perror(NULL); |
107 | return -1; |
108 | } |
109 | |
110 | size = cd_index_table[sector + 1] - start_byte; |
111 | if (size > sizeof(cdbuffer_compressed)) { |
112 | fprintf(stderr, "sector %d is too large: %u\n", sector, size); |
113 | return -1; |
114 | } |
115 | |
116 | if (fread(cdbuffer_compressed, 1, size, cd_file) != size) { |
117 | fprintf(stderr, "read error for sector %d at %x: ", |
118 | sector, start_byte); |
119 | perror(NULL); |
120 | return -1; |
121 | } |
122 | |
123 | cdbuffer_size = sizeof(cdbuffer); |
124 | ret = uncompress(cdbuffer, &cdbuffer_size, cdbuffer_compressed, size); |
125 | if (ret != 0) { |
126 | fprintf(stderr, "uncompress failed with %d for sector %d\n", ret, sector); |
127 | return -1; |
128 | } |
129 | if (cdbuffer_size != sizeof(cdbuffer)) |
130 | printf("%lu != %d\n", cdbuffer_size, sizeof(cdbuffer)); |
131 | |
132 | // done at last! |
133 | current_sector = sector; |
134 | return 0; |
135 | } |
136 | |
137 | // return read track |
138 | static unsigned char *CDRgetBuffer(void) |
139 | { |
140 | return cdbuffer + 12; |
141 | } |
142 | |
143 | // plays cdda audio |
144 | // sector: byte 0 - minute; byte 1 - second; byte 2 - frame |
145 | // does NOT uses bcd format |
146 | static long CDRplay(unsigned char *time) |
147 | { |
148 | return 0; |
149 | } |
150 | |
151 | // stops cdda audio |
152 | static long CDRstop(void) |
153 | { |
154 | return 0; |
155 | } |
156 | |
157 | // gets subchannel data |
158 | static unsigned char* CDRgetBufferSub(void) |
159 | { |
160 | return NULL; |
161 | } |
162 | |
163 | static long CDRgetStatus(struct CdrStat *stat) { |
164 | CDR__getStatus(stat); |
165 | |
166 | stat->Type = 0x01; |
167 | |
168 | return 0; |
169 | } |
170 | |
171 | static long CDRclose(void) |
172 | { |
173 | if (cd_file != NULL) { |
174 | fclose(cd_file); |
175 | cd_file = NULL; |
176 | } |
177 | if (cd_index_table != NULL) { |
178 | free(cd_index_table); |
179 | cd_index_table = NULL; |
180 | } |
181 | return 0; |
182 | } |
183 | |
184 | static long CDRshutdown(void) |
185 | { |
186 | return CDRclose(); |
187 | } |
188 | |
189 | static long CDRinit(void) |
190 | { |
191 | return 0; // do nothing |
192 | } |
193 | |
194 | // This function is invoked by the front-end when opening an ISO |
195 | // file for playback |
196 | static long CDRopen(void) |
197 | { |
198 | // a Z.table file is binary where each element represents |
199 | // one compressed frame. |
200 | // 4 bytes: the offset of the frame in the .Z file |
201 | // 2 bytes: the length of the compressed frame |
202 | struct { |
203 | unsigned int offset; |
204 | unsigned short size; |
205 | } __attribute__((packed)) ztab_entry; |
206 | char table_fname[256]; |
207 | long table_size; |
208 | int i, ret; |
209 | FILE *f; |
210 | |
211 | if (cd_file != NULL) |
212 | return 0; // it's already open |
213 | |
214 | numtracks = 0; |
215 | |
216 | if (cd_fname == NULL) |
217 | return -1; |
218 | |
219 | snprintf(table_fname, sizeof(table_fname), "%s.table", cd_fname); |
220 | f = fopen(table_fname, "rb"); |
221 | if (f == NULL) { |
222 | fprintf(stderr, "missing file: %s: ", table_fname); |
223 | perror(NULL); |
224 | return -1; |
225 | } |
226 | |
227 | ret = fseek(f, 0, SEEK_END); |
228 | if (ret != 0) { |
229 | fprintf(stderr, "failed to seek\n"); |
230 | goto fail_table_io; |
231 | } |
232 | table_size = ftell(f); |
233 | fseek(f, 0, SEEK_SET); |
234 | |
235 | if (table_size > 2 * 1024 * 1024) { |
236 | fprintf(stderr, ".table too large\n"); |
237 | goto fail_table_io; |
238 | } |
239 | |
240 | cd_index_len = table_size / 6; |
241 | cd_index_table = malloc((cd_index_len + 1) * sizeof(cd_index_table[0])); |
242 | if (cd_index_table == NULL) |
243 | goto fail_table_io; |
244 | |
245 | for (i = 0; i < cd_index_len; i++) { |
246 | ret = fread(&ztab_entry, 1, sizeof(ztab_entry), f); |
247 | if (ret != sizeof(ztab_entry)) { |
248 | fprintf(stderr, ".table read failed on entry %d/%d\n", i, cd_index_len); |
249 | goto fail_table_io_read; |
250 | } |
251 | cd_index_table[i] = ztab_entry.offset; |
252 | } |
253 | // fake entry, so that we know last compressed block size |
254 | cd_index_table[i] = ztab_entry.offset + ztab_entry.size; |
255 | |
256 | cd_file = fopen(cd_fname, "rb"); |
257 | if (cd_file == NULL) { |
258 | fprintf(stderr, "faied to open: %s: ", table_fname); |
259 | perror(NULL); |
260 | goto fail_img; |
261 | } |
262 | fclose(f); |
263 | |
264 | printf("Loaded compressed CD Image: %s.\n", cd_fname); |
265 | current_sector = -1; |
266 | |
267 | return 0; |
268 | |
269 | fail_img: |
270 | fail_table_io_read: |
271 | free(cd_index_table); |
272 | cd_index_table = NULL; |
273 | fail_table_io: |
274 | fclose(f); |
275 | return -1; |
276 | } |
277 | |
278 | #define FUNC(n) { #n, n } |
279 | |
280 | static const struct { |
281 | const char *name; |
282 | void *func; |
283 | } plugin_funcs[] = { |
284 | /* CDR */ |
285 | FUNC(CDRinit), |
286 | FUNC(CDRshutdown), |
287 | FUNC(CDRopen), |
288 | FUNC(CDRclose), |
289 | FUNC(CDRgetTN), |
290 | FUNC(CDRgetTD), |
291 | FUNC(CDRreadTrack), |
292 | FUNC(CDRgetBuffer), |
293 | FUNC(CDRgetBufferSub), |
294 | FUNC(CDRplay), |
295 | FUNC(CDRstop), |
296 | FUNC(CDRgetStatus), |
297 | }; |
298 | |
299 | void cdrcimg_set_fname(const char *fname) |
300 | { |
301 | cd_fname = fname; |
302 | } |
303 | |
304 | void *cdrcimg_get_sym(const char *sym) |
305 | { |
306 | int i; |
307 | for (i = 0; i < sizeof(plugin_funcs) / sizeof(plugin_funcs[0]); i++) |
308 | if (strcmp(plugin_funcs[i].name, sym) == 0) |
309 | return plugin_funcs[i].func; |
310 | return NULL; |
311 | } |
312 | |