CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/FileSystems/BlockDevices.cpp
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <cstdio>
19
#include <cstring>
20
#include <algorithm>
21
22
#include "Common/Data/Text/I18n.h"
23
#include "Common/File/FileUtil.h"
24
#include "Common/System/OSD.h"
25
#include "Common/Log.h"
26
#include "Common/Swap.h"
27
#include "Common/File/FileUtil.h"
28
#include "Common/File/DirListing.h"
29
#include "Core/Loaders.h"
30
#include "Core/FileSystems/BlockDevices.h"
31
#include "libchdr/chd.h"
32
33
extern "C"
34
{
35
#include "zlib.h"
36
#include "ext/libkirk/amctrl.h"
37
#include "ext/libkirk/kirk_engine.h"
38
};
39
40
std::mutex NPDRMDemoBlockDevice::mutex_;
41
42
BlockDevice *constructBlockDevice(FileLoader *fileLoader) {
43
if (!fileLoader->Exists()) {
44
return nullptr;
45
}
46
if (fileLoader->IsDirectory()) {
47
ERROR_LOG(Log::Loader, "Can't open directory directly as block device: %s", fileLoader->GetPath().c_str());
48
return nullptr;
49
}
50
51
char buffer[8]{};
52
size_t size = fileLoader->ReadAt(0, 1, 8, buffer);
53
if (size != 8) {
54
// Bad or empty file
55
return nullptr;
56
}
57
58
// Check for CISO
59
if (!memcmp(buffer, "CISO", 4)) {
60
return new CISOFileBlockDevice(fileLoader);
61
} else if (!memcmp(buffer, "\x00PBP", 4)) {
62
uint32_t psarOffset = 0;
63
size = fileLoader->ReadAt(0x24, 1, 4, &psarOffset);
64
if (size == 4 && psarOffset < fileLoader->FileSize())
65
return new NPDRMDemoBlockDevice(fileLoader);
66
} else if (!memcmp(buffer, "MComprHD", 8)) {
67
return new CHDFileBlockDevice(fileLoader);
68
}
69
70
// Should be just a regular ISO file. Let's open it as a plain block device and let the other systems take over.
71
return new FileBlockDevice(fileLoader);
72
}
73
74
void BlockDevice::NotifyReadError() {
75
if (!reportedError_) {
76
auto err = GetI18NCategory(I18NCat::ERRORS);
77
g_OSD.Show(OSDType::MESSAGE_WARNING, err->T("Game disc read error - ISO corrupt"), fileLoader_->GetPath().ToVisualString(), 6.0f);
78
reportedError_ = true;
79
}
80
}
81
82
FileBlockDevice::FileBlockDevice(FileLoader *fileLoader)
83
: BlockDevice(fileLoader) {
84
filesize_ = fileLoader->FileSize();
85
}
86
87
FileBlockDevice::~FileBlockDevice() {
88
}
89
90
bool FileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr, bool uncached) {
91
FileLoader::Flags flags = uncached ? FileLoader::Flags::HINT_UNCACHED : FileLoader::Flags::NONE;
92
size_t retval = fileLoader_->ReadAt((u64)blockNumber * (u64)GetBlockSize(), 1, 2048, outPtr, flags);
93
if (retval != 2048) {
94
DEBUG_LOG(Log::FileSystem, "Could not read 2048 byte block, at block offset %d. Only got %d bytes", blockNumber, (int)retval);
95
return false;
96
}
97
98
return true;
99
}
100
101
bool FileBlockDevice::ReadBlocks(u32 minBlock, int count, u8 *outPtr) {
102
size_t retval = fileLoader_->ReadAt((u64)minBlock * (u64)GetBlockSize(), 2048, count, outPtr);
103
if (retval != (size_t)count) {
104
ERROR_LOG(Log::FileSystem, "Could not read %d blocks, at block offset %d. Only got %d blocks", count, minBlock, (int)retval);
105
return false;
106
}
107
return true;
108
}
109
110
// .CSO format
111
112
// compressed ISO(9660) header format
113
typedef struct ciso_header
114
{
115
unsigned char magic[4]; // +00 : 'C','I','S','O'
116
u32_le header_size; // +04 : header size (==0x18)
117
u64_le total_bytes; // +08 : number of original data size
118
u32_le block_size; // +10 : number of compressed block size
119
unsigned char ver; // +14 : version 01
120
unsigned char align; // +15 : align of index value
121
unsigned char rsv_06[2]; // +16 : reserved
122
#if 0
123
// INDEX BLOCK
124
unsigned int index[0]; // +18 : block[0] index
125
unsigned int index[1]; // +1C : block[1] index
126
:
127
:
128
unsigned int index[last]; // +?? : block[last]
129
unsigned int index[last+1]; // +?? : end of last data point
130
// DATA BLOCK
131
unsigned char data[]; // +?? : compressed or plain sector data
132
#endif
133
} CISO_H;
134
135
136
// TODO: Need much better error handling.
137
138
static const u32 CSO_READ_BUFFER_SIZE = 256 * 1024;
139
140
CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader)
141
: BlockDevice(fileLoader)
142
{
143
// CISO format is fairly simple, but most tools do not write the header_size.
144
145
CISO_H hdr;
146
size_t readSize = fileLoader->ReadAt(0, sizeof(CISO_H), 1, &hdr);
147
if (readSize != 1 || memcmp(hdr.magic, "CISO", 4) != 0) {
148
WARN_LOG(Log::Loader, "Invalid CSO!");
149
}
150
if (hdr.ver > 1) {
151
WARN_LOG(Log::Loader, "CSO version too high!");
152
}
153
154
frameSize = hdr.block_size;
155
if ((frameSize & (frameSize - 1)) != 0)
156
ERROR_LOG(Log::Loader, "CSO block size %i unsupported, must be a power of two", frameSize);
157
else if (frameSize < 0x800)
158
ERROR_LOG(Log::Loader, "CSO block size %i unsupported, must be at least one sector", frameSize);
159
160
// Determine the translation from block to frame.
161
blockShift = 0;
162
for (u32 i = frameSize; i > 0x800; i >>= 1)
163
++blockShift;
164
165
indexShift = hdr.align;
166
const u64 totalSize = hdr.total_bytes;
167
numFrames = (u32)((totalSize + frameSize - 1) / frameSize);
168
numBlocks = (u32)(totalSize / GetBlockSize());
169
VERBOSE_LOG(Log::Loader, "CSO numBlocks=%i numFrames=%i align=%i", numBlocks, numFrames, indexShift);
170
171
// We might read a bit of alignment too, so be prepared.
172
if (frameSize + (1 << indexShift) < CSO_READ_BUFFER_SIZE)
173
readBuffer = new u8[CSO_READ_BUFFER_SIZE];
174
else
175
readBuffer = new u8[frameSize + (1 << indexShift)];
176
zlibBuffer = new u8[frameSize + (1 << indexShift)];
177
zlibBufferFrame = numFrames;
178
179
const u32 indexSize = numFrames + 1;
180
const size_t headerEnd = hdr.ver > 1 ? (size_t)hdr.header_size : sizeof(hdr);
181
182
#if COMMON_LITTLE_ENDIAN
183
index = new u32[indexSize];
184
if (fileLoader->ReadAt(headerEnd, sizeof(u32), indexSize, index) != indexSize) {
185
NotifyReadError();
186
memset(index, 0, indexSize * sizeof(u32));
187
}
188
#else
189
index = new u32[indexSize];
190
u32_le *indexTemp = new u32_le[indexSize];
191
192
if (fileLoader->ReadAt(headerEnd, sizeof(u32), indexSize, indexTemp) != indexSize) {
193
NotifyReadError();
194
memset(indexTemp, 0, indexSize * sizeof(u32_le));
195
}
196
197
for (u32 i = 0; i < indexSize; i++)
198
index[i] = indexTemp[i];
199
200
delete[] indexTemp;
201
#endif
202
203
ver_ = hdr.ver;
204
205
// Double check that the CSO is not truncated. In most cases, this will be the exact size.
206
u64 fileSize = fileLoader->FileSize();
207
u64 lastIndexPos = index[indexSize - 1] & 0x7FFFFFFF;
208
u64 expectedFileSize = lastIndexPos << indexShift;
209
if (expectedFileSize > fileSize) {
210
ERROR_LOG(Log::Loader, "Expected CSO to at least be %lld bytes, but file is %lld bytes. File: '%s'",
211
expectedFileSize, fileSize, fileLoader->GetPath().c_str());
212
NotifyReadError();
213
}
214
}
215
216
CISOFileBlockDevice::~CISOFileBlockDevice()
217
{
218
delete [] index;
219
delete [] readBuffer;
220
delete [] zlibBuffer;
221
}
222
223
bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr, bool uncached)
224
{
225
FileLoader::Flags flags = uncached ? FileLoader::Flags::HINT_UNCACHED : FileLoader::Flags::NONE;
226
if ((u32)blockNumber >= numBlocks) {
227
memset(outPtr, 0, GetBlockSize());
228
return false;
229
}
230
231
const u32 frameNumber = blockNumber >> blockShift;
232
const u32 idx = index[frameNumber];
233
const u32 indexPos = idx & 0x7FFFFFFF;
234
const u32 nextIndexPos = index[frameNumber + 1] & 0x7FFFFFFF;
235
z_stream z{};
236
237
const u64 compressedReadPos = (u64)indexPos << indexShift;
238
const u64 compressedReadEnd = (u64)nextIndexPos << indexShift;
239
const size_t compressedReadSize = (size_t)(compressedReadEnd - compressedReadPos);
240
const u32 compressedOffset = (blockNumber & ((1 << blockShift) - 1)) * GetBlockSize();
241
242
bool plain = (idx & 0x80000000) != 0;
243
if (ver_ >= 2) {
244
// CSO v2+ requires blocks be uncompressed if large enough to be. High bit means other things.
245
plain = compressedReadSize >= frameSize;
246
}
247
if (plain) {
248
int readSize = (u32)fileLoader_->ReadAt(compressedReadPos + compressedOffset, 1, GetBlockSize(), outPtr, flags);
249
if (readSize < GetBlockSize())
250
memset(outPtr + readSize, 0, GetBlockSize() - readSize);
251
} else if (zlibBufferFrame == frameNumber) {
252
// We already have it. Just apply the offset and copy.
253
memcpy(outPtr, zlibBuffer + compressedOffset, GetBlockSize());
254
} else {
255
const u32 readSize = (u32)fileLoader_->ReadAt(compressedReadPos, 1, compressedReadSize, readBuffer, flags);
256
257
z.zalloc = Z_NULL;
258
z.zfree = Z_NULL;
259
z.opaque = Z_NULL;
260
if (inflateInit2(&z, -15) != Z_OK) {
261
ERROR_LOG(Log::Loader, "GetBlockSize() ERROR: %s\n", (z.msg) ? z.msg : "?");
262
NotifyReadError();
263
return false;
264
}
265
z.avail_in = readSize;
266
z.next_out = frameSize == (u32)GetBlockSize() ? outPtr : zlibBuffer;
267
z.avail_out = frameSize;
268
z.next_in = readBuffer;
269
270
int status = inflate(&z, Z_FINISH);
271
if (status != Z_STREAM_END) {
272
ERROR_LOG(Log::Loader, "block %d: inflate : %s[%d]\n", blockNumber, (z.msg) ? z.msg : "error", status);
273
NotifyReadError();
274
inflateEnd(&z);
275
memset(outPtr, 0, GetBlockSize());
276
return false;
277
}
278
if (z.total_out != frameSize) {
279
ERROR_LOG(Log::Loader, "block %d: block size error %d != %d\n", blockNumber, (u32)z.total_out, frameSize);
280
NotifyReadError();
281
inflateEnd(&z);
282
memset(outPtr, 0, GetBlockSize());
283
return false;
284
}
285
inflateEnd(&z);
286
287
if (frameSize != (u32)GetBlockSize()) {
288
zlibBufferFrame = frameNumber;
289
memcpy(outPtr, zlibBuffer + compressedOffset, GetBlockSize());
290
}
291
}
292
return true;
293
}
294
295
bool CISOFileBlockDevice::ReadBlocks(u32 minBlock, int count, u8 *outPtr) {
296
if (count == 1) {
297
return ReadBlock(minBlock, outPtr);
298
}
299
if (minBlock >= numBlocks) {
300
memset(outPtr, 0, GetBlockSize() * count);
301
return false;
302
}
303
304
const u32 lastBlock = std::min(minBlock + count, numBlocks) - 1;
305
const u32 missingBlocks = (lastBlock + 1 - minBlock) - count;
306
if (lastBlock < minBlock + count) {
307
memset(outPtr + GetBlockSize() * (count - missingBlocks), 0, GetBlockSize() * missingBlocks);
308
}
309
310
const u32 minFrameNumber = minBlock >> blockShift;
311
const u32 lastFrameNumber = lastBlock >> blockShift;
312
const u32 afterLastIndexPos = index[lastFrameNumber + 1] & 0x7FFFFFFF;
313
const u64 totalReadEnd = (u64)afterLastIndexPos << indexShift;
314
315
z_stream z{};
316
if (inflateInit2(&z, -15) != Z_OK) {
317
ERROR_LOG(Log::Loader, "Unable to initialize inflate: %s\n", (z.msg) ? z.msg : "?");
318
return false;
319
}
320
321
u64 readBufferStart = 0;
322
u64 readBufferEnd = 0;
323
u32 block = minBlock;
324
const u32 blocksPerFrame = 1 << blockShift;
325
for (u32 frame = minFrameNumber; frame <= lastFrameNumber; ++frame) {
326
const u32 idx = index[frame];
327
const u32 indexPos = idx & 0x7FFFFFFF;
328
const u32 nextIndexPos = index[frame + 1] & 0x7FFFFFFF;
329
330
const u64 frameReadPos = (u64)indexPos << indexShift;
331
const u64 frameReadEnd = (u64)nextIndexPos << indexShift;
332
const u32 frameReadSize = (u32)(frameReadEnd - frameReadPos);
333
const u32 frameBlockOffset = block & ((1 << blockShift) - 1);
334
const u32 frameBlocks = std::min(lastBlock - block + 1, blocksPerFrame - frameBlockOffset);
335
336
if (frameReadEnd > readBufferEnd) {
337
const s64 maxNeeded = totalReadEnd - frameReadPos;
338
const size_t chunkSize = (size_t)std::min(maxNeeded, (s64)std::max(frameReadSize, CSO_READ_BUFFER_SIZE));
339
340
const u32 readSize = (u32)fileLoader_->ReadAt(frameReadPos, 1, chunkSize, readBuffer);
341
if (readSize < chunkSize) {
342
memset(readBuffer + readSize, 0, chunkSize - readSize);
343
}
344
345
readBufferStart = frameReadPos;
346
readBufferEnd = frameReadPos + readSize;
347
}
348
349
u8 *rawBuffer = &readBuffer[frameReadPos - readBufferStart];
350
const int plain = idx & 0x80000000;
351
if (plain) {
352
memcpy(outPtr, rawBuffer + frameBlockOffset * GetBlockSize(), frameBlocks * GetBlockSize());
353
} else {
354
z.avail_in = frameReadSize;
355
z.next_out = frameBlocks == blocksPerFrame ? outPtr : zlibBuffer;
356
z.avail_out = frameSize;
357
z.next_in = rawBuffer;
358
359
int status = inflate(&z, Z_FINISH);
360
if (status != Z_STREAM_END) {
361
ERROR_LOG(Log::Loader, "Inflate frame %d: failed - %s[%d]\n", frame, (z.msg) ? z.msg : "error", status);
362
NotifyReadError();
363
memset(outPtr, 0, frameBlocks * GetBlockSize());
364
} else if (z.total_out != frameSize) {
365
ERROR_LOG(Log::Loader, "Inflate frame %d: block size error %d != %d\n", frame, (u32)z.total_out, frameSize);
366
NotifyReadError();
367
memset(outPtr, 0, frameBlocks * GetBlockSize());
368
} else if (frameBlocks != blocksPerFrame) {
369
memcpy(outPtr, zlibBuffer + frameBlockOffset * GetBlockSize(), frameBlocks * GetBlockSize());
370
// In case we end up reusing it in a single read later.
371
zlibBufferFrame = frame;
372
}
373
374
inflateReset(&z);
375
}
376
377
block += frameBlocks;
378
outPtr += frameBlocks * GetBlockSize();
379
}
380
381
inflateEnd(&z);
382
return true;
383
}
384
385
NPDRMDemoBlockDevice::NPDRMDemoBlockDevice(FileLoader *fileLoader)
386
: BlockDevice(fileLoader)
387
{
388
std::lock_guard<std::mutex> guard(mutex_);
389
MAC_KEY mkey;
390
CIPHER_KEY ckey;
391
u8 np_header[256];
392
u32 tableOffset, tableSize;
393
u32 lbaStart, lbaEnd;
394
395
fileLoader_->ReadAt(0x24, 1, 4, &psarOffset);
396
size_t readSize = fileLoader_->ReadAt(psarOffset, 1, 256, &np_header);
397
if (readSize != 256){
398
ERROR_LOG(Log::Loader, "Invalid NPUMDIMG header!");
399
}
400
401
kirk_init();
402
403
// getkey
404
sceDrmBBMacInit(&mkey, 3);
405
sceDrmBBMacUpdate(&mkey, np_header, 0xc0);
406
bbmac_getkey(&mkey, np_header+0xc0, vkey);
407
408
// decrypt NP header
409
memcpy(hkey, np_header+0xa0, 0x10);
410
sceDrmBBCipherInit(&ckey, 1, 2, hkey, vkey, 0);
411
sceDrmBBCipherUpdate(&ckey, np_header+0x40, 0x60);
412
sceDrmBBCipherFinal(&ckey);
413
414
lbaStart = *(u32*)(np_header+0x54); // LBA start
415
lbaEnd = *(u32*)(np_header+0x64); // LBA end
416
lbaSize = (lbaEnd-lbaStart+1); // LBA size of ISO
417
blockLBAs = *(u32*)(np_header+0x0c); // block size in LBA
418
blockSize = blockLBAs*2048;
419
numBlocks = (lbaSize+blockLBAs-1)/blockLBAs; // total blocks;
420
421
blockBuf = new u8[blockSize];
422
tempBuf = new u8[blockSize];
423
424
tableOffset = *(u32*)(np_header+0x6c); // table offset
425
426
tableSize = numBlocks*32;
427
table = new table_info[numBlocks];
428
429
readSize = fileLoader_->ReadAt(psarOffset + tableOffset, 1, tableSize, table);
430
if(readSize!=tableSize){
431
ERROR_LOG(Log::Loader, "Invalid NPUMDIMG table!");
432
}
433
434
u32 *p = (u32*)table;
435
u32 i, k0, k1, k2, k3;
436
for(i=0; i<numBlocks; i++){
437
k0 = p[0]^p[1];
438
k1 = p[1]^p[2];
439
k2 = p[0]^p[3];
440
k3 = p[2]^p[3];
441
p[4] ^= k3;
442
p[5] ^= k1;
443
p[6] ^= k2;
444
p[7] ^= k0;
445
p += 8;
446
}
447
448
currentBlock = -1;
449
}
450
451
NPDRMDemoBlockDevice::~NPDRMDemoBlockDevice()
452
{
453
std::lock_guard<std::mutex> guard(mutex_);
454
delete [] table;
455
delete [] tempBuf;
456
delete [] blockBuf;
457
}
458
459
int lzrc_decompress(void *out, int out_len, void *in, int in_len);
460
461
bool NPDRMDemoBlockDevice::ReadBlock(int blockNumber, u8 *outPtr, bool uncached)
462
{
463
FileLoader::Flags flags = uncached ? FileLoader::Flags::HINT_UNCACHED : FileLoader::Flags::NONE;
464
std::lock_guard<std::mutex> guard(mutex_);
465
CIPHER_KEY ckey;
466
int block, lba, lzsize;
467
size_t readSize;
468
u8 *readBuf;
469
470
lba = blockNumber-currentBlock;
471
if(lba>=0 && lba<blockLBAs){
472
memcpy(outPtr, blockBuf+lba*2048, 2048);
473
return true;
474
}
475
476
block = blockNumber/blockLBAs;
477
lba = blockNumber%blockLBAs;
478
currentBlock = block*blockLBAs;
479
480
if(table[block].unk_1c!=0){
481
if((u32)block==(numBlocks-1))
482
return true; // demos make by fake_np
483
else
484
return false;
485
}
486
487
if(table[block].size<blockSize)
488
readBuf = tempBuf;
489
else
490
readBuf = blockBuf;
491
492
readSize = fileLoader_->ReadAt(psarOffset+table[block].offset, 1, table[block].size, readBuf, flags);
493
if(readSize != (size_t)table[block].size){
494
if((u32)block==(numBlocks-1))
495
return true;
496
else
497
return false;
498
}
499
500
if((table[block].flag&1)==0){
501
// skip mac check
502
}
503
504
if((table[block].flag&4)==0){
505
sceDrmBBCipherInit(&ckey, 1, 2, hkey, vkey, table[block].offset>>4);
506
sceDrmBBCipherUpdate(&ckey, readBuf, table[block].size);
507
sceDrmBBCipherFinal(&ckey);
508
}
509
510
if(table[block].size<blockSize){
511
lzsize = lzrc_decompress(blockBuf, 0x00100000, readBuf, table[block].size);
512
if(lzsize!=blockSize){
513
ERROR_LOG(Log::Loader, "LZRC decompress error! lzsize=%d\n", lzsize);
514
NotifyReadError();
515
return false;
516
}
517
}
518
519
memcpy(outPtr, blockBuf+lba*2048, 2048);
520
521
return true;
522
}
523
524
/*
525
* CHD file
526
*/
527
static const UINT8 nullsha1[CHD_SHA1_BYTES] = { 0 };
528
529
struct CHDImpl {
530
chd_file *chd = nullptr;
531
const chd_header *header = nullptr;
532
};
533
534
struct ExtendedCoreFile {
535
core_file core; // Must be the first struct member, for some tricky pointer casts.
536
uint64_t seekPos;
537
};
538
539
CHDFileBlockDevice::CHDFileBlockDevice(FileLoader *fileLoader)
540
: BlockDevice(fileLoader), impl_(new CHDImpl())
541
{
542
Path paths[8];
543
paths[0] = fileLoader->GetPath();
544
int depth = 0;
545
546
core_file_ = new ExtendedCoreFile();
547
core_file_->core.argp = fileLoader;
548
core_file_->core.fsize = [](core_file *file) -> uint64_t {
549
FileLoader *loader = (FileLoader *)file->argp;
550
return loader->FileSize();
551
};
552
core_file_->core.fseek = [](core_file *file, int64_t offset, int seekType) -> int {
553
ExtendedCoreFile *coreFile = (ExtendedCoreFile *)file;
554
switch (seekType) {
555
case SEEK_SET:
556
coreFile->seekPos = offset;
557
break;
558
case SEEK_CUR:
559
coreFile->seekPos += offset;
560
break;
561
case SEEK_END:
562
{
563
FileLoader *loader = (FileLoader *)file->argp;
564
coreFile->seekPos = loader->FileSize() + offset;
565
break;
566
}
567
default:
568
break;
569
}
570
return 0;
571
};
572
core_file_->core.fread = [](void *out_data, size_t size, size_t count, core_file *file) {
573
ExtendedCoreFile *coreFile = (ExtendedCoreFile *)file;
574
FileLoader *loader = (FileLoader *)file->argp;
575
uint64_t totalSize = size * count;
576
loader->ReadAt(coreFile->seekPos, totalSize, out_data);
577
coreFile->seekPos += totalSize;
578
return size * count;
579
};
580
core_file_->core.fclose = [](core_file *file) {
581
ExtendedCoreFile *coreFile = (ExtendedCoreFile *)file;
582
delete coreFile;
583
return 0;
584
};
585
586
/*
587
// TODO: Support parent/child CHD files.
588
589
// Default, in case of failure
590
numBlocks = 0;
591
592
chd_header childHeader;
593
594
chd_error err = chd_read_header(paths[0].c_str(), &childHeader);
595
if (err != CHDERR_NONE) {
596
ERROR_LOG(Log::Loader, "Error loading CHD header for '%s': %s", paths[0].c_str(), chd_error_string(err));
597
NotifyReadError();
598
return;
599
}
600
601
if (memcmp(nullsha1, childHeader.parentsha1, sizeof(childHeader.sha1)) != 0) {
602
chd_header parentHeader;
603
604
// Look for parent CHD in current directory
605
Path chdDir = paths[0].NavigateUp();
606
607
std::vector<File::FileInfo> files;
608
if (File::GetFilesInDir(chdDir, &files)) {
609
parentHeader.length = 0;
610
611
for (const auto &file : files) {
612
std::string extension = file.fullName.GetFileExtension();
613
if (extension != ".chd") {
614
continue;
615
}
616
617
if (chd_read_header(filepath.c_str(), &parentHeader) == CHDERR_NONE &&
618
memcmp(parentHeader.sha1, childHeader.parentsha1, sizeof(parentHeader.sha1)) == 0) {
619
// ERROR_LOG(Log::Loader, "Checking '%s'", filepath.c_str());
620
paths[++depth] = filepath;
621
break;
622
}
623
}
624
625
// Check if parentHeader was opened
626
if (parentHeader.length == 0) {
627
ERROR_LOG(Log::Loader, "Error loading CHD '%s': parents not found", fileLoader->GetPath().c_str());
628
NotifyReadError();
629
return;
630
}
631
memcpy(childHeader.parentsha1, parentHeader.parentsha1, sizeof(childHeader.parentsha1));
632
} while (memcmp(nullsha1, childHeader.parentsha1, sizeof(childHeader.sha1)) != 0);
633
}
634
*/
635
636
chd_file *file = nullptr;
637
chd_error err = chd_open_core_file(&core_file_->core, CHD_OPEN_READ, NULL, &file);
638
if (err != CHDERR_NONE) {
639
ERROR_LOG(Log::Loader, "Error loading CHD '%s': %s", paths[depth].c_str(), chd_error_string(err));
640
NotifyReadError();
641
return;
642
}
643
644
impl_->chd = file;
645
impl_->header = chd_get_header(impl_->chd);
646
647
readBuffer = new u8[impl_->header->hunkbytes];
648
currentHunk = -1;
649
blocksPerHunk = impl_->header->hunkbytes / impl_->header->unitbytes;
650
numBlocks = impl_->header->unitcount;
651
}
652
653
CHDFileBlockDevice::~CHDFileBlockDevice()
654
{
655
if (impl_->chd) {
656
chd_close(impl_->chd);
657
delete[] readBuffer;
658
}
659
}
660
661
bool CHDFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr, bool uncached)
662
{
663
if (!impl_->chd) {
664
ERROR_LOG(Log::Loader, "ReadBlock: CHD not open. %s", fileLoader_->GetPath().c_str());
665
return false;
666
}
667
if ((u32)blockNumber >= numBlocks) {
668
memset(outPtr, 0, GetBlockSize());
669
return false;
670
}
671
u32 hunk = blockNumber / blocksPerHunk;
672
u32 blockInHunk = blockNumber % blocksPerHunk;
673
674
if (currentHunk != hunk) {
675
chd_error err = chd_read(impl_->chd, hunk, readBuffer);
676
if (err != CHDERR_NONE) {
677
ERROR_LOG(Log::Loader, "CHD read failed: %d %d %s", blockNumber, hunk, chd_error_string(err));
678
NotifyReadError();
679
}
680
currentHunk = hunk;
681
}
682
memcpy(outPtr, readBuffer + blockInHunk * impl_->header->unitbytes, GetBlockSize());
683
return true;
684
}
685
686
bool CHDFileBlockDevice::ReadBlocks(u32 minBlock, int count, u8 *outPtr) {
687
if (minBlock >= numBlocks) {
688
memset(outPtr, 0, GetBlockSize() * count);
689
return false;
690
}
691
692
for (int i = 0; i < count; i++) {
693
if (!ReadBlock(minBlock + i, outPtr + i * GetBlockSize())) {
694
return false;
695
}
696
}
697
return true;
698
}
699
700