1 |
/* PCSX2 - PS2 Emulator for PCs |
2 |
* Copyright (C) 2002-2010 PCSX2 Dev Team |
3 |
* |
4 |
* PCSX2 is free software: you can redistribute it and/or modify it under the terms |
5 |
* of the GNU Lesser General Public License as published by the Free Software Found- |
6 |
* ation, either version 3 of the License, or (at your option) any later version. |
7 |
* |
8 |
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
9 |
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
10 |
* PURPOSE. See the GNU General Public License for more details. |
11 |
* |
12 |
* You should have received a copy of the GNU General Public License along with PCSX2. |
13 |
* If not, see <http://www.gnu.org/licenses/>. |
14 |
*/ |
15 |
|
16 |
|
17 |
#include "PrecompiledHeader.h" |
18 |
#include "IopCommon.h" |
19 |
|
20 |
#include <stdio.h> |
21 |
#include <fcntl.h> |
22 |
#include <errno.h> |
23 |
|
24 |
#include "IsoFileFormats.h" |
25 |
|
26 |
static bool detect(isoFile *iso) |
27 |
{ |
28 |
u8 buf[2456]; |
29 |
u8* pbuf; |
30 |
|
31 |
if (!isoReadBlock(iso, buf, 16)) return false; // Not readable |
32 |
|
33 |
pbuf = (( iso->flags & ISOFLAGS_BLOCKDUMP_V3 ) ? buf : (buf + 24)); |
34 |
|
35 |
if (strncmp((char*)(pbuf+1), "CD001", 5)) return false; // Not ISO 9660 compliant |
36 |
|
37 |
if (*(u16*)(pbuf+166) == 2048) |
38 |
iso->type = ISOTYPE_CD; |
39 |
else |
40 |
iso->type = ISOTYPE_DVD; |
41 |
|
42 |
return true; // We can deal with this. |
43 |
} |
44 |
|
45 |
static bool _isoReadDtable(isoFile *iso) |
46 |
{ |
47 |
uint ret; |
48 |
|
49 |
_seekfile(iso->handle, 0, SEEK_END); |
50 |
iso->dtablesize = (_tellfile(iso->handle) - 16) / (iso->blocksize + 4); |
51 |
iso->dtable = (u32*)malloc(iso->dtablesize * 4); |
52 |
|
53 |
for (int i = 0; i < iso->dtablesize; i++) |
54 |
{ |
55 |
_seekfile(iso->handle, 16 + (iso->blocksize + 4) * i, SEEK_SET); |
56 |
ret = _readfile(iso->handle, &iso->dtable[i], 4); |
57 |
if (ret < 4) return false; |
58 |
} |
59 |
|
60 |
return true; |
61 |
} |
62 |
|
63 |
static bool tryIsoType(isoFile *iso, u32 size, s32 offset, s32 blockofs) |
64 |
{ |
65 |
iso->blocksize = size; |
66 |
iso->offset = offset; |
67 |
iso->blockofs = blockofs; |
68 |
|
69 |
return detect(iso); |
70 |
} |
71 |
|
72 |
// based on florin's CDVDbin detection code :) |
73 |
// Returns true if the image is valid/known/supported, or false if not (iso->type == ISOTYPE_ILLEGAL). |
74 |
bool isoDetect(isoFile *iso) |
75 |
{ |
76 |
char buf[32]; |
77 |
int len; |
78 |
|
79 |
iso->type = ISOTYPE_ILLEGAL; |
80 |
|
81 |
len = strlen(iso->filename); |
82 |
|
83 |
_seekfile(iso->handle, 0, SEEK_SET); |
84 |
_readfile(iso->handle, buf, 4); |
85 |
|
86 |
if (strncmp(buf, "BDV2", 4) == 0) |
87 |
{ |
88 |
iso->flags = ISOFLAGS_BLOCKDUMP_V2; |
89 |
_readfile(iso->handle, &iso->blocksize, 4); |
90 |
_readfile(iso->handle, &iso->blocks, 4); |
91 |
_readfile(iso->handle, &iso->blockofs, 4); |
92 |
_isoReadDtable(iso); |
93 |
return (detect(iso)); |
94 |
} |
95 |
else if (strncmp(buf, "BDV3", 4) == 0) |
96 |
{ |
97 |
iso->flags = ISOFLAGS_BLOCKDUMP_V3; |
98 |
_readfile(iso->handle, &iso->blocksize, 4); |
99 |
_readfile(iso->handle, &iso->blocks, 4); |
100 |
_readfile(iso->handle, &iso->blockofs, 4); |
101 |
_isoReadDtable(iso); |
102 |
return (detect(iso)); |
103 |
} |
104 |
else |
105 |
{ |
106 |
iso->blocks = 16; |
107 |
} |
108 |
|
109 |
if (tryIsoType(iso, 2048, 0, 24)) return true; // ISO 2048 |
110 |
if (tryIsoType(iso, 2336, 0, 16)) return true; // RAW 2336 |
111 |
if (tryIsoType(iso, 2352, 0, 0)) return true; // RAW 2352 |
112 |
if (tryIsoType(iso, 2448, 0, 0)) return true; // RAWQ 2448 |
113 |
if (tryIsoType(iso, 2048, 150 * 2048, 24)) return true; // NERO ISO 2048 |
114 |
if (tryIsoType(iso, 2352, 150 * 2048, 0)) return true; // NERO RAW 2352 |
115 |
if (tryIsoType(iso, 2448, 150 * 2048, 0)) return true; // NERO RAWQ 2448 |
116 |
if (tryIsoType(iso, 2048, -8, 24)) return true; // ISO 2048 |
117 |
if (tryIsoType(iso, 2352, -8, 0)) return true; // RAW 2352 |
118 |
if (tryIsoType(iso, 2448, -8, 0)) return true; // RAWQ 2448 |
119 |
|
120 |
iso->offset = 0; |
121 |
iso->blocksize = CD_FRAMESIZE_RAW; |
122 |
iso->blockofs = 0; |
123 |
iso->type = ISOTYPE_AUDIO; |
124 |
return true; |
125 |
} |
126 |
|
127 |
isoFile *isoOpen(const char *filename) |
128 |
{ |
129 |
isoFile *iso; |
130 |
|
131 |
iso = (isoFile*)malloc(sizeof(isoFile)); |
132 |
if (iso == NULL) return NULL; |
133 |
|
134 |
memzero( *iso ); |
135 |
strcpy(iso->filename, filename); |
136 |
|
137 |
iso->handle = _openfile( iso->filename, O_RDONLY); |
138 |
if (iso->handle == NULL) |
139 |
{ |
140 |
Console.Error("error loading %s", iso->filename); |
141 |
return NULL; |
142 |
} |
143 |
|
144 |
if (!isoDetect(iso)) return NULL; |
145 |
|
146 |
Console.WriteLn("detected blocksize = %u", iso->blocksize); |
147 |
|
148 |
if ((strlen(iso->filename) > 3) && strncmp(iso->filename + (strlen(iso->filename) - 3), "I00", 3) == 0) |
149 |
{ |
150 |
int i; |
151 |
|
152 |
_closefile(iso->handle); |
153 |
iso->flags |= ISOFLAGS_MULTI; |
154 |
iso->blocks = 0; |
155 |
|
156 |
for (i = 0; i < 8; i++) |
157 |
{ |
158 |
iso->filename[strlen(iso->filename) - 1] = '0' + i; |
159 |
iso->multih[i].handle = _openfile(iso->filename, O_RDONLY); |
160 |
|
161 |
if (iso->multih[i].handle == NULL) |
162 |
{ |
163 |
break; |
164 |
} |
165 |
|
166 |
iso->multih[i].slsn = iso->blocks; |
167 |
_seekfile(iso->multih[i].handle, 0, SEEK_END); |
168 |
iso->blocks += (u32)((_tellfile(iso->multih[i].handle) - iso->offset) / (iso->blocksize)); |
169 |
iso->multih[i].elsn = iso->blocks - 1; |
170 |
} |
171 |
|
172 |
if (i == 0) |
173 |
{ |
174 |
return NULL; |
175 |
} |
176 |
} |
177 |
|
178 |
if (iso->flags == 0) |
179 |
{ |
180 |
_seekfile(iso->handle, 0, SEEK_END); |
181 |
iso->blocks = (u32)((_tellfile(iso->handle) - iso->offset) / (iso->blocksize)); |
182 |
} |
183 |
|
184 |
switch(iso->type) |
185 |
{ |
186 |
case ISOTYPE_CD: Console.Write("isoOpen(CD): "); break; |
187 |
case ISOTYPE_DVD: Console.Write("isoOpen(DVD): "); break; |
188 |
case ISOTYPE_AUDIO: Console.Write("isoOpen(Audio CD): "); break; |
189 |
case ISOTYPE_DVDDL: Console.Write("isoOpen(DVDDL): "); break; |
190 |
case ISOTYPE_ILLEGAL: |
191 |
default: Console.Write("isoOpen(illegal media): "); break; |
192 |
} |
193 |
Console.WriteLn("%s ok.", iso->filename); |
194 |
Console.WriteLn("The iso has %u blocks (size %u).", iso->blocks, iso->blocksize); |
195 |
Console.WriteLn("The isos offset is %d, and the block offset is %d.", iso->offset, iso->blockofs); |
196 |
|
197 |
return iso; |
198 |
} |
199 |
|
200 |
isoFile *isoCreate(const char *filename, int flags) |
201 |
{ |
202 |
isoFile *iso; |
203 |
char Zfile[256]; |
204 |
|
205 |
iso = (isoFile*)malloc(sizeof(isoFile)); |
206 |
if (iso == NULL) return NULL; |
207 |
|
208 |
memset(iso, 0, sizeof(isoFile)); |
209 |
strcpy(iso->filename, filename); |
210 |
|
211 |
iso->flags = flags; |
212 |
iso->offset = 0; |
213 |
iso->blockofs = 24; |
214 |
iso->blocksize = 2048; |
215 |
|
216 |
if (iso->flags & (ISOFLAGS_Z | ISOFLAGS_Z2 | ISOFLAGS_BZ2)) |
217 |
{ |
218 |
sprintf(Zfile, "%s.table", iso->filename); |
219 |
iso->htable = _openfile(Zfile, O_WRONLY); |
220 |
|
221 |
if (iso->htable == NULL) return NULL; |
222 |
} |
223 |
|
224 |
iso->handle = _openfile(iso->filename, O_WRONLY | O_CREAT); |
225 |
|
226 |
if (iso->handle == NULL) |
227 |
{ |
228 |
Console.Error("Error loading %s", iso->filename); |
229 |
return NULL; |
230 |
} |
231 |
|
232 |
Console.WriteLn("isoCreate: %s ok", iso->filename); |
233 |
Console.WriteLn("offset = %d", iso->offset); |
234 |
|
235 |
return iso; |
236 |
} |
237 |
|
238 |
bool isoSetFormat(isoFile *iso, int blockofs, uint blocksize, uint blocks) |
239 |
{ |
240 |
iso->blocksize = blocksize; |
241 |
iso->blocks = blocks; |
242 |
iso->blockofs = blockofs; |
243 |
|
244 |
Console.WriteLn("blockofs = %d", iso->blockofs); |
245 |
Console.WriteLn("blocksize = %u", iso->blocksize); |
246 |
Console.WriteLn("blocks = %u", iso->blocks); |
247 |
|
248 |
if (iso->flags & ISOFLAGS_BLOCKDUMP_V2) |
249 |
{ |
250 |
if (_writefile(iso->handle, "BDV2", 4) < 4) return false; |
251 |
if (_writefile(iso->handle, &blocksize, 4) < 4) return false; |
252 |
if (_writefile(iso->handle, &blocks, 4) < 4) return false; |
253 |
if (_writefile(iso->handle, &blockofs, 4) < 4) return false; |
254 |
} |
255 |
else if (iso->flags & ISOFLAGS_BLOCKDUMP_V3) |
256 |
{ |
257 |
if (_writefile(iso->handle, "BDV3", 4) < 4) return false; |
258 |
if (_writefile(iso->handle, &blocksize, 4) < 4) return false; |
259 |
if (_writefile(iso->handle, &blocks, 4) < 4) return false; |
260 |
} |
261 |
|
262 |
return true; |
263 |
} |
264 |
|
265 |
bool _isoReadBlock(isoFile *iso, u8 *dst, int lsn) |
266 |
{ |
267 |
u64 ofs = (u64)lsn * iso->blocksize + iso->offset; |
268 |
|
269 |
memset(dst, 0, iso->blockofs); |
270 |
_seekfile(iso->handle, ofs, SEEK_SET); |
271 |
|
272 |
uint ret = _readfile(iso->handle, dst + iso->blockofs, iso->blocksize); |
273 |
|
274 |
if (ret < iso->blocksize) |
275 |
{ |
276 |
Console.Error("read error in _isoReadBlock." ); |
277 |
return false; |
278 |
} |
279 |
|
280 |
return true; |
281 |
} |
282 |
|
283 |
bool _isoReadBlockD(isoFile *iso, u8 *dst, uint lsn) |
284 |
{ |
285 |
uint ret; |
286 |
|
287 |
// Console.WriteLn("_isoReadBlockD %u, blocksize=%u, blockofs=%u\n", lsn, iso->blocksize, iso->blockofs); |
288 |
|
289 |
memset(dst, 0, iso->blockofs); |
290 |
for (int i = 0; i < iso->dtablesize; i++) |
291 |
{ |
292 |
if (iso->dtable[i] != lsn) continue; |
293 |
|
294 |
_seekfile(iso->handle, 16 + i * (iso->blocksize + 4) + 4, SEEK_SET); |
295 |
ret = _readfile(iso->handle, dst + iso->blockofs, iso->blocksize); |
296 |
|
297 |
if (ret < iso->blocksize) return false; |
298 |
|
299 |
return true; |
300 |
} |
301 |
Console.WriteLn("Block %u not found in dump", lsn); |
302 |
|
303 |
return false; |
304 |
} |
305 |
|
306 |
bool _isoReadBlockM(isoFile *iso, u8 *dst, uint lsn) |
307 |
{ |
308 |
u64 ofs; |
309 |
uint ret, i; |
310 |
|
311 |
for (i = 0; i < 8; i++) |
312 |
{ |
313 |
if ((lsn >= iso->multih[i].slsn) && (lsn <= iso->multih[i].elsn)) |
314 |
{ |
315 |
break; |
316 |
} |
317 |
} |
318 |
|
319 |
if (i == 8) return false; |
320 |
|
321 |
ofs = (u64)(lsn - iso->multih[i].slsn) * iso->blocksize + iso->offset; |
322 |
|
323 |
// Console.WriteLn("_isoReadBlock %u, blocksize=%u, blockofs=%u\n", lsn, iso->blocksize, iso->blockofs); |
324 |
|
325 |
memset(dst, 0, iso->blockofs); |
326 |
_seekfile(iso->multih[i].handle, ofs, SEEK_SET); |
327 |
ret = _readfile(iso->multih[i].handle, dst + iso->blockofs, iso->blocksize); |
328 |
|
329 |
if (ret < iso->blocksize) |
330 |
{ |
331 |
Console.Error("read error in _isoReadBlockM"); |
332 |
return false; |
333 |
} |
334 |
|
335 |
return true; |
336 |
} |
337 |
|
338 |
bool isoReadBlock(isoFile *iso, u8 *dst, uint lsn) |
339 |
{ |
340 |
bool ret; |
341 |
|
342 |
if (lsn > iso->blocks) |
343 |
{ |
344 |
Console.WriteLn("isoReadBlock: %u > %u", lsn, iso->blocks); |
345 |
return false; |
346 |
} |
347 |
|
348 |
if (iso->flags & ISOFLAGS_BLOCKDUMP_V2) |
349 |
ret = _isoReadBlockD(iso, dst, lsn); |
350 |
else if( iso->flags & ISOFLAGS_BLOCKDUMP_V3 ) |
351 |
ret = _isoReadBlockD(iso, dst, lsn); |
352 |
else if (iso->flags & ISOFLAGS_MULTI) |
353 |
ret = _isoReadBlockM(iso, dst, lsn); |
354 |
else |
355 |
ret = _isoReadBlock(iso, dst, lsn); |
356 |
|
357 |
if (!ret) return false; |
358 |
|
359 |
if (iso->type == ISOTYPE_CD) |
360 |
{ |
361 |
lsn_to_msf(dst + 12, lsn); |
362 |
dst[15] = 2; |
363 |
} |
364 |
|
365 |
return true; |
366 |
} |
367 |
|
368 |
|
369 |
bool _isoWriteBlock(isoFile *iso, u8 *src, uint lsn) |
370 |
{ |
371 |
uint ret; |
372 |
u64 ofs = (u64)lsn * iso->blocksize + iso->offset; |
373 |
|
374 |
_seekfile(iso->handle, ofs, SEEK_SET); |
375 |
ret = _writefile(iso->handle, src + iso->blockofs, iso->blocksize); |
376 |
if (ret < iso->blocksize) return false; |
377 |
|
378 |
return true; |
379 |
} |
380 |
|
381 |
bool _isoWriteBlockD(isoFile *iso, u8 *src, uint lsn) |
382 |
{ |
383 |
uint ret; |
384 |
|
385 |
ret = _writefile(iso->handle, &lsn, 4); |
386 |
if (ret < 4) return false; |
387 |
ret = _writefile(iso->handle, src + iso->blockofs, iso->blocksize); |
388 |
|
389 |
if (ret < iso->blocksize) return false; |
390 |
|
391 |
return true; |
392 |
} |
393 |
|
394 |
bool isoWriteBlock(isoFile *iso, u8 *src, uint lsn) |
395 |
{ |
396 |
if (iso->flags & ISOFLAGS_BLOCKDUMP_V3) |
397 |
return _isoWriteBlockD(iso, src, lsn); |
398 |
else |
399 |
return _isoWriteBlock(iso, src, lsn); |
400 |
} |
401 |
|
402 |
void isoClose(isoFile *iso) |
403 |
{ |
404 |
if (iso == NULL ) return; |
405 |
if (iso->handle) _closefile(iso->handle); |
406 |
if (iso->htable) _closefile(iso->htable); |
407 |
safe_free( iso->buffer ); |
408 |
safe_free( iso->dtable ); |
409 |
safe_free( iso ); |
410 |
} |
411 |
|