/* * This file is part of PRO CFW. * PRO CFW 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, either version 3 of the License, or * (at your option) any later version. * PRO CFW 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 for more details. * * You should have received a copy of the GNU General Public License * along with PRO CFW. If not, see #include #include #include "printk.h" #include "utils.h" #include "systemctrl_private.h" #include "lz4.h" #define MAX_RETRIES 8 #define MAX_DIR_LEVEL 8 #define CISO_IDX_BUFFER_SIZE 0x200 #define CISO_DEC_BUFFER_SIZE 0x2000 #define ISO_STANDARD_ID "CD001" typedef struct _CISOHeader { u8 magic[4]; /* +00 : 'C','I','S','O' */ u32 header_size; u64 total_bytes; /* +08 : number of original data size */ u32 block_size; /* +10 : number of compressed block size */ u8 ver; /* +14 : version 01 */ u8 align; /* +15 : align of index (offset = index[n]< 0) { fd = sceIoOpen(g_filename, PSP_O_RDONLY, 0777); if (fd >= 0) { break; } sceKernelDelayThread(100000); } if (fd >= 0) { g_isofd = fd; } return fd; } static int readRawData(void* addr, u32 size, u32 offset) { int ret, i; SceOff ofs; for(i=0; i= 0) { break; } else { printk("%s: sceIoLseek -> 0x%08X\n", __func__, (int)ofs); reOpen(); } sceKernelDelayThread(100000); } for(i=0; i= 0) { break; } else { printk("%s: sceIoRead -> 0x%08X\n", __func__, ret); reOpen(); sceIoLseek(g_isofd, offset, PSP_SEEK_SET); } sceKernelDelayThread(100000); } return ret; } static int readSectorCompressed(int sector, void *addr) { int ret; int n_sector; u32 offset, next_offset; int size; n_sector = sector - g_CISO_cur_idx; // not within sector idx cache? if (g_CISO_cur_idx == -1 || n_sector < 0 || n_sector >= NELEMS(g_CISO_idx_cache)) { ret = readRawData(g_CISO_idx_cache, sizeof(g_CISO_idx_cache), (sector << 2) + sizeof(CISOHeader)); if (ret != sizeof(g_CISO_idx_cache)) { return -21; } g_CISO_cur_idx = sector; n_sector = 0; } offset = (g_CISO_idx_cache[n_sector] & 0x7FFFFFFF) << g_ciso_h.align; // is uncompressed data? if (g_CISO_idx_cache[n_sector] & 0x80000000) { return readRawData(addr, SECTOR_SIZE, offset); } sector++; n_sector = sector - g_CISO_cur_idx; if (g_CISO_cur_idx == -1 || n_sector < 0 || n_sector >= NELEMS(g_CISO_idx_cache)) { ret = readRawData(g_CISO_idx_cache, sizeof(g_CISO_idx_cache), (sector << 2) + sizeof(CISOHeader)); if (ret != sizeof(g_CISO_idx_cache)) { return -22; } g_CISO_cur_idx = sector; n_sector = 0; } next_offset = (g_CISO_idx_cache[n_sector] & 0x7FFFFFFF) << g_ciso_h.align; size = next_offset - offset; if(g_ciso_h.align) size += 1 << g_ciso_h.align; if (size <= SECTOR_SIZE) size = SECTOR_SIZE; if (g_ciso_dec_buf_offset == (u32)-1 || offset < g_ciso_dec_buf_offset || offset + size >= g_ciso_dec_buf_offset + g_ciso_dec_buf_size) { ret = readRawData(PTR_ALIGN_64(g_ciso_dec_buf), size, offset); if (ret < 0) { g_ciso_dec_buf_offset = (u32)-1; return -24; } g_ciso_dec_buf_offset = offset; g_ciso_dec_buf_size = ret; } if(!lz4_compressed) { ret = sceKernelDeflateDecompress(addr, SECTOR_SIZE, PTR_ALIGN_64(g_ciso_dec_buf) + offset - g_ciso_dec_buf_offset, 0); } else { ret = LZ4_decompress_fast(PTR_ALIGN_64(g_ciso_dec_buf) + offset - g_ciso_dec_buf_offset, addr, SECTOR_SIZE); if(ret < 0) { ret = -20; printk("%s: -> %d\n", __func__, ret); } } return ret < 0 ? ret : SECTOR_SIZE; } static int readSector(u32 sector, void *buf) { int ret; u32 pos; if (g_is_compressed) { ret = readSectorCompressed(sector, buf); } else { pos = isoLBA2Pos(sector, 0); ret = readRawData(buf, SECTOR_SIZE, pos); } return ret; } static void normalizeName(char *filename) { char *p; p = strstr(filename, ";1"); if (p) { *p = '\0'; } } static int findFile(const char * file, u32 lba, u32 dir_size, u32 is_dir, Iso9660DirectoryRecord *result_record) { u32 pos; int ret; Iso9660DirectoryRecord *rec; char name[32]; int re; pos = isoLBA2Pos(lba, 0); re = lba = 0; while ( re < dir_size ) { if (isoPos2LBA(pos) != lba) { lba = isoPos2LBA(pos); ret = readSector(lba, g_sector_buffer); if (ret != SECTOR_SIZE) { return -23; } } rec = (Iso9660DirectoryRecord*)&g_sector_buffer[isoPos2OffsetInSector(pos)]; if(rec->len_dr == 0) { u32 remaining; remaining = isoPos2RestSize(pos); pos += remaining; re += remaining; continue; } #ifdef DEBUG if(rec->len_dr < rec->len_fi + sizeof(*rec)) { printk("%s: Corrupted directory record found in %s, LBA %d\n", __func__, g_filename, (int)lba); } #endif if(rec->len_fi > 32) { return -11; } if(rec->len_fi == 1 && rec->fi == 0) { if (0 == strcmp(file, ".")) { memcpy(result_record, rec, sizeof(*result_record)); return 0; } } else if(rec->len_fi == 1 && rec->fi == 1) { if (0 == strcmp(file, "..")) { // didn't support .. return -19; } } else { memset(name, 0, sizeof(name)); memcpy(name, &rec->fi, rec->len_fi); normalizeName(name); if (0 == strcmp(name, file)) { if (is_dir) { if(!(rec->fileFlags & ISO9660_FILEFLAGS_DIR)) { return -14; } } memcpy(result_record, rec, sizeof(*result_record)); return 0; } } pos += rec->len_dr; re += rec->len_dr; } return -18; } static int findPath(const char *path, Iso9660DirectoryRecord *result_record) { int level = 0, ret; const char *cur_path, *next; u32 lba, dir_size; char cur_dir[32]; if (result_record == NULL) { return -17; } memset(result_record, 0, sizeof(*result_record)); lba = g_root_record.lsbStart; dir_size = g_root_record.lsbDataLength; cur_path = path; while(*cur_path == '/') { cur_path++; } next = strchr(cur_path, '/'); while (next != NULL) { if (next-cur_path >= sizeof(cur_dir)) { return -15; } memset(cur_dir, 0, sizeof(cur_dir)); strncpy(cur_dir, cur_path, next-cur_path); cur_dir[next-cur_path] = '\0'; if (0 == strcmp(cur_dir, ".")) { } else if (0 == strcmp(cur_dir, "..")) { level--; } else { level++; } if(level > MAX_DIR_LEVEL) { return -16; } ret = findFile(cur_dir, lba, dir_size, 1, result_record); if (ret < 0) { return ret; } lba = result_record->lsbStart; dir_size = result_record->lsbDataLength; cur_path=next+1; // skip unwant path separator while(*cur_path == '/') { cur_path++; } next = strchr(cur_path, '/'); } ret = findFile(cur_path, lba, dir_size, 0, result_record); return ret; } int isoOpen(const char *path) { int ret; u32 *magic; if (g_isofd >= 0) { isoClose(); } g_filename = path; if (reOpen() < 0) { printk("%s: open failed %s -> 0x%08X\n", __func__, g_filename, g_isofd); ret = -2; goto error; } sceIoLseek(g_isofd, 0, PSP_SEEK_SET); memset(&g_ciso_h, 0, sizeof(g_ciso_h)); ret = sceIoRead(g_isofd, &g_ciso_h, sizeof(g_ciso_h)); if (ret != sizeof(g_ciso_h)) { ret = -9; goto error; } magic = (u32*)g_ciso_h.magic; if ((*magic == 0x4F534943 || *magic == 0x4F53495A) && g_ciso_h.block_size == SECTOR_SIZE) { lz4_compressed = (*magic == 0x4F53495A) ? 1 : 0; g_is_compressed = 1; } else { g_is_compressed = 0; } if (g_is_compressed) { g_total_sectors = g_ciso_h.total_bytes / g_ciso_h.block_size; g_CISO_cur_idx = -1; if (g_ciso_dec_buf == NULL) { g_ciso_dec_buf = oe_malloc(CISO_DEC_BUFFER_SIZE + (1 << g_ciso_h.align) + 64); if (g_ciso_dec_buf == NULL) { printk("oe_malloc -> 0x%08x\n", (uint)g_ciso_dec_buf); ret = -6; goto error; } } memset(g_CISO_idx_cache, 0, sizeof(g_CISO_idx_cache)); g_ciso_dec_buf_offset = (u32)-1; g_ciso_dec_buf_size = 0; g_CISO_cur_idx = -1; } else { SceOff size, orig; orig = sceIoLseek(g_isofd, 0, PSP_SEEK_CUR); size = sceIoLseek(g_isofd, 0, PSP_SEEK_END); sceIoLseek(g_isofd, orig, PSP_SEEK_SET); g_total_sectors = isoPos2LBA((u32)size); } ret = readSector(16, g_sector_buffer); if (ret != SECTOR_SIZE) { ret = -7; goto error; } if (memcmp(&g_sector_buffer[1], ISO_STANDARD_ID, sizeof(ISO_STANDARD_ID)-1)) { printk("%s: vol descriptor not found\n", __func__); ret = -10; goto error; } memcpy(&g_root_record, &g_sector_buffer[0x9C], sizeof(g_root_record)); return 0; error: if (g_isofd >= 0) { isoClose(); } return ret; } int isoGetTotalSectorSize(void) { return g_total_sectors; } void isoClose(void) { sceIoClose(g_isofd); g_isofd = -1; g_filename = NULL; if (g_ciso_dec_buf != NULL) { oe_free(g_ciso_dec_buf); g_ciso_dec_buf = NULL; } g_total_sectors = 0; } int isoGetFileInfo(char * path, u32 *filesize, u32 *lba) { int ret; Iso9660DirectoryRecord rec; ret = findPath(path, &rec); if (ret < 0) { return ret; } *lba = rec.lsbStart; if (filesize != NULL) { *filesize = rec.lsbDataLength; } return 0; } int isoRead(void *buffer, u32 lba, int offset, u32 size) { u32 pos, re; int ret; void *o_buffer = buffer; pos = isoLBA2Pos(lba, offset); while(size > 0) { if (isoPos2LBA(pos) >= g_total_sectors) { break; } ret = readSector(isoPos2LBA(pos), g_sector_buffer); if (ret != SECTOR_SIZE) { printk("%s: readSector -> 0x%08X\n", __func__, ret); return -20; } re = MIN(isoPos2RestSize(pos), size); memcpy(buffer, g_sector_buffer+isoPos2OffsetInSector(pos), re); size -= re; pos += re; buffer += re; } return buffer - o_buffer; }