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/Log.h"
25 #include "Common/Swap.h"
26 #include "Core/Loaders.h"
27 #include "Core/Host.h"
28 #include "Core/FileSystems/BlockDevices.h"
29 
30 extern "C"
31 {
32 #include "zlib.h"
33 #include "ext/libkirk/amctrl.h"
34 #include "ext/libkirk/kirk_engine.h"
35 };
36 
37 std::mutex NPDRMDemoBlockDevice::mutex_;
38 
constructBlockDevice(FileLoader * fileLoader)39 BlockDevice *constructBlockDevice(FileLoader *fileLoader) {
40 	// Check for CISO
41 	if (!fileLoader->Exists())
42 		return nullptr;
43 	char buffer[4]{};
44 	size_t size = fileLoader->ReadAt(0, 1, 4, buffer);
45 	if (size == 4 && !memcmp(buffer, "CISO", 4))
46 		return new CISOFileBlockDevice(fileLoader);
47 	if (size == 4 && !memcmp(buffer, "\x00PBP", 4)) {
48 		uint32_t psarOffset = 0;
49 		size = fileLoader->ReadAt(0x24, 1, 4, &psarOffset);
50 		if (size == 4 && psarOffset < fileLoader->FileSize())
51 			return new NPDRMDemoBlockDevice(fileLoader);
52 	}
53 	return new FileBlockDevice(fileLoader);
54 }
55 
CalculateCRC(volatile bool * cancel)56 u32 BlockDevice::CalculateCRC(volatile bool *cancel) {
57 	u32 crc = crc32(0, Z_NULL, 0);
58 
59 	u8 block[2048];
60 	for (u32 i = 0; i < GetNumBlocks(); ++i) {
61 		if (cancel && *cancel)
62 			return 0;
63 		if (!ReadBlock(i, block, true)) {
64 			ERROR_LOG(FILESYS, "Failed to read block for CRC");
65 			return 0;
66 		}
67 		crc = crc32(crc, block, 2048);
68 	}
69 
70 	return crc;
71 }
72 
NotifyReadError()73 void BlockDevice::NotifyReadError() {
74 	auto err = GetI18NCategory("Error");
75 	if (!reportedError_) {
76 		host->NotifyUserMessage(err->T("Game disc read error - ISO corrupt"), 6.0f);
77 		reportedError_ = true;
78 	}
79 }
80 
FileBlockDevice(FileLoader * fileLoader)81 FileBlockDevice::FileBlockDevice(FileLoader *fileLoader)
82 	: fileLoader_(fileLoader) {
83 	filesize_ = fileLoader->FileSize();
84 }
85 
~FileBlockDevice()86 FileBlockDevice::~FileBlockDevice() {
87 }
88 
ReadBlock(int blockNumber,u8 * outPtr,bool uncached)89 bool FileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr, bool uncached) {
90 	FileLoader::Flags flags = uncached ? FileLoader::Flags::HINT_UNCACHED : FileLoader::Flags::NONE;
91 	if (fileLoader_->ReadAt((u64)blockNumber * (u64)GetBlockSize(), 1, 2048, outPtr, flags) != 2048) {
92 		DEBUG_LOG(FILESYS, "Could not read 2048 bytes from block");
93 		return false;
94 	}
95 
96 	return true;
97 }
98 
ReadBlocks(u32 minBlock,int count,u8 * outPtr)99 bool FileBlockDevice::ReadBlocks(u32 minBlock, int count, u8 *outPtr) {
100 	if (fileLoader_->ReadAt((u64)minBlock * (u64)GetBlockSize(), 2048, count, outPtr) != (size_t)count) {
101 		ERROR_LOG(FILESYS, "Could not read %d bytes from block", 2048 * count);
102 		return false;
103 	}
104 	return true;
105 }
106 
107 // .CSO format
108 
109 // compressed ISO(9660) header format
110 typedef struct ciso_header
111 {
112 	unsigned char magic[4];         // +00 : 'C','I','S','O'
113 	u32_le header_size;             // +04 : header size (==0x18)
114 	u64_le total_bytes;             // +08 : number of original data size
115 	u32_le block_size;              // +10 : number of compressed block size
116 	unsigned char ver;              // +14 : version 01
117 	unsigned char align;            // +15 : align of index value
118 	unsigned char rsv_06[2];        // +16 : reserved
119 #if 0
120 	// INDEX BLOCK
121 	unsigned int index[0];          // +18 : block[0] index
122 	unsigned int index[1];          // +1C : block[1] index
123 	:
124 	:
125 	unsigned int index[last];       // +?? : block[last]
126 	unsigned int index[last+1];     // +?? : end of last data point
127 	// DATA BLOCK
128 	unsigned char data[];           // +?? : compressed or plain sector data
129 #endif
130 } CISO_H;
131 
132 
133 // TODO: Need much better error handling.
134 
135 static const u32 CSO_READ_BUFFER_SIZE = 256 * 1024;
136 
CISOFileBlockDevice(FileLoader * fileLoader)137 CISOFileBlockDevice::CISOFileBlockDevice(FileLoader *fileLoader)
138 	: fileLoader_(fileLoader)
139 {
140 	// CISO format is fairly simple, but most tools do not write the header_size.
141 
142 	CISO_H hdr;
143 	size_t readSize = fileLoader->ReadAt(0, sizeof(CISO_H), 1, &hdr);
144 	if (readSize != 1 || memcmp(hdr.magic, "CISO", 4) != 0) {
145 		WARN_LOG(LOADER, "Invalid CSO!");
146 	}
147 	if (hdr.ver > 1) {
148 		WARN_LOG(LOADER, "CSO version too high!");
149 	}
150 
151 	frameSize = hdr.block_size;
152 	if ((frameSize & (frameSize - 1)) != 0)
153 		ERROR_LOG(LOADER, "CSO block size %i unsupported, must be a power of two", frameSize);
154 	else if (frameSize < 0x800)
155 		ERROR_LOG(LOADER, "CSO block size %i unsupported, must be at least one sector", frameSize);
156 
157 	// Determine the translation from block to frame.
158 	blockShift = 0;
159 	for (u32 i = frameSize; i > 0x800; i >>= 1)
160 		++blockShift;
161 
162 	indexShift = hdr.align;
163 	const u64 totalSize = hdr.total_bytes;
164 	numFrames = (u32)((totalSize + frameSize - 1) / frameSize);
165 	numBlocks = (u32)(totalSize / GetBlockSize());
166 	VERBOSE_LOG(LOADER, "CSO numBlocks=%i numFrames=%i align=%i", numBlocks, numFrames, indexShift);
167 
168 	// We might read a bit of alignment too, so be prepared.
169 	if (frameSize + (1 << indexShift) < CSO_READ_BUFFER_SIZE)
170 		readBuffer = new u8[CSO_READ_BUFFER_SIZE];
171 	else
172 		readBuffer = new u8[frameSize + (1 << indexShift)];
173 	zlibBuffer = new u8[frameSize + (1 << indexShift)];
174 	zlibBufferFrame = numFrames;
175 
176 	const u32 indexSize = numFrames + 1;
177 	const size_t headerEnd = hdr.ver > 1 ? (size_t)hdr.header_size : sizeof(hdr);
178 
179 #if COMMON_LITTLE_ENDIAN
180 	index = new u32[indexSize];
181 	if (fileLoader->ReadAt(headerEnd, sizeof(u32), indexSize, index) != indexSize) {
182 		NotifyReadError();
183 		memset(index, 0, indexSize * sizeof(u32));
184 	}
185 #else
186 	index = new u32[indexSize];
187 	u32_le *indexTemp = new u32_le[indexSize];
188 
189 	if (fileLoader->ReadAt(headerEnd, sizeof(u32), indexSize, indexTemp) != indexSize) {
190 		NotifyReadError();
191 		memset(indexTemp, 0, indexSize * sizeof(u32_le));
192 	}
193 
194 	for (u32 i = 0; i < indexSize; i++)
195 		index[i] = indexTemp[i];
196 
197 	delete[] indexTemp;
198 #endif
199 
200 	ver_ = hdr.ver;
201 
202 	// Double check that the CSO is not truncated.  In most cases, this will be the exact size.
203 	u64 fileSize = fileLoader->FileSize();
204 	u64 lastIndexPos = index[indexSize - 1] & 0x7FFFFFFF;
205 	u64 expectedFileSize = lastIndexPos << indexShift;
206 	if (expectedFileSize > fileSize) {
207 		ERROR_LOG(LOADER, "Expected CSO to at least be %lld bytes, but file is %lld bytes. File: '%s'",
208 			expectedFileSize, fileSize, fileLoader->GetPath().c_str());
209 		NotifyReadError();
210 	}
211 }
212 
~CISOFileBlockDevice()213 CISOFileBlockDevice::~CISOFileBlockDevice()
214 {
215 	delete [] index;
216 	delete [] readBuffer;
217 	delete [] zlibBuffer;
218 }
219 
ReadBlock(int blockNumber,u8 * outPtr,bool uncached)220 bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr, bool uncached)
221 {
222 	FileLoader::Flags flags = uncached ? FileLoader::Flags::HINT_UNCACHED : FileLoader::Flags::NONE;
223 	if ((u32)blockNumber >= numBlocks) {
224 		memset(outPtr, 0, GetBlockSize());
225 		return false;
226 	}
227 
228 	const u32 frameNumber = blockNumber >> blockShift;
229 	const u32 idx = index[frameNumber];
230 	const u32 indexPos = idx & 0x7FFFFFFF;
231 	const u32 nextIndexPos = index[frameNumber + 1] & 0x7FFFFFFF;
232 	z_stream z;
233 
234 	const u64 compressedReadPos = (u64)indexPos << indexShift;
235 	const u64 compressedReadEnd = (u64)nextIndexPos << indexShift;
236 	const size_t compressedReadSize = (size_t)(compressedReadEnd - compressedReadPos);
237 	const u32 compressedOffset = (blockNumber & ((1 << blockShift) - 1)) * GetBlockSize();
238 
239 	bool plain = (idx & 0x80000000) != 0;
240 	if (ver_ >= 2) {
241 		// CSO v2+ requires blocks be uncompressed if large enough to be.  High bit means other things.
242 		plain = compressedReadSize >= frameSize;
243 	}
244 	if (plain) {
245 		int readSize = (u32)fileLoader_->ReadAt(compressedReadPos + compressedOffset, 1, GetBlockSize(), outPtr, flags);
246 		if (readSize < GetBlockSize())
247 			memset(outPtr + readSize, 0, GetBlockSize() - readSize);
248 	} else if (zlibBufferFrame == frameNumber) {
249 		// We already have it.  Just apply the offset and copy.
250 		memcpy(outPtr, zlibBuffer + compressedOffset, GetBlockSize());
251 	} else {
252 		const u32 readSize = (u32)fileLoader_->ReadAt(compressedReadPos, 1, compressedReadSize, readBuffer, flags);
253 
254 		z.zalloc = Z_NULL;
255 		z.zfree = Z_NULL;
256 		z.opaque = Z_NULL;
257 		if (inflateInit2(&z, -15) != Z_OK) {
258 			ERROR_LOG(LOADER, "GetBlockSize() ERROR: %s\n", (z.msg) ? z.msg : "?");
259 			NotifyReadError();
260 			return false;
261 		}
262 		z.avail_in = readSize;
263 		z.next_out = frameSize == (u32)GetBlockSize() ? outPtr : zlibBuffer;
264 		z.avail_out = frameSize;
265 		z.next_in = readBuffer;
266 
267 		int status = inflate(&z, Z_FINISH);
268 		if (status != Z_STREAM_END) {
269 			ERROR_LOG(LOADER, "block %d: inflate : %s[%d]\n", blockNumber, (z.msg) ? z.msg : "error", status);
270 			NotifyReadError();
271 			inflateEnd(&z);
272 			memset(outPtr, 0, GetBlockSize());
273 			return false;
274 		}
275 		if (z.total_out != frameSize) {
276 			ERROR_LOG(LOADER, "block %d: block size error %d != %d\n", blockNumber, (u32)z.total_out, frameSize);
277 			NotifyReadError();
278 			inflateEnd(&z);
279 			memset(outPtr, 0, GetBlockSize());
280 			return false;
281 		}
282 		inflateEnd(&z);
283 
284 		if (frameSize != (u32)GetBlockSize()) {
285 			zlibBufferFrame = frameNumber;
286 			memcpy(outPtr, zlibBuffer + compressedOffset, GetBlockSize());
287 		}
288 	}
289 	return true;
290 }
291 
ReadBlocks(u32 minBlock,int count,u8 * outPtr)292 bool CISOFileBlockDevice::ReadBlocks(u32 minBlock, int count, u8 *outPtr) {
293 	if (count == 1) {
294 		return ReadBlock(minBlock, outPtr);
295 	}
296 	if (minBlock >= numBlocks) {
297 		memset(outPtr, 0, GetBlockSize() * count);
298 		return false;
299 	}
300 
301 	const u32 lastBlock = std::min(minBlock + count, numBlocks) - 1;
302 	const u32 missingBlocks = (lastBlock + 1 - minBlock) - count;
303 	if (lastBlock < minBlock + count) {
304 		memset(outPtr + GetBlockSize() * (count - missingBlocks), 0, GetBlockSize() * missingBlocks);
305 	}
306 
307 	const u32 minFrameNumber = minBlock >> blockShift;
308 	const u32 lastFrameNumber = lastBlock >> blockShift;
309 	const u32 afterLastIndexPos = index[lastFrameNumber + 1] & 0x7FFFFFFF;
310 	const u64 totalReadEnd = (u64)afterLastIndexPos << indexShift;
311 
312 	z_stream z;
313 	z.zalloc = Z_NULL;
314 	z.zfree = Z_NULL;
315 	z.opaque = Z_NULL;
316 	if (inflateInit2(&z, -15) != Z_OK) {
317 		ERROR_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(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(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 
NPDRMDemoBlockDevice(FileLoader * fileLoader)385 NPDRMDemoBlockDevice::NPDRMDemoBlockDevice(FileLoader *fileLoader)
386 	: fileLoader_(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(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(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()452 NPDRMDemoBlockDevice::~NPDRMDemoBlockDevice()
453 {
454 	std::lock_guard<std::mutex> guard(mutex_);
455 	delete [] table;
456 	delete [] tempBuf;
457 	delete [] blockBuf;
458 }
459 
460 int lzrc_decompress(void *out, int out_len, void *in, int in_len);
461 
ReadBlock(int blockNumber,u8 * outPtr,bool uncached)462 bool NPDRMDemoBlockDevice::ReadBlock(int blockNumber, u8 *outPtr, bool uncached)
463 {
464 	FileLoader::Flags flags = uncached ? FileLoader::Flags::HINT_UNCACHED : FileLoader::Flags::NONE;
465 	std::lock_guard<std::mutex> guard(mutex_);
466 	CIPHER_KEY ckey;
467 	int block, lba, lzsize;
468 	size_t readSize;
469 	u8 *readBuf;
470 
471 	lba = blockNumber-currentBlock;
472 	if(lba>=0 && lba<blockLBAs){
473 		memcpy(outPtr, blockBuf+lba*2048, 2048);
474 		return true;
475 	}
476 
477 	block = blockNumber/blockLBAs;
478 	lba = blockNumber%blockLBAs;
479 	currentBlock = block*blockLBAs;
480 
481 	if(table[block].unk_1c!=0){
482 		if((u32)block==(numBlocks-1))
483 			return true; // demos make by fake_np
484 		else
485 			return false;
486 	}
487 
488 	if(table[block].size<blockSize)
489 		readBuf = tempBuf;
490 	else
491 		readBuf = blockBuf;
492 
493 	readSize = fileLoader_->ReadAt(psarOffset+table[block].offset, 1, table[block].size, readBuf, flags);
494 	if(readSize != (size_t)table[block].size){
495 		if((u32)block==(numBlocks-1))
496 			return true;
497 		else
498 			return false;
499 	}
500 
501 	if((table[block].flag&1)==0){
502 		// skip mac check
503 	}
504 
505 	if((table[block].flag&4)==0){
506 		sceDrmBBCipherInit(&ckey, 1, 2, hkey, vkey, table[block].offset>>4);
507 		sceDrmBBCipherUpdate(&ckey, readBuf, table[block].size);
508 		sceDrmBBCipherFinal(&ckey);
509 	}
510 
511 	if(table[block].size<blockSize){
512 		lzsize = lzrc_decompress(blockBuf, 0x00100000, readBuf, table[block].size);
513 		if(lzsize!=blockSize){
514 			ERROR_LOG(LOADER, "LZRC decompress error! lzsize=%d\n", lzsize);
515 			NotifyReadError();
516 			return false;
517 		}
518 	}
519 
520 	memcpy(outPtr, blockBuf+lba*2048, 2048);
521 
522 	return true;
523 }
524