add NPUMDIMG support

This commit is contained in:
tpu 2013-05-08 23:36:57 +08:00
parent 34da6f983a
commit c9af16e332
8 changed files with 501 additions and 60 deletions

View File

@ -170,6 +170,7 @@
<ClCompile Include="FileSystems\DirectoryFileSystem.cpp" />
<ClCompile Include="FileSystems\ISOFileSystem.cpp" />
<ClCompile Include="FileSystems\MetaFileSystem.cpp" />
<ClCompile Include="FileSystems\tlzrc.cpp" />
<ClCompile Include="Font\PGF.cpp" />
<ClCompile Include="HLE\HLE.cpp" />
<ClCompile Include="HLE\HLETables.cpp" />

View File

@ -407,6 +407,10 @@
<ClCompile Include="HLE\sceNp.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
<ClCompile Include="HLE\sceMp3.cpp" />
<ClCompile Include="FileSystems\tlzrc.cpp">
<Filter>FileSystems</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
@ -752,6 +756,7 @@
<ClInclude Include="HLE\sceNp.h">
<Filter>HLE\Libraries</Filter>
</ClInclude>
<ClInclude Include="HLE\sceMp3.h" />
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />

View File

@ -15,15 +15,18 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
extern "C"
{
#include "zlib.h"
};
#include "BlockDevices.h"
#include <cstdio>
#include <cstring>
extern "C"
{
#include "zlib.h"
#include "ext/libkirk/amctrl.h"
};
BlockDevice *constructBlockDevice(const char *filename) {
// Check for CISO
FILE *f = fopen(filename, "rb");
@ -34,6 +37,8 @@ BlockDevice *constructBlockDevice(const char *filename) {
fclose(f);
if (!memcmp(buffer, "CISO", 4) && size == 4)
return new CISOFileBlockDevice(filename);
else if (!memcmp(buffer, "\x00PBP", 4) && size == 4)
return new NPDRMDemoBlockDevice(filename);
else
return new FileBlockDevice(filename);
}
@ -201,19 +206,143 @@ bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr)
}
NPDRMDemoBlockDevice::NPDRMDemoBlockDevice(std::string _filename)
: filename_(_filename), pbpReader_(_filename.c_str()) {
std::string paramSfo;
pbpReader_.GetSubFileAsString(PBP_PARAM_SFO, &paramSfo);
}
NPDRMDemoBlockDevice::NPDRMDemoBlockDevice(std::string _filename)
: filename_(_filename)
{
MAC_KEY mkey;
CIPHER_KEY ckey;
u8 np_header[256];
u32 tableOffset, tableSize;
u32 lbaStart, lbaEnd;
NPDRMDemoBlockDevice::~NPDRMDemoBlockDevice() {
f = fopen(_filename.c_str(), "rb");
fseek(f, 0x24, SEEK_SET);
fread(&psarOffset, 1, 4, f);
fseek(f, psarOffset, SEEK_SET);
size_t readSize = fread(&np_header, 1, 256, f);
if(readSize!=256){
ERROR_LOG(LOADER, "Invalid NPUMDIMG header!");
}
// getkey
sceDrmBBMacInit(&mkey, 3);
sceDrmBBMacUpdate(&mkey, np_header, 0xc0);
bbmac_getkey(&mkey, np_header+0xc0, vkey);
// decrypt NP header
memcpy(hkey, np_header+0xa0, 0x10);
sceDrmBBCipherInit(&ckey, 1, 2, hkey, vkey, 0);
sceDrmBBCipherUpdate(&ckey, np_header+0x40, 0x60);
sceDrmBBCipherFinal(&ckey);
lbaStart = *(u32*)(np_header+0x54); // LBA start
lbaEnd = *(u32*)(np_header+0x64); // LBA end
lbaSize = (lbaEnd-lbaStart+1); // LBA size of ISO
blockLBAs = *(u32*)(np_header+0x0c); // block size in LBA
blockSize = blockLBAs*2048;
numBlocks = (lbaSize+blockLBAs-1)/blockLBAs; // total blocks;
blockBuf = new u8[blockSize];
tempBuf = new u8[blockSize];
tableOffset = *(u32*)(np_header+0x6c); // table offset
fseek(f, psarOffset+tableOffset, SEEK_SET);
tableSize = numBlocks*32;
table = new table_info[numBlocks];
readSize = fread(table, 1, tableSize, f);
if(readSize!=tableSize){
ERROR_LOG(LOADER, "Invalid NPUMDIMG table!");
}
u32 *p = (u32*)table;
u32 i, k0, k1, k2, k3;
for(i=0; i<numBlocks; i++){
k0 = p[0]^p[1];
k1 = p[1]^p[2];
k2 = p[0]^p[3];
k3 = p[2]^p[3];
p[4] ^= k3;
p[5] ^= k1;
p[6] ^= k2;
p[7] ^= k0;
p += 8;
}
currentBlock = -1;
}
bool NPDRMDemoBlockDevice::ReadBlock(int blockNumber, u8 *outPtr) {
// TODO: Fill in decryption code here. Use pbpReader to read the file - might need to
// extend its functionality to do it efficiently.
return false;
NPDRMDemoBlockDevice::~NPDRMDemoBlockDevice()
{
fclose(f);
delete [] table;
delete [] tempBuf;
delete [] blockBuf;
}
int lzrc_decompress(void *out, int out_len, void *in, int in_len);
bool NPDRMDemoBlockDevice::ReadBlock(int blockNumber, u8 *outPtr)
{
CIPHER_KEY ckey;
int block, lba, lzsize;
size_t readSize;
u8 *readBuf;
lba = blockNumber-currentBlock;
if(lba>=0 && lba<blockLBAs){
memcpy(outPtr, blockBuf+lba*2048, 2048);
return true;
}
block = blockNumber/blockLBAs;
lba = blockNumber%blockLBAs;
currentBlock = block*blockLBAs;
if(table[block].unk_1c!=0){
if(block==(numBlocks-1))
return true; // demos make by fake_np
else
return false;
}
fseek(f, psarOffset+table[block].offset, SEEK_SET);
if(table[block].size<blockSize)
readBuf = tempBuf;
else
readBuf = blockBuf;
readSize = fread(readBuf, 1, table[block].size, f);
if(readSize!=table[block].size){
if(block==(numBlocks-1))
return true;
else
return false;
}
if((table[block].flag&1)==0){
// skip mac check
}
if((table[block].flag&4)==0){
sceDrmBBCipherInit(&ckey, 1, 2, hkey, vkey, table[block].offset>>4);
sceDrmBBCipherUpdate(&ckey, readBuf, table[block].size);
sceDrmBBCipherFinal(&ckey);
}
if(table[block].size<blockSize){
lzsize = lzrc_decompress(blockBuf, 0x00100000, readBuf, table[block].size);
if(lzsize!=blockSize){
ERROR_LOG(LOADER, "LZRC decompress error! lzsize=%d\n", lzsize);
return false;
}
}
memcpy(outPtr, blockBuf+lba*2048, 2048);
return true;
}

View File

@ -73,6 +73,14 @@ private:
// For encrypted ISOs in PBP files.
struct table_info {
u8 mac[16];
u32 offset;
int size;
int flag;
int unk_1c;
};
class NPDRMDemoBlockDevice : public BlockDevice
{
public:
@ -80,14 +88,25 @@ public:
~NPDRMDemoBlockDevice();
bool ReadBlock(int blockNumber, u8 *outPtr);
u32 GetNumBlocks() {return (u32)numBlocks_;}
u32 GetNumBlocks() {return (u32)lbaSize;}
private:
std::string filename_;
PBPReader pbpReader_;
FILE *file_;
size_t size_;
size_t numBlocks_;
FILE *f;
u32 lbaSize;
u32 psarOffset;
int blockSize;
int blockLBAs;
u32 numBlocks;
u8 vkey[16];
u8 hkey[16];
struct table_info *table;
int currentBlock;
u8 *blockBuf;
u8 *tempBuf;
};

295
Core/FileSystems/tlzrc.cpp Normal file
View File

@ -0,0 +1,295 @@
// tlzrc.c: LZRC decodeer
// based on benhur's code, rewrite by tpu
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include "Common.h"
/*************************************************************/
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;
/*************************************************************/
typedef struct{
// input stream
u8 *input;
int in_ptr;
int in_len;
// output stream
u8 *output;
int out_ptr;
int out_len;
// range decode
u32 range;
u32 code;
u32 out_code;
u8 lc;
u8 bm_literal[8][256];
u8 bm_dist_bits[8][39];
u8 bm_dist[18][8];
u8 bm_match[8][8];
u8 bm_len[8][31];
}LZRC_DECODE;
/*************************************************************/
static u8 rc_getbyte(LZRC_DECODE *rc)
{
if(rc->in_ptr == rc->in_len){
_dbg_assert_msg_(LOADER, false, "LZRC: End of input!");
}
return rc->input[rc->in_ptr++];
}
static void rc_putbyte(LZRC_DECODE *rc, u8 byte)
{
if(rc->out_ptr == rc->out_len){
_dbg_assert_msg_(LOADER, false, "LZRC: Output overflow!");
}
rc->output[rc->out_ptr++] = byte;
}
static void rc_init(LZRC_DECODE *rc, void *out, int out_len, void *in, int in_len)
{
rc->input = (u8*)in;
rc->in_len = in_len;
rc->in_ptr = 0;
rc->output = (u8*)out;
rc->out_len = out_len;
rc->out_ptr = 0;
rc->range = 0xffffffff;
rc->lc = rc_getbyte(rc);
rc->code = (rc_getbyte(rc)<<24) |
(rc_getbyte(rc)<<16) |
(rc_getbyte(rc)<< 8) |
(rc_getbyte(rc)<< 0) ;
rc->out_code = 0xffffffff;
memset(rc->bm_literal, 0x80, 2048);
memset(rc->bm_dist_bits, 0x80, 312);
memset(rc->bm_dist, 0x80, 144);
memset(rc->bm_match, 0x80, 64);
memset(rc->bm_len, 0x80, 248);
}
/*************************************************************/
/* range decode */
static void normalize(LZRC_DECODE *rc)
{
if(rc->range<0x01000000){
rc->range <<= 8;
rc->code = (rc->code<<8)+rc->input[rc->in_ptr];
rc->in_ptr++;
}
}
/* Decode a bit */
static int rc_bit(LZRC_DECODE *rc, u8 *prob)
{
u32 bound;
normalize(rc);
bound = (rc->range>>8)*(*prob);
*prob -= *prob>>3;
if(rc->code < bound){
rc->range = bound;
*prob += 31;
return 1;
}else{
rc->code -= bound;
rc->range -= bound;
return 0;
}
}
/* Decode a bittree starting from MSB */
static int rc_bittree(LZRC_DECODE *rc, u8 *probs, int limit)
{
int number = 1;
do{
number = (number<<1)+rc_bit(rc, probs+number);
}while(number<limit);
return number;
}
/*
* decode a number
*
* a number are divide into three part:
* MSB 2bits
* direct bits (don't use probability modle)
* LSB 3bits
*/
static int rc_number(LZRC_DECODE *rc, u8 *prob, int n)
{
int i, number = 1;
if(n>3){
number = (number<<1)+rc_bit(rc, prob+3);
if(n>4){
number = (number<<1)+rc_bit(rc, prob+3);
if(n>5){
// direct bits
normalize(rc);
for(i=0; i<n-5; i++){
rc->range >>= 1;
number <<= 1;
if (rc->code < rc->range){
number += 1;
}else{
rc->code -= rc->range;
}
}
}
}
}
if(n>0){
number = (number<<1)+rc_bit(rc, prob);
if(n>1){
number = (number<<1)+rc_bit(rc, prob+1);
if(n>2){
number = (number<<1)+rc_bit(rc, prob+2);
}
}
}
return number;
}
int lzrc_decompress(void *out, int out_len, void *in, int in_len)
{
LZRC_DECODE rc;
int match_step, rc_state, len_state, dist_state;
int i, bit, byte, last_byte;
int match_len, len_bits;
int match_dist, dist_bits, limit;
u8 *match_src;
int round = -1;
rc_init(&rc, out, out_len, in, in_len);
if(rc.lc&0x80){
/* plain text */
memcpy(rc.output, rc.input+5, rc.code);
return rc.code;
}
rc_state = 0;
last_byte = 0;
while (1) {
round += 1;
match_step = 0;
bit = rc_bit(&rc, &rc.bm_match[rc_state][match_step]);
if (bit==0) {
/* 0 -> raw char */
if(rc_state>0)
rc_state -= 1;
byte = rc_bittree(&rc, &rc.bm_literal[((last_byte>>rc.lc)&0x07)][0], 0x100);
byte -= 0x100;
rc_putbyte(&rc, byte);
} else {
/* 1 -> a match */
/* find bits of match length */
len_bits = 0;
for(i=0; i<7; i++){
match_step += 1;
bit = rc_bit(&rc, &rc.bm_match[rc_state][match_step]);
if(bit==0)
break;
len_bits += 1;
}
/* find match length */
if(len_bits==0){
match_len = 1;
}else{
len_state = ((len_bits-1)<<2)+((rc.out_ptr<<(len_bits-1))&0x03);
match_len = rc_number(&rc, &rc.bm_len[rc_state][len_state], len_bits);
if (match_len == 0xFF){
//end of stream
return rc.out_ptr;
}
}
/* find number of bits of match distance */
dist_state = 0;
limit = 8;
if(match_len>2){
dist_state += 7;
limit = 44;
}
dist_bits = rc_bittree(&rc, &rc.bm_dist_bits[len_bits][dist_state], limit);
dist_bits -= limit;
/* find match distance */
if(dist_bits>0){
match_dist = rc_number(&rc, &rc.bm_dist[dist_bits][0], dist_bits);
} else {
match_dist = 1;
}
/* copy match bytes */
if(match_dist>rc.out_ptr || match_dist<0){
printf("match_dist out of range! %08x\n", match_dist);
return -1;
}
match_src = rc.output+rc.out_ptr-match_dist;
for(i=0; i<match_len+1; i++){
rc_putbyte(&rc, *match_src++);
}
rc_state = 6+((rc.out_ptr+1)&1);
}
last_byte = rc.output[rc.out_ptr-1];
}
}

View File

@ -37,12 +37,20 @@ EmuFileType Identify_File(const char *filename)
return FILETYPE_ERROR;
}
u32 id;
size_t readSize = fread(&id,4,1,f);
fclose(f);
u32 psar_offset, psar_id;
size_t readSize = fread(&id,4,1,f);
if(readSize != 1)
return FILETYPE_ERROR;
psar_id = 0;
fseek(f, 0x24, SEEK_SET);
fread(&psar_offset, 4, 1, f);
fseek(f, psar_offset, SEEK_SET);
fread(&psar_id, 4, 1, f);
fclose(f);
if (strlen(filename) < 5) {
ERROR_LOG(LOADER, "invalid filename %s", filename);
}
@ -60,7 +68,10 @@ EmuFileType Identify_File(const char *filename)
}
else if (id == 'PBP\x00')
{
return FILETYPE_PSP_PBP;
if(psar_id == 'MUPN')
return FILETYPE_PSP_ISO_NP;
else
return FILETYPE_PSP_PBP;
}
else
{
@ -101,6 +112,7 @@ bool LoadFile(const char *filename, std::string *error_string)
return Load_PSP_ELF_PBP(filename, error_string);
}
case FILETYPE_PSP_ISO:
case FILETYPE_PSP_ISO_NP:
pspFileSystem.SetStartingDirectory("disc0:/PSP_GAME/USRDIR");
return Load_PSP_ISO(filename, error_string);
case FILETYPE_ERROR:

View File

@ -24,6 +24,7 @@ enum EmuFileType
FILETYPE_PSP_PBP,
FILETYPE_PSP_ELF,
FILETYPE_PSP_ISO,
FILETYPE_PSP_ISO_NP,
FILETYPE_UNKNOWN_BIN,
FILETYPE_UNKNOWN_ELF,

View File

@ -126,47 +126,26 @@ bool Load_PSP_ISO(const char *filename, std::string *error_string)
bool Load_PSP_ELF_PBP(const char *filename, std::string *error_string)
{
// Figure out if this is a "DEMO" PBP. In that case we want to mount it
// on UMD0:.
PBPReader reader(filename);
// This is really just for headless, might need tweaking later.
if (!PSP_CoreParameter().mountIso.empty())
{
ISOFileSystem *umd2 = new ISOFileSystem(&pspFileSystem, constructBlockDevice(PSP_CoreParameter().mountIso.c_str()));
// Hacky check, should find something better
if (reader.IsValid() && reader.GetSubFileSize(PBP_UNKNOWN_PSAR) > 0x100000) {
// Yay, got a demo.
ISOFileSystem *umd0 = new ISOFileSystem(&pspFileSystem, new NPDRMDemoBlockDevice(filename));
pspFileSystem.Mount("umd1:", umd0);
pspFileSystem.Mount("disc0:", umd0);
pspFileSystem.Mount("umd:", umd0);
pspFileSystem.Mount("umd0:", umd0);
pspFileSystem.Mount("umd1:", umd2);
pspFileSystem.Mount("disc0:", umd2);
pspFileSystem.Mount("umd:", umd2);
}
std::string bootpath = "disc0:/PSP_GAME/SYSDIR/EBOOT.BIN";
INFO_LOG(LOADER,"Loading %s from demo iso...", bootpath.c_str());
return __KernelLoadExec(bootpath.c_str(), 0, error_string);
} else {
// Classic homebrew PBP.
// This is really just for headless, might need tweaking later.
if (!PSP_CoreParameter().mountIso.empty())
{
ISOFileSystem *umd2 = new ISOFileSystem(&pspFileSystem, constructBlockDevice(PSP_CoreParameter().mountIso.c_str()));
pspFileSystem.Mount("umd1:", umd2);
pspFileSystem.Mount("disc0:", umd2);
pspFileSystem.Mount("umd:", umd2);
}
std::string full_path = filename;
std::string path, file, extension;
SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension);
std::string full_path = filename;
std::string path, file, extension;
SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension);
#ifdef _WIN32
path = ReplaceAll(path, "/", "\\");
path = ReplaceAll(path, "/", "\\");
#endif
DirectoryFileSystem *fs = new DirectoryFileSystem(&pspFileSystem, path);
pspFileSystem.Mount("umd0:", fs);
DirectoryFileSystem *fs = new DirectoryFileSystem(&pspFileSystem, path);
pspFileSystem.Mount("umd0:", fs);
std::string finalName = "umd0:/" + file + extension;
return __KernelLoadExec(finalName.c_str(), 0, error_string);
}
std::string finalName = "umd0:/" + file + extension;
return __KernelLoadExec(finalName.c_str(), 0, error_string);
}