Skip to content

Instantly share code, notes, and snippets.

@codestation
Last active August 29, 2015 14:02
Show Gist options
  • Save codestation/bf1cc67ddf7c490c9626 to your computer and use it in GitHub Desktop.
Save codestation/bf1cc67ddf7c490c9626 to your computer and use it in GitHub Desktop.
#define _GNU_SOURCE 1
#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <lz4.h>
#include <openssl/md5.h>
#include <zlib.h>
// PSP compat layer
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define u64 uint64_t
#define SceOff int64_t
#define SceUID int
#define PSP_SEEK_SET SEEK_SET
#define PSP_SEEK_CUR SEEK_CUR
#define PSP_SEEK_END SEEK_END
#define sceIoOpen(a,b,c) open(a,O_RDONLY,S_IRUSR)
#define sceIoLseek lseek
#define sceIoRead read
#define sceIoClose close
#define printk printf
#define sceKernelDelayThread(delay) usleep(delay)
#define oe_malloc malloc
// from misc headers
#define ISO_SECTOR_SIZE 2048
#define CISO_IDX_BUFFER_SIZE 0x200
#define CISO_DEC_BUFFER_SIZE 0x2000
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define NELEMS(a) (sizeof(a) / sizeof(a[0]))
// from ISODrivers/Inferno/isoread.c
#define CISO_IDX_MAX_ENTRIES 4096
#define REMAINDER(a,b) ((a) % (b))
#define IS_DIVISIBLE(a,b) (REMAINDER(a,b) == 0)
#define GET_CSO_OFFSET(block) ((g_cso_idx_cache[block] & 0x7FFFFFFF) << g_CISO_hdr.align)
// 0x00002484
void *g_sector_buf = NULL;
// 0x0000248C
int g_iso_opened = 0;
// 0x000023D0
SceUID g_iso_fd = -1;
// 0x000023D4
int g_total_sectors = -1;
// 0x00002488
static int g_is_ciso = 0;
// 0x000024C0
static void *g_ciso_block_buf = NULL;
// 0x000024C4, size CISO_DEC_BUFFER_SIZE + (1 << g_CISO_hdr.align), align 64
static void *g_ciso_dec_buf = NULL;
// 0x00002704
static int g_CISO_cur_idx = 0;
// 0x00002700
static u32 g_ciso_dec_buf_offset = (u32)-1;
static int g_ciso_dec_buf_size = 0;
// 0x00002720
static u32 g_ciso_total_block = 0;
static int lz4_compressed = 0;
struct CISO_header {
u8 magic[4]; // 0
u32 header_size; // 4
u64 total_bytes; // 8
u32 block_size; // 16
u8 ver; // 20
u8 align; // 21
u8 rsv_06[2]; // 22
};
// 0x00002708
struct CISO_header g_CISO_hdr __attribute__((aligned(64)));
u32 g_CISO_idx_cache[CISO_IDX_BUFFER_SIZE / 4] __attribute__((aligned(64)));
u32 *g_cso_idx_cache = NULL;
u32 g_cso_idx_start_block = -1;
// variables for the simulation
static z_stream z;
static char *g_iso_fn = NULL;
u32 *g_cso_idx_cache_orig = NULL;
void *g_ciso_block_buf_orig = NULL;
void *g_ciso_dec_buf_orig = NULL;
// simulate psp decompress function
int sceKernelDeflateDecompress(u8 *dest, u32 destSize, u8 *src, u32 unknown)
{
int ret = inflateInit2(&z, -15);
z.avail_in = 2048;
z.next_out = dest;
z.avail_out = destSize;
z.next_in = src;
ret = inflate(&z, Z_FULL_FLUSH);
inflateEnd(&z);
if (ret != Z_STREAM_END) {
return -1;
} else {
return destSize;
}
}
void wait_until_ms0_ready()
{
// not used in simulation, lets init zlib here
z.zalloc = Z_NULL;
z.zfree = Z_NULL;
z.opaque = Z_NULL;
}
// 0x00000EE4
static int ciso_get_nsector(SceUID fd)
{
// return g_CISO_hdr.total_bytes / g_CISO_hdr.block_size;;
return g_ciso_total_block;
}
// 0x00000E58
static int iso_get_nsector(SceUID fd)
{
SceOff off, total;
off = sceIoLseek(fd, 0, PSP_SEEK_CUR);
total = sceIoLseek(fd, 0, PSP_SEEK_END);
sceIoLseek(fd, off, PSP_SEEK_SET);
return total / ISO_SECTOR_SIZE;
}
// 0x00000E58
static int get_nsector(void)
{
if(g_is_ciso) {
return ciso_get_nsector(g_iso_fd);
}
return iso_get_nsector(g_iso_fd);
}
// 0x00000F00
static int is_ciso(SceUID fd)
{
int ret;
u32 *magic;
g_CISO_hdr.magic[0] = '\0';
g_ciso_dec_buf_offset = (u32)-1;
g_ciso_dec_buf_size = 0;
sceIoLseek(fd, 0, PSP_SEEK_SET);
ret = sceIoRead(fd, &g_CISO_hdr, sizeof(g_CISO_hdr));
if(ret != sizeof(g_CISO_hdr)) {
ret = -1;
printk("%s: -> %d\n", __func__, ret);
goto exit;
}
magic = (u32*)g_CISO_hdr.magic;
if(*magic == 0x4F534943 || *magic == 0x4F53495A) { // CISO or ZISO
lz4_compressed = (*magic == 0x4F53495A) ? 1 : 0;
g_CISO_cur_idx = -1;
g_ciso_total_block = g_CISO_hdr.total_bytes / g_CISO_hdr.block_size;
printk("%s: total block %d\n", __func__, (int)g_ciso_total_block);
if(g_ciso_dec_buf == NULL) {
g_ciso_dec_buf = oe_malloc(CISO_DEC_BUFFER_SIZE + (1 << g_CISO_hdr.align) + 64);
if(g_ciso_dec_buf == NULL) {
ret = -2;
printk("%s: -> %d\n", __func__, ret);
goto exit;
}
g_ciso_dec_buf_orig = g_ciso_dec_buf;
if((uintptr_t)g_ciso_dec_buf & 63)
g_ciso_dec_buf = (void*)(((uintptr_t)g_ciso_dec_buf & (~63)) + 64);
}
if(g_ciso_block_buf == NULL) {
g_ciso_block_buf = oe_malloc(ISO_SECTOR_SIZE + 64);
if(g_ciso_block_buf == NULL) {
ret = -3;
printk("%s: -> %d\n", __func__, ret);
goto exit;
}
g_ciso_block_buf_orig = g_ciso_block_buf;
if((uintptr_t)g_ciso_block_buf & 63)
g_ciso_block_buf = (void*)(((uintptr_t)g_ciso_block_buf & (~63)) + 64);
}
if(g_cso_idx_cache == NULL) {
g_cso_idx_cache = oe_malloc(CISO_IDX_MAX_ENTRIES * 4 + 64);
if(g_cso_idx_cache == NULL) {
ret = -3;
printk("%s: -> %d\n", __func__, ret);
goto exit;
}
g_cso_idx_cache_orig = g_cso_idx_cache;
if((uintptr_t)g_cso_idx_cache & 63)
g_cso_idx_cache = (void*)(((uintptr_t)g_cso_idx_cache & (~63)) + 64);
}
ret = 0;
} else {
ret = 0x8002012F;
}
exit:
return ret;
}
// 0x000009D4
int iso_open(void)
{
int ret, retries;
wait_until_ms0_ready();
if(g_iso_fd >= 0)
sceIoClose(g_iso_fd);
g_iso_opened = 0;
retries = 0;
do {
g_iso_fd = sceIoOpen(g_iso_fn, 0x000F0001, 0777);
if(g_iso_fd < 0) {
if(++retries >= 16) {
return -1;
}
sceKernelDelayThread(20000);
}
} while(g_iso_fd < 0);
if(g_iso_fd < 0) {
return -1;
}
g_is_ciso = 0;
ret = is_ciso(g_iso_fd);
if(ret >= 0) {
g_is_ciso = 1;
}
g_iso_opened = 1;
g_total_sectors = get_nsector();
return 0;
}
// 0x00000BB4
static int read_raw_data(u8* addr, u32 size, u32 offset)
{
int ret, i;
SceOff ofs;
i = 0;
do {
i++;
ofs = sceIoLseek(g_iso_fd, offset, PSP_SEEK_SET);
if(ofs >= 0) {
i = 0;
break;
} else {
printk("%s: lseek retry %d error 0x%08X\n", __func__, i, (int)ofs);
iso_open();
}
} while(i < 16);
if(i == 16) {
ret = 0x80010013;
goto exit;
}
for(i=0; i<16; ++i) {
ret = sceIoRead(g_iso_fd, addr, size);
if(ret >= 0) {
i = 0;
break;
} else {
printk("%s: read retry %d error 0x%08X\n", __func__, i, ret);
iso_open();
}
}
if(i == 16) {
ret = 0x80010013;
goto exit;
}
exit:
return ret;
}
// 0x00001018
static int read_cso_sector(u8 *addr, int sector)
{
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 = read_raw_data((u8*)g_CISO_idx_cache, sizeof(g_CISO_idx_cache), (sector << 2) + sizeof(struct CISO_header));
if(ret < 0) {
ret = -4;
printk("%s: -> %d\n", __func__, ret);
return ret;
}
g_CISO_cur_idx = sector;
n_sector = 0;
}
offset = (g_CISO_idx_cache[n_sector] & 0x7FFFFFFF) << g_CISO_hdr.align;
// is plain?
if(g_CISO_idx_cache[n_sector] & 0x80000000) {
return read_raw_data(addr, ISO_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 = read_raw_data((u8*)g_CISO_idx_cache, sizeof(g_CISO_idx_cache), (sector << 2) + sizeof(struct CISO_header));
if(ret < 0) {
ret = -5;
printk("%s: -> %d\n", __func__, ret);
return ret;
}
g_CISO_cur_idx = sector;
n_sector = 0;
}
next_offset = (g_CISO_idx_cache[n_sector] & 0x7FFFFFFF) << g_CISO_hdr.align;
size = next_offset - offset;
if(g_CISO_hdr.align)
size += 1 << g_CISO_hdr.align;
if(size <= ISO_SECTOR_SIZE)
size = ISO_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 = read_raw_data(g_ciso_dec_buf, size, offset);
/* May not reach CISO_DEC_BUFFER_SIZE */
if(ret < 0) {
g_ciso_dec_buf_offset = (u32)-1;
ret = -6;
printk("%s: -> %d\n", __func__, ret);
return ret;
}
g_ciso_dec_buf_offset = offset;
g_ciso_dec_buf_size = ret;
}
if(!lz4_compressed) {
ret = sceKernelDeflateDecompress(addr, ISO_SECTOR_SIZE, g_ciso_dec_buf + offset - g_ciso_dec_buf_offset, 0);
} else {
ret = LZ4_decompress_fast(g_ciso_dec_buf + offset - g_ciso_dec_buf_offset, (char *)addr, ISO_SECTOR_SIZE);
if(ret < 0) {
ret = -20;
printk("%s: -> %d\n", __func__, ret);
}
}
return ret < 0 ? ret : ISO_SECTOR_SIZE;
}
static int read_cso_data(u8* addr, u32 size, u32 offset)
{
u32 cur_block;
int pos, ret, read_bytes;
u32 o_offset = offset;
while(size > 0) {
cur_block = offset / ISO_SECTOR_SIZE;
pos = offset & (ISO_SECTOR_SIZE - 1);
if(cur_block >= g_total_sectors) {
// EOF reached
break;
}
ret = read_cso_sector(g_ciso_block_buf, cur_block);
if(ret != ISO_SECTOR_SIZE) {
ret = -7;
printk("%s: -> %d\n", __func__, ret);
return ret;
}
read_bytes = MIN(size, (ISO_SECTOR_SIZE - pos));
memcpy(addr, g_ciso_block_buf + pos, read_bytes);
size -= read_bytes;
addr += read_bytes;
offset += read_bytes;
}
return offset - o_offset;
}
/**
* decompress (if necessary) a compressed block and copy it to the destination
* If offset_shift is nonzero then the decompressed data will by used from this offset.
* The block_num is used to know if it needs to be decompressed
*/
static int decompress_block(u8 *dst, u8 *src, int size, int block_num, int offset_shift)
{
int ret;
if(g_cso_idx_cache[block_num] & 0x80000000) {
// do not copy if the block is already in place
if(offset_shift > 0 || src != dst) {
// no decompression is needed, copy the data from the end of the buffer to the start
memmove(dst, src + offset_shift, size);
}
ret = size;
} else {
if(!lz4_compressed) {
// gzip decompress
ret = sceKernelDeflateDecompress(g_ciso_dec_buf, ISO_SECTOR_SIZE, src, 0);
} else {
// LZ4 decompress
ret = LZ4_decompress_fast((char *)src, (char *)g_ciso_dec_buf, ISO_SECTOR_SIZE);
}
if(ret < 0) {
ret = -20;
printk("%s: -> %d\n", __func__, ret);
return ret;
}
// copy the decompressed data to the destination buffer
memcpy(dst, g_ciso_dec_buf + offset_shift, size);
}
return ret;
}
static int refresh_cso_index(u32 size, u32 offset) {
// seek the first block offset
u32 starting_block = offset / ISO_SECTOR_SIZE;
// calculate the last needed block and read the index
u32 ending_block = (offset + size) / ISO_SECTOR_SIZE + 1;
u32 idx_size = (ending_block - starting_block + 1) * 4;
if (idx_size > CISO_IDX_MAX_ENTRIES * 4) {
// the requested index size is too big
return -1;
}
// out of scope, read cso index table again
if (starting_block < g_cso_idx_start_block|| ending_block >= g_cso_idx_start_block + CISO_IDX_MAX_ENTRIES) {
sceIoLseek(g_iso_fd, starting_block * 4 + 24, PSP_SEEK_SET);
u32 total_blocks = g_CISO_hdr.total_bytes / g_CISO_hdr.block_size;
if (starting_block > total_blocks) {
// the requested block goes beyond the max block number
return -1;
}
if (starting_block + 4096 > total_blocks) {
idx_size = (total_blocks - starting_block + 1) * 4;
} else {
idx_size = CISO_IDX_MAX_ENTRIES * 4;
}
sceIoRead(g_iso_fd, g_cso_idx_cache, idx_size);
g_cso_idx_start_block = starting_block;
return 0;
}
return starting_block - g_cso_idx_start_block;
}
static int read_cso_data_ng(u8 *addr, u32 size, u32 offset)
{
u32 cso_block;
u32 start_blk = 0;
u32 first_block_size = 0;
u32 last_block_size = 0;
u32 cso_read_offset, cso_read_size;
if(offset > g_CISO_hdr.total_bytes) {
// return if the offset goes beyond the iso size
return 0;
} else if(offset + size > g_CISO_hdr.total_bytes) {
// adjust size if it tries to read beyond the game data
size = g_CISO_hdr.total_bytes - offset;
}
if ((start_blk = refresh_cso_index(size, offset)) < 0) {
//FIXME: fallback to slower read, try to get a bigger block instead
// a game shouldn't request more than 8MiB in a single read so this
// isn't executed in normal cases
printk("Index for read of size %i is greater that allowed maximum\n", size);
return read_cso_data(addr, size, offset);
}
// check if the first read is in the middle of a compressed block or if there is only one block
if(!IS_DIVISIBLE(offset, ISO_SECTOR_SIZE) || size <= ISO_SECTOR_SIZE) {
// calculate the offset and size of the compressed data
cso_read_offset = GET_CSO_OFFSET(start_blk);
cso_read_size = GET_CSO_OFFSET(start_blk + 1) - cso_read_offset;
// READ #2 (only if the first block is a partial one)
read_raw_data(g_ciso_block_buf, cso_read_size, cso_read_offset);
u32 offset_shift = REMAINDER(offset, ISO_SECTOR_SIZE);
// calculate the real size needed from the decompressed block
if(offset_shift + size <= ISO_SECTOR_SIZE) {
// if the size + offset shift is less than the sector size then
// use the value directly for the first block size
first_block_size = size;
} else {
// else use the remainder
first_block_size = ISO_SECTOR_SIZE - offset_shift;
}
// decompress (if required)
if(decompress_block(addr, g_ciso_block_buf, first_block_size, start_blk, offset_shift) < 0) {
return -2;
}
// update size
size -= first_block_size;
// only one block to read, return early
if(size == 0) {
return first_block_size;
}
// update offset and addr
offset += first_block_size;
addr += first_block_size;
start_blk++;
}
{
// calculate the last block (or the remaining one)
cso_block = size / 2048 + start_blk;
// don't go over the next block if the read size occupies all of it
if(IS_DIVISIBLE(size, ISO_SECTOR_SIZE)) {
cso_block--;
}
cso_read_offset = GET_CSO_OFFSET(cso_block);
// get the compressed block size
cso_read_size = GET_CSO_OFFSET(cso_block + 1) - cso_read_offset;
// READ #3 (only if the last block is a partial one)
read_raw_data(g_ciso_block_buf, cso_read_size, cso_read_offset);
// calculate the partial decompressed block size
last_block_size = size % 2048;
// update size
size -= last_block_size;
// calculate the offset to place the last decompressed block
void *last_offset = addr + ((size / ISO_SECTOR_SIZE) * ISO_SECTOR_SIZE);
if(decompress_block(last_offset, g_ciso_block_buf, last_block_size, cso_block, 0) < 0) {
return -3;
}
// no more blocks
if(size == 0) {
return first_block_size +last_block_size;
}
}
// calculate the needed blocks
if(IS_DIVISIBLE(size, 2048)) {
cso_block = size / 2048;
} else {
cso_block = size / 2048 + 1;
}
cso_read_offset = GET_CSO_OFFSET(start_blk);
cso_read_size = GET_CSO_OFFSET(start_blk + cso_block) - cso_read_offset;
// place the compressed blocks at the end of the provided buffer
// so it can be reused in the decompression without overlap
u32 shifted_offset = cso_block * 2048 - cso_read_size;
// READ #4 (main section of compressed blocks)
read_raw_data(addr + shifted_offset, cso_read_size, cso_read_offset);
int i;
u32 read_size = 0;
// process every compressed block
for(i = 0; i < cso_block ; i++) {
// shift the source with the size of the last read
void *src = addr + shifted_offset + read_size;
// shift the destination, block by block
void *dst = addr + i * ISO_SECTOR_SIZE;
// calculate a size in case last block is a partial one and its
// size is less that the sector size
int dec_size = size < ISO_SECTOR_SIZE ? size : ISO_SECTOR_SIZE;
if(decompress_block(dst, src, dec_size, i + start_blk, 0) < 0) {
return -4;
}
cso_read_offset = GET_CSO_OFFSET(start_blk + i);
u32 decompressed_size = GET_CSO_OFFSET(start_blk + i + 1) - cso_read_offset;
read_size += decompressed_size;
}
return size + first_block_size + last_block_size;
}
// 0x00000C7C
int iso_read_old(u8 *address, u32 size, u32 offset)
{
if(g_is_ciso != 0) {
return read_cso_data(address, size, offset);
}
return read_raw_data(address, size, offset);
}
// 0x00000C7C
int iso_read(u8 *address, u32 size, u32 offset)
{
if(g_is_ciso != 0) {
return read_cso_data_ng(address, size, offset);
}
return read_raw_data(address, size, offset);
}
// TESTING CODE
void calculate_md5(unsigned char *out, unsigned char *in, int size)
{
MD5_CTX mdContext;
MD5_Init(&mdContext);
MD5_Update(&mdContext, in, size);
MD5_Final(out, &mdContext);
}
int test_integrity(const char *offsetFile, unsigned char *buffer)
{
FILE *fd = fopen(offsetFile, "r");
if(fd == NULL)
return 1;
ssize_t read;
size_t len = 0;
char *line = NULL;
int lines = 0;
while ((read = getline(&line, &len, fd)) != -1)
{
if(line[0] == '#')
continue;
int offset;
int read_size;
sscanf(line, "offset: %i, size: %i\n", &offset, &read_size);
memset(buffer, 0xCC, read_size);
unsigned char old_digest[MD5_DIGEST_LENGTH];
unsigned char new_digest[MD5_DIGEST_LENGTH];
int read_buffer;
if((read_buffer = iso_read_old(buffer, read_size, offset)) < 0)
{
printf("Failed to read game (old method), return value: %i\n", read_buffer);
printf("Size %i, offset %i\n", read_size, offset);
return 1;
}
calculate_md5((unsigned char *)&old_digest, buffer, read_size);
if((read_buffer = iso_read(buffer, read_size, offset)) < 0)
{
printf("Failed to read game (new method), return value: %i\n", read_buffer);
printf("Size %i, offset %i\n", read_size, offset);
return 1;
}
calculate_md5((unsigned char *)&new_digest, buffer, read_size);
if (memcmp(old_digest, new_digest, MD5_DIGEST_LENGTH) != 0) {
printf("MD5 check failed\n");
printf("Size %i, offset %i\n", read_size, offset);
return 1;
}
lines++;
}
free(line);
fclose(fd);
printf("Total reads: %i\n", lines);
return 0;
}
typedef int(*read_func)(u8 *address, u32 size, u32 offset);
int process_loops(FILE *fd, unsigned char *buffer, int loops, read_func func)
{
ssize_t read_size;
char *line = NULL;
size_t len = 0;
for(int i = 0; i < loops; i++)
{
// reset cso cache
g_CISO_cur_idx = -1;
while ((read_size = getline(&line, &len, fd)) != -1)
{
if(line[0] == '#')
continue;
int offset;
int read_size;
sscanf(line, "offset: %i, size: %i\n", &offset, &read_size);
int read_buffer;
if((read_buffer = func(buffer, read_size, offset)) < 0)
{
printf("Failed to read game (old method), return value: %i\n", read_buffer);
free(line);
return 1;
}
}
free(line);
line = NULL;
// restart offset file
fseek(fd, 0, SEEK_SET);
}
return 0;
}
int test_speed(const char *offsetFile, unsigned char *buffer, int loops)
{
FILE *fd = fopen(offsetFile, "r");
if(fd == NULL)
return 1;
posix_fadvise(fileno(fd), 0, 0, POSIX_FADV_DONTNEED);
clock_t begin, end;
double time_spent;
// start timing old read
begin = clock();
process_loops(fd, buffer, loops, iso_read_old);
end = clock();
time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
printk("time spent (old method): %f\n", time_spent);
// start timing new read
begin = clock();
process_loops(fd, buffer, loops, iso_read);
end = clock();
time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
printk("time spent (new method): %f\n", time_spent);
fclose(fd);
return 0;
}
int main(int argc, char **argv) {
if (argc < 3) {
return 1;
}
g_iso_fn = argv[1];
printk("Opening %s\n", g_iso_fn);
if(iso_open() == 0)
{
posix_fadvise(g_iso_fd, 0, 0, POSIX_FADV_DONTNEED);
unsigned char *buffer = (unsigned char *)malloc(1024*1024); // 1MiB
printf("Integrity test...\n");
if(test_integrity(argv[2], buffer) == 0)
{
printf("Integrity test OK\n");
printf("Starting speed test, %i loops\n", 500);
test_speed(argv[2], buffer, 500);
}
free(buffer);
close(g_iso_fd);
free(g_cso_idx_cache_orig);
free(g_ciso_block_buf_orig);
free(g_ciso_dec_buf_orig);
}
return 0;
}
offset: 1048576, size: 1
offset: 1048577, size: 1
offset: 1048578, size: 1
offset: 1048579, size: 1
offset: 1048580, size: 1
offset: 1048581, size: 1
offset: 1048582, size: 1
offset: 1048583, size: 1
offset: 1048584, size: 1
offset: 1048585, size: 1
offset: 1048586, size: 1
offset: 1048587, size: 1
offset: 1048588, size: 1
offset: 1048589, size: 1
offset: 1048590, size: 1
offset: 1048591, size: 1
offset: 1048592, size: 1
offset: 1048593, size: 1
offset: 1048594, size: 1
offset: 1048595, size: 1
offset: 1048596, size: 1
offset: 1048597, size: 1
offset: 1048598, size: 1
offset: 1048599, size: 1
offset: 1048600, size: 1
offset: 1048601, size: 1
offset: 1048602, size: 1
offset: 1048603, size: 1
offset: 1048604, size: 1
offset: 1048606, size: 2
offset: 1048608, size: 2
offset: 1048610, size: 2
offset: 1048612, size: 2
offset: 1048614, size: 2
offset: 1048616, size: 2
offset: 1048618, size: 2
offset: 1048620, size: 2
offset: 1048622, size: 2
offset: 1048624, size: 2
offset: 1048626, size: 2
offset: 1048628, size: 2
offset: 1048630, size: 2
offset: 1048632, size: 2
offset: 1048634, size: 2
offset: 1048636, size: 2
offset: 1048638, size: 2
offset: 1048640, size: 2
offset: 1048642, size: 2
offset: 1048644, size: 2
offset: 1048646, size: 2
offset: 1048648, size: 2
offset: 1048650, size: 2
offset: 1048652, size: 2
offset: 1048654, size: 2
offset: 1048656, size: 2
offset: 1048658, size: 2
offset: 1048660, size: 2
offset: 1048664, size: 4
offset: 1048668, size: 4
offset: 1048672, size: 4
offset: 1048676, size: 4
offset: 1048680, size: 4
offset: 1048684, size: 4
offset: 1048688, size: 4
offset: 1048692, size: 4
offset: 1048696, size: 4
offset: 1048700, size: 4
offset: 1048704, size: 4
offset: 1048708, size: 4
offset: 1048712, size: 4
offset: 1048716, size: 4
offset: 1048720, size: 4
offset: 1048724, size: 4
offset: 1048728, size: 4
offset: 1048732, size: 4
offset: 1048736, size: 4
offset: 1048740, size: 4
offset: 1048744, size: 4
offset: 1048748, size: 4
offset: 1048752, size: 4
offset: 1048756, size: 4
offset: 1048760, size: 4
offset: 1048764, size: 4
offset: 1048768, size: 4
offset: 1048772, size: 4
offset: 1048780, size: 8
offset: 1048788, size: 8
offset: 1048796, size: 8
offset: 1048804, size: 8
offset: 1048812, size: 8
offset: 1048820, size: 8
offset: 1048828, size: 8
offset: 1048836, size: 8
offset: 1048844, size: 8
offset: 1048852, size: 8
offset: 1048860, size: 8
offset: 1048868, size: 8
offset: 1048876, size: 8
offset: 1048884, size: 8
offset: 1048892, size: 8
offset: 1048900, size: 8
offset: 1048908, size: 8
offset: 1048916, size: 8
offset: 1048924, size: 8
offset: 1048932, size: 8
offset: 1048940, size: 8
offset: 1048948, size: 8
offset: 1048956, size: 8
offset: 1048964, size: 8
offset: 1048972, size: 8
offset: 1048980, size: 8
offset: 1048988, size: 8
offset: 1048996, size: 8
offset: 1049012, size: 16
offset: 1049028, size: 16
offset: 1049044, size: 16
offset: 1049060, size: 16
offset: 1049076, size: 16
offset: 1049092, size: 16
offset: 1049108, size: 16
offset: 1049124, size: 16
offset: 1049140, size: 16
offset: 1049156, size: 16
offset: 1049172, size: 16
offset: 1049188, size: 16
offset: 1049204, size: 16
offset: 1049220, size: 16
offset: 1049236, size: 16
offset: 1049252, size: 16
offset: 1049268, size: 16
offset: 1049284, size: 16
offset: 1049300, size: 16
offset: 1049316, size: 16
offset: 1049332, size: 16
offset: 1049348, size: 16
offset: 1049364, size: 16
offset: 1049380, size: 16
offset: 1049396, size: 16
offset: 1049412, size: 16
offset: 1049428, size: 16
offset: 1049444, size: 16
offset: 1049476, size: 32
offset: 1049508, size: 32
offset: 1049540, size: 32
offset: 1049572, size: 32
offset: 1049604, size: 32
offset: 1049636, size: 32
offset: 1049668, size: 32
offset: 1049700, size: 32
offset: 1049732, size: 32
offset: 1049764, size: 32
offset: 1049796, size: 32
offset: 1049828, size: 32
offset: 1049860, size: 32
offset: 1049892, size: 32
offset: 1049924, size: 32
offset: 1049956, size: 32
offset: 1049988, size: 32
offset: 1050020, size: 32
offset: 1050052, size: 32
offset: 1050084, size: 32
offset: 1050116, size: 32
offset: 1050148, size: 32
offset: 1050180, size: 32
offset: 1050212, size: 32
offset: 1050244, size: 32
offset: 1050276, size: 32
offset: 1050308, size: 32
offset: 1050340, size: 32
offset: 1050404, size: 64
offset: 1050468, size: 64
offset: 1050532, size: 64
offset: 1050596, size: 64
offset: 1050660, size: 64
offset: 1050724, size: 64
offset: 1050788, size: 64
offset: 1050852, size: 64
offset: 1050916, size: 64
offset: 1050980, size: 64
offset: 1051044, size: 64
offset: 1051108, size: 64
offset: 1051172, size: 64
offset: 1051236, size: 64
offset: 1051300, size: 64
offset: 1051364, size: 64
offset: 1051428, size: 64
offset: 1051492, size: 64
offset: 1051556, size: 64
offset: 1051620, size: 64
offset: 1051684, size: 64
offset: 1051748, size: 64
offset: 1051812, size: 64
offset: 1051876, size: 64
offset: 1051940, size: 64
offset: 1052004, size: 64
offset: 1052068, size: 64
offset: 1052132, size: 64
offset: 1052260, size: 128
offset: 1052388, size: 128
offset: 1052516, size: 128
offset: 1052644, size: 128
offset: 1052772, size: 128
offset: 1052900, size: 128
offset: 1053028, size: 128
offset: 1053156, size: 128
offset: 1053284, size: 128
offset: 1053412, size: 128
offset: 1053540, size: 128
offset: 1053668, size: 128
offset: 1053796, size: 128
offset: 1053924, size: 128
offset: 1054052, size: 128
offset: 1054180, size: 128
offset: 1054308, size: 128
offset: 1054436, size: 128
offset: 1054564, size: 128
offset: 1054692, size: 128
offset: 1054820, size: 128
offset: 1054948, size: 128
offset: 1055076, size: 128
offset: 1055204, size: 128
offset: 1055332, size: 128
offset: 1055460, size: 128
offset: 1055588, size: 128
offset: 1055716, size: 128
offset: 1055972, size: 256
offset: 1056228, size: 256
offset: 1056484, size: 256
offset: 1056740, size: 256
offset: 1056996, size: 256
offset: 1057252, size: 256
offset: 1057508, size: 256
offset: 1057764, size: 256
offset: 1058020, size: 256
offset: 1058276, size: 256
offset: 1058532, size: 256
offset: 1058788, size: 256
offset: 1059044, size: 256
offset: 1059300, size: 256
offset: 1059556, size: 256
offset: 1059812, size: 256
offset: 1060068, size: 256
offset: 1060324, size: 256
offset: 1060580, size: 256
offset: 1060836, size: 256
offset: 1061092, size: 256
offset: 1061348, size: 256
offset: 1061604, size: 256
offset: 1061860, size: 256
offset: 1062116, size: 256
offset: 1062372, size: 256
offset: 1062628, size: 256
offset: 1062884, size: 256
offset: 1063396, size: 512
offset: 1063908, size: 512
offset: 1064420, size: 512
offset: 1064932, size: 512
offset: 1065444, size: 512
offset: 1065956, size: 512
offset: 1066468, size: 512
offset: 1066980, size: 512
offset: 1067492, size: 512
offset: 1068004, size: 512
offset: 1068516, size: 512
offset: 1069028, size: 512
offset: 1069540, size: 512
offset: 1070052, size: 512
offset: 1070564, size: 512
offset: 1071076, size: 512
offset: 1071588, size: 512
offset: 1072100, size: 512
offset: 1072612, size: 512
offset: 1073124, size: 512
offset: 1073636, size: 512
offset: 1074148, size: 512
offset: 1074660, size: 512
offset: 1075172, size: 512
offset: 1075684, size: 512
offset: 1076196, size: 512
offset: 1076708, size: 512
offset: 1077220, size: 512
offset: 1078244, size: 1024
offset: 1079268, size: 1024
offset: 1080292, size: 1024
offset: 1081316, size: 1024
offset: 1082340, size: 1024
offset: 1083364, size: 1024
offset: 1084388, size: 1024
offset: 1085412, size: 1024
offset: 1086436, size: 1024
offset: 1087460, size: 1024
offset: 1088484, size: 1024
offset: 1089508, size: 1024
offset: 1090532, size: 1024
offset: 1091556, size: 1024
offset: 1092580, size: 1024
offset: 1093604, size: 1024
offset: 1094628, size: 1024
offset: 1095652, size: 1024
offset: 1096676, size: 1024
offset: 1097700, size: 1024
offset: 1098724, size: 1024
offset: 1099748, size: 1024
offset: 1100772, size: 1024
offset: 1101796, size: 1024
offset: 1102820, size: 1024
offset: 1103844, size: 1024
offset: 1104868, size: 1024
offset: 1105892, size: 1024
offset: 1107940, size: 2048
offset: 1109988, size: 2048
offset: 1112036, size: 2048
offset: 1114084, size: 2048
offset: 1116132, size: 2048
offset: 1118180, size: 2048
offset: 1120228, size: 2048
offset: 1122276, size: 2048
offset: 1124324, size: 2048
offset: 1126372, size: 2048
offset: 1128420, size: 2048
offset: 1130468, size: 2048
offset: 1132516, size: 2048
offset: 1134564, size: 2048
offset: 1136612, size: 2048
offset: 1138660, size: 2048
offset: 1140708, size: 2048
offset: 1142756, size: 2048
offset: 1144804, size: 2048
offset: 1146852, size: 2048
offset: 1148900, size: 2048
offset: 1150948, size: 2048
offset: 1152996, size: 2048
offset: 1155044, size: 2048
offset: 1157092, size: 2048
offset: 1159140, size: 2048
offset: 1161188, size: 2048
offset: 1163236, size: 2048
offset: 1167332, size: 4096
offset: 1171428, size: 4096
offset: 1175524, size: 4096
offset: 1179620, size: 4096
offset: 1183716, size: 4096
offset: 1187812, size: 4096
offset: 1191908, size: 4096
offset: 1196004, size: 4096
offset: 1200100, size: 4096
offset: 1204196, size: 4096
offset: 1208292, size: 4096
offset: 1212388, size: 4096
offset: 1216484, size: 4096
offset: 1220580, size: 4096
offset: 1224676, size: 4096
offset: 1228772, size: 4096
offset: 1232868, size: 4096
offset: 1236964, size: 4096
offset: 1241060, size: 4096
offset: 1245156, size: 4096
offset: 1249252, size: 4096
offset: 1253348, size: 4096
offset: 1257444, size: 4096
offset: 1261540, size: 4096
offset: 1265636, size: 4096
offset: 1269732, size: 4096
offset: 1273828, size: 4096
offset: 1277924, size: 4096
offset: 1286116, size: 8192
offset: 1294308, size: 8192
offset: 1302500, size: 8192
offset: 1310692, size: 8192
offset: 1318884, size: 8192
offset: 1327076, size: 8192
offset: 1335268, size: 8192
offset: 1343460, size: 8192
offset: 1351652, size: 8192
offset: 1359844, size: 8192
offset: 1368036, size: 8192
offset: 1376228, size: 8192
offset: 1384420, size: 8192
offset: 1392612, size: 8192
offset: 1400804, size: 8192
offset: 1408996, size: 8192
offset: 1417188, size: 8192
offset: 1425380, size: 8192
offset: 1433572, size: 8192
offset: 1441764, size: 8192
offset: 1449956, size: 8192
offset: 1458148, size: 8192
offset: 1466340, size: 8192
offset: 1474532, size: 8192
offset: 1482724, size: 8192
offset: 1490916, size: 8192
offset: 1499108, size: 8192
offset: 1507300, size: 8192
offset: 1523684, size: 16384
offset: 1540068, size: 16384
offset: 1556452, size: 16384
offset: 1572836, size: 16384
offset: 1589220, size: 16384
offset: 1605604, size: 16384
offset: 1621988, size: 16384
offset: 1638372, size: 16384
offset: 1654756, size: 16384
offset: 1671140, size: 16384
offset: 1687524, size: 16384
offset: 1703908, size: 16384
offset: 1720292, size: 16384
offset: 1736676, size: 16384
offset: 1753060, size: 16384
offset: 1769444, size: 16384
offset: 1785828, size: 16384
offset: 1802212, size: 16384
offset: 1818596, size: 16384
offset: 1834980, size: 16384
offset: 1851364, size: 16384
offset: 1867748, size: 16384
offset: 1884132, size: 16384
offset: 1900516, size: 16384
offset: 1916900, size: 16384
offset: 1933284, size: 16384
offset: 1949668, size: 16384
offset: 1966052, size: 16384
offset: 1998820, size: 32768
offset: 2031588, size: 32768
offset: 2064356, size: 32768
offset: 2097124, size: 32768
offset: 2129892, size: 32768
offset: 2162660, size: 32768
offset: 2195428, size: 32768
offset: 2228196, size: 32768
offset: 2260964, size: 32768
offset: 2293732, size: 32768
offset: 2326500, size: 32768
offset: 2359268, size: 32768
offset: 2392036, size: 32768
offset: 2424804, size: 32768
offset: 2457572, size: 32768
offset: 2490340, size: 32768
offset: 2523108, size: 32768
offset: 2555876, size: 32768
offset: 2588644, size: 32768
offset: 2621412, size: 32768
offset: 2654180, size: 32768
offset: 2686948, size: 32768
offset: 2719716, size: 32768
offset: 2752484, size: 32768
offset: 2785252, size: 32768
offset: 2818020, size: 32768
offset: 2850788, size: 32768
offset: 2883556, size: 32768
offset: 2949092, size: 65536
offset: 3014628, size: 65536
offset: 3080164, size: 65536
offset: 3145700, size: 65536
offset: 3211236, size: 65536
offset: 3276772, size: 65536
offset: 3342308, size: 65536
offset: 3407844, size: 65536
offset: 3473380, size: 65536
offset: 3538916, size: 65536
offset: 3604452, size: 65536
offset: 3669988, size: 65536
offset: 3735524, size: 65536
offset: 3801060, size: 65536
offset: 3866596, size: 65536
offset: 3932132, size: 65536
offset: 3997668, size: 65536
offset: 4063204, size: 65536
offset: 4128740, size: 65536
offset: 4194276, size: 65536
offset: 4259812, size: 65536
offset: 4325348, size: 65536
offset: 4390884, size: 65536
offset: 4456420, size: 65536
offset: 4521956, size: 65536
offset: 4587492, size: 65536
offset: 4653028, size: 65536
offset: 4718564, size: 65536
offset: 4849636, size: 131072
offset: 4980708, size: 131072
offset: 5111780, size: 131072
offset: 5242852, size: 131072
offset: 5373924, size: 131072
offset: 5504996, size: 131072
offset: 5636068, size: 131072
offset: 5767140, size: 131072
offset: 5898212, size: 131072
offset: 6029284, size: 131072
offset: 6160356, size: 131072
offset: 6291428, size: 131072
offset: 6422500, size: 131072
offset: 6553572, size: 131072
offset: 6684644, size: 131072
offset: 6815716, size: 131072
offset: 6946788, size: 131072
offset: 7077860, size: 131072
offset: 7208932, size: 131072
offset: 7340004, size: 131072
offset: 7471076, size: 131072
offset: 7602148, size: 131072
offset: 7733220, size: 131072
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment