Add Wii U RPX/RPL loader src. Some bug fixes.

This commit is contained in:
aerosoul94 2017-04-09 00:35:32 -05:00
parent 991de71c14
commit 592b0484b9
16 changed files with 1227 additions and 18 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/src/ps3/build/
/src/vita/build/
/src/wiiu/build/

View file

@ -2,6 +2,7 @@
A collection of user mode ELF loaders for the following game consoles:
* PS3
* PS Vita
* Wii U
## Installation
Copy loader plugins to IDA loaders directory.
@ -14,15 +15,17 @@ Copy loader plugins to IDA loaders directory.
### Generate Projects With CMake
The IDA cmake module included will expect to find the IDA SDK in an `IDA_SDK_DIR` or `IDA_SDK` environment variable.
If you would like to generate 64-bit EA targeted loaders, you need to add `-DIDA_64_BIT_EA_T=YES` to cmake command line.
If you would like to generate 64-bit EA targeted loaders, you need to add `-D IDA_64_BIT_EA_T=YES` to cmake command line.
Navigate to the directory of the loader you would like to build in 'src/', then run the following command
`mkdir build && cd build && cmake ../`
This should create a build directory with your generated project files.
### Building
Optionally, you can also build using cmake with the following command
`cmake --build ./`
## Notes

View file

@ -104,7 +104,7 @@ public:
void setData(const char *data, size_t length)
{
m_data.assign(data, length);
m_data.assign(data, data + length);
}
void setReader(linput_t *li)
@ -117,7 +117,7 @@ template <class Elf>
class Section
: public Elf::Shdr {
linput_t *m_reader;
std::vector<char> m_data; // TODO: switch to std::vector
std::vector<char> m_data;
public:
Section() {}
@ -137,7 +137,7 @@ public:
void setData(const char *data, size_t length)
{
m_data.assign(data, length);
m_data.assign(data, data + length);
}
void setReader(linput_t *li)
@ -181,16 +181,16 @@ public:
}
void read() {
readHeader();
readSegments();
readSections();
this->readHeader();
this->readSegments();
this->readSections();
}
void print() {
printHeader();
printSegment();
printSections();
printSymbols();
this->printHeader();
this->printSegment();
this->printSections();
this->printSymbols();
}
bool verifyHeader() {
@ -207,7 +207,7 @@ public:
}
linput_t *getReader() const
{ return this->m_reader; }
{ return m_reader; }
uchar osabi() const
{ return m_header.e_ident[EI_OSABI]; }
@ -304,7 +304,7 @@ private:
swap(m_header.e_shstrndx);
}
//printHeader();
//this->printHeader();
}
void readSegments() {
@ -331,7 +331,7 @@ private:
segment.setReader(m_reader);
}
//printSegments();
//this->printSegments();
}
}
@ -372,7 +372,7 @@ private:
m_sections[m_header.e_shstrndx].sh_type == SHT_STRTAB)
m_sectionStringTable = &m_sections[m_header.e_shstrndx];
//printSections();
//this->printSections();
}
}

24
src/ps3/ReadMe.md Normal file
View file

@ -0,0 +1,24 @@
# ps3ldr
An IDA Pro loader for PS3 Cell Lv-2 OS.
## Features
* Loads symbols
* Processes and labels exports and imports
* PRX relocation
* Find and set TOC address
## Usage
### NID Database
This loader expects an NID xml database named `ps3.xml` to be present within IDA's loaders directory. For convenience, it is the same xml database format that was used in xorloser's loader.
Example:
<?xml version="1.0"?>
<IdaInfoDatabase>
<Group name="moduleName">
<Entry id="0x1529E506" name="cellAdecDecodeAu"/>
</Group>
</IdaInfoDatabase>
### PRX Relocation
Relocation of PRX's is possible by checking the *Manual Load* checkbox in IDA's *Load New File* dialog, then before loading the loader will ask for a relocation base address.

View file

@ -20,6 +20,7 @@ cell_loader::cell_loader(elf_reader<elf64> *elf,
inf.demnames |= DEMNAM_GCC3; // assume gcc3 names
inf.af |= AF_PROCPTR; // Create function if data xref data->code32 exists
inf.filetype = f_ELF;
char databasePath[QMAXPATH];

View file

@ -1,4 +1,3 @@
#pragma once
#include "elf_reader.h"

View file

@ -1,4 +1,3 @@
#include "../elf_common/elf_reader.h"
#include "cell_loader.h"
#include "sce.h"

17
src/vita/ReadMe.md Normal file
View file

@ -0,0 +1,17 @@
# vitaldr
An IDA Pro loader for Playstation Vita OS.
## Features
* Loads symbols
* Supports kernel and user modules.
* Processes and labels exports and imports
## Usage
### NID Database
This loader expects an NID database named `vita.txt` to be present within IDA's loader directory. The format is simple:
0x34EFD876 sceIoWrite
0xC70B8886 sceIoClose
## Todo
* Although it does process all relocation formats (form 0 - 9), module relocation still needs to be completed.

View file

@ -1,4 +1,3 @@
#include "../elf_common/elf_reader.h"
#include "psp2_loader.h"
#include "sce.h"

28
src/wiiu/CMakeLists.txt Normal file
View file

@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 2.8)
project(wiiuldr)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/../../cmake)
set(ELF_COMMON_PATH ${CMAKE_SOURCE_DIR}/../elf_common)
set(THIRD_PARTY_PATH ${CMAKE_SOURCE_DIR}../../third_party)
set(SOURCES
${ELF_COMMON_PATH}/elf_reader.h
${ELF_COMMON_PATH}/elf.h
tinfl.c
cafe_loader.cpp
cafe_loader.h
wiiu.cpp
cafe.h
)
find_package(IDA)
include_directories(${IDA_INCLUDE_DIR})
include_directories(${IDA_SDK_PATH}/ldr)
include_directories(${ELF_COMMON_PATH})
add_definitions(${IDA_DEFINITIONS})
add_library(wiiuldr SHARED ${SOURCES})
target_link_libraries(wiiuldr ${IDA_LIBRARIES})
set_target_properties(wiiuldr PROPERTIES OUTPUT_NAME "wiiu" PREFIX "" SUFFIX "${IDA_PLUGIN_EXT}")

11
src/wiiu/ReadMe.md Normal file
View file

@ -0,0 +1,11 @@
# wiiuldr
An IDA Pro loader for the Wii U Cafe OS.
## Features
* Compressed section handling
* Creates extern segment for imported functions
* Symbol table loading
* Adds imports and exports
## Todo
* Support RPL relocation

84
src/wiiu/cafe.h Normal file
View file

@ -0,0 +1,84 @@
#ifndef CAFE_H
#define CAFE_H
#define ELF_IDENT_OS_CAFE 0xCA
#define ELF_IDENT_ABI_CAFE_RPL 0xFE
#define ELF_FILETYPE_CAFE_RPL 0xfe01
#define ELF_SECTIONFLAGEX_CAFE_RPL_COMPZ 0x08000000
#define ELF_SECTIONTYPE_CAFE_RPL_EXPORTS 0x80000001
#define ELF_SECTIONTYPE_CAFE_RPL_IMPORTS 0x80000002
#define ELF_SECTIONTYPE_CAFE_RPL_CRCS 0x80000003
#define ELF_SECTIONTYPE_CAFE_RPL_FILEINFO 0x80000004
typedef struct _CAFE_RPL_FILE_INFO_3_0
{
Elf32_Word mVersion; /* CAFE_RPL_FILE_INFO_VERSION */
Elf32_Word mRegBytes_Text;
Elf32_Word mRegBytes_TextAlign;
Elf32_Word mRegBytes_Data;
Elf32_Word mRegBytes_DataAlign;
Elf32_Word mRegBytes_Read;
Elf32_Word mRegBytes_ReadAlign;
Elf32_Word mRegBytes_Temp;
Elf32_Word mTrampAdj;
Elf32_Word mSDABase;
Elf32_Word mSDA2Base;
Elf32_Word mSizeCoreStacks;
Elf32_Word mSrcFileNameOffset; /* from start of FILE_INFO */
Elf32_Word mReserved[3];
} CAFE_RPL_FILE_INFO_3_0;
typedef struct _CAFE_RPL_FILE_INFO_4_1
{
Elf32_Word mVersion;
Elf32_Word mRegBytes_Text;
Elf32_Word mRegBytes_TexAlign;
Elf32_Word mRegBytes_Data;
Elf32_Word mRegBytes_DataAlign;
Elf32_Word mRegBytes_LoaderInfo;
Elf32_Word mRegBytes_LoaderInfoAlign;
Elf32_Word mRegBytes_Temp;
Elf32_Word mTrampAdj;
Elf32_Word mSDABase;
Elf32_Word mSDA2Base;
Elf32_Word mSizeCoreStacks;
Elf32_Word mSrcFileNameOffset;
Elf32_Word mFlags;
Elf32_Word mSysHeapBytes;
Elf32_Word mTagsOffset;
} CAFE_RPL_FILE_INFO_4_1;
typedef struct _CAFE_RPL_FILE_INFO_4_2
{
Elf32_Word mVersion;
Elf32_Word mRegBytes_Text;
Elf32_Word mRegBytes_TextAlign;
Elf32_Word mRegBytes_Data;
Elf32_Word mRegBytes_DataAlign;
Elf32_Word mRegBytes_LoaderInfo;
Elf32_Word mRegBytes_LoaderInfoAlign;
Elf32_Word mRegBytes_Temp;
Elf32_Word mTrampAdj;
Elf32_Word mSDABase;
Elf32_Word mSDA2Base;
Elf32_Word mSizeCoreStacks;
Elf32_Word mSrcFileNameOffset;
Elf32_Word mFlags;
Elf32_Word mSysHeapBytes;
Elf32_Word mTagsOffset;
Elf32_Word mRPLMinSDKVersion;
Elf32_Word mCompressionLevel;
Elf32_Word mTrampAddition;
Elf32_Word mFileInfoPad;
Elf32_Word mSDKVersion;
Elf32_Word mSDKRevision;
Elf32_Word mTLSModuleIndex;
Elf32_Half mTLSAlignShift;
Elf32_Half mRuntimeFileInfoSize;
} CAFE_RPL_FILE_INFO_4_2;
#endif /* CAFE_H */

355
src/wiiu/cafe_loader.cpp Normal file
View file

@ -0,0 +1,355 @@
#include "cafe_loader.h"
#include "cafe.h"
#include "tinfl.c"
cafe_loader::cafe_loader(elf_reader<elf32> *elf)
: m_elf(elf)
{
m_externStart = 0xffffffff;
m_externEnd = 0;
}
void cafe_loader::apply() {
applySegments();
swapSymbols();
applyRelocations();
processImports();
processExports();
applySymbols();
}
void cafe_loader::applySegments() {
auto &sections = m_elf->getSections();
// decompress all sections
// TODO: only decompress once and when needed
for (auto &section : sections) {
if (section.sh_flags & ELF_SECTIONFLAGEX_CAFE_RPL_COMPZ) {
const char *data = section.data();
uint32 deflatedLen = *(uint32 *)data;
swap(deflatedLen);
unsigned char *deflatedData = new unsigned char[deflatedLen];
deflatedLen = tinfl_decompress_mem_to_mem(
deflatedData,
deflatedLen,
data + 4,
section.sh_size - 4,
TINFL_FLAG_PARSE_ZLIB_HEADER
);
section.setData((const char *)deflatedData, deflatedLen);
}
}
const char *stringTable = m_elf->getSectionStringTable()->data();
size_t index = 0;
for (auto section : m_elf->getSections()) {
if (section.sh_flags & SHF_ALLOC) {
if (section.sh_type == SHT_NULL)
continue;
uchar perm = SEGPERM_READ;
char *sclass;
if (section.sh_flags & SHF_WRITE)
perm |= SEGPERM_WRITE;
if (section.sh_flags & SHF_EXECINSTR)
perm |= SEGPERM_EXEC;
if (section.sh_flags & SHF_EXECINSTR &&
section.sh_type != ELF_SECTIONTYPE_CAFE_RPL_IMPORTS &&
section.sh_type != ELF_SECTIONTYPE_CAFE_RPL_EXPORTS)
sclass = CLASS_CODE;
else if (section.sh_type == SHT_NOBITS)
sclass = CLASS_BSS;
else
sclass = CLASS_DATA;
const char *data = section.data();
const char *name = NULL;
if (section.sh_name != NULL)
name = &stringTable[section.sh_name];
applySegment(index,
data,
section.sh_addr,
section.getSize(),
name,
sclass,
perm,
m_elf->getAlignment(section.sh_addralign),
section.sh_type == SHT_NOBITS ? false : true);
++index;
}
}
}
void cafe_loader::applySegment(uint32 sel,
const char *data,
uint32 addr,
uint32 size,
const char *name,
const char *sclass,
uchar perm,
uchar align,
bool load) {
segment_t seg;
seg.startEA = addr;
seg.endEA = addr + size;
seg.color = DEFCOLOR;
seg.sel = sel;
seg.bitness = 1;
seg.orgbase = sel;
seg.comb = scPub;
seg.perm = perm;
seg.flags = SFL_LOADER;
seg.align = align;
set_selector(sel, 0);
if (name == NULL)
name = "";
add_segm_ex(&seg, name, sclass, NULL);
if (load == true)
mem2base(data, addr, addr + size, BADADDR);
}
void cafe_loader::applyRelocations() {
auto &sections = m_elf->getSections();
for (auto &section : sections) {
if (section.sh_type == SHT_RELA) {
auto symsec = m_elf->getSymbolsSection();
auto symbols = m_elf->getSymbols();
auto stringTable = m_elf->getSections()[symsec->sh_link].data();
auto nrela = section.getSize() / sizeof(Elf32_Rela);
auto relocations = reinterpret_cast<Elf32_Rela *>(section.data());
for (size_t i = 0; i < nrela; ++i) {
auto &rela = relocations[i];
swap(rela.r_info);
swap(rela.r_offset);
swap(rela.r_addend);
uint32 type = ELF32_R_TYPE(rela.r_info);
uint32 sym = ELF32_R_SYM (rela.r_info);
// r_offset = address of relocation
// r_addend = offset past symbol
// r_sym = address used as value to patch
if (type == R_PPC_NONE)
continue;
uint32 addr = symbols[sym].st_value + rela.r_addend;
switch (type) {
// TODO: support RPL relocation
// since RPL relocation is not yet supported we do
// not need to patch anything.
// as far as I've seen they're already set anyway
/* case R_PPC_ADDR32:
patch_long(rela.r_offset, addr);
break;
case R_PPC_ADDR16_LO:
patch_word(rela.r_offset, addr);
break;
case R_PPC_ADDR16_HI:
patch_word(rela.r_offset, addr >> 16);
break;
case R_PPC_ADDR16_HA:
patch_word(rela.r_offset, (addr + 0x8000) >> 16);
break; */
case R_PPC_REL24: {
if (symbols[sym].st_value & 0xc0000000 &&
ELF32_ST_TYPE(symbols[sym].st_info) == STT_FUNC) {
auto inst = get_original_long(rela.r_offset);
auto addr = rela.r_offset + (inst & 0x3fffffc);
if (m_externStart > addr)
m_externStart = addr;
if (m_externEnd < addr)
m_externEnd = addr + 8;
import temp = {
addr,
symbols[sym].st_value,
&stringTable[symbols[sym].st_name]
};
m_imports.push_back(temp);
}
}
break;
}
}
}
}
}
void cafe_loader::processImports() {
if (m_externStart != 0xffffffff && m_externEnd != 0) {
segment_t ext;
ext.startEA = m_externStart;
ext.endEA = m_externEnd;
ext.sel = 255;
ext.bitness = 1;
ext.color = DEFCOLOR;
ext.orgbase = 255;
ext.comb = scPub;
ext.perm = SEGPERM_READ | SEGPERM_EXEC;
ext.flags = SFL_LOADER;
ext.align = saRelQword;
set_selector(255, 0);
add_segm_ex(&ext, ".extern", "XTRN", NULL);
}
for (auto &import : m_imports) {
char name[256];
do_name_anyway(import.addr, import.name);
char lib[32];
get_segm_name(import.orig, lib, 32);
netnode impnode;
impnode.create();
if (demangle_name(name, 256, import.name, NULL))
impnode.supset(import.addr, name);
else
impnode.supset(import.addr, import.name);
import_module(lib + 9, NULL, impnode, NULL, "wiiu");
}
}
void cafe_loader::processExports() {
segment_t *seg;
uint32 start = 0;
uint32 numExports;
if ((seg = get_segm_by_name(".fexports")) != NULL) {
start = seg->startEA;
numExports = get_long(start);
for (int i = 0; i < numExports + 1; ++i) {
doDwrd((start + (i * 8)) + 0, 4);
doDwrd((start + (i * 8)) + 4, 4);
if (i == 0)
continue;
uint32 addr = get_long(start + (i * 8) + 0);
uint32 name = get_long(start + (i * 8) + 4);
auto_make_proc(addr);
char exp[256];
get_ascii_contents(start + name,
get_max_ascii_length(start + name, ASCSTR_C, true),
ASCSTR_C, exp, 256);
add_entry(addr, addr, exp, true);
}
}
if ((seg = get_segm_by_name(".dexports")) != NULL)
{
start = seg->startEA;
numExports = get_long(start);
for (int i = 0; i < numExports + 1; i++)
{
doDwrd((start + (i * 8)) + 0, 4);
doDwrd((start + (i * 8)) + 4, 4);
if (i == 0)
continue;
uint32 addr = get_long(start + (8 * i) + 0);
uint32 name = get_long(start + (8 * i) + 4);
char exp[256];
get_ascii_contents(start + name,
get_max_ascii_length(start + name, ASCSTR_C, true),
ASCSTR_C, exp, 256);
add_entry(addr, addr, exp, true);
}
}
}
void cafe_loader::swapSymbols() {
msg("Swapping symbols...\n");
// Since section based relocations depend on symbols
// we need to swap symbols before we get to relocations.
auto section = m_elf->getSymbolsSection();
auto symbols = m_elf->getSymbols();
for (size_t i = 0;
i < section->getSize() /
section->sh_entsize; ++i) {
auto symbol = &symbols[i];
swap(symbol->st_name);
swap(symbol->st_shndx);
swap(symbol->st_size);
swap(symbol->st_value);
}
}
void cafe_loader::applySymbols() {
msg("Applying symbols...");
auto section = m_elf->getSymbolsSection();
if (section == NULL)
return;
auto nsym = section->getSize() / section->sh_entsize;
auto symbols = m_elf->getSymbols();
const char *stringTable = m_elf->getSections()[section->sh_link].data();
for (size_t i = 0; i < nsym; ++i) {
auto &symbol = symbols[i];
uint32 type = ELF32_ST_TYPE(symbol.st_info),
bind = ELF32_ST_BIND(symbol.st_info);
uint32 value = symbol.st_value;
if (symbol.st_shndx > m_elf->getNumSections() ||
!(m_elf->getSections()[symbol.st_shndx].sh_flags & SHF_ALLOC))
continue;
if (symbol.st_shndx == SHN_ABS)
continue;
// TODO: these are the same for all ELF's, maybe move to ELF reader
switch (type) {
case STT_OBJECT:
do_name_anyway(value, &stringTable[symbol.st_name]);
break;
case STT_FILE:
describe(value, true, "Source File: %s", &stringTable[symbol.st_name]);
break;
case STT_FUNC:
do_name_anyway(value, &stringTable[symbol.st_name]);
auto_make_proc(value);
break;
}
}
}

45
src/wiiu/cafe_loader.h Normal file
View file

@ -0,0 +1,45 @@
#pragma once
#include "elf_reader.h"
#include "cafe.h"
class cafe_loader {
elf_reader<elf32> *m_elf;
uint32 m_relocAddr;
uint32 m_externStart;
uint32 m_externEnd;
struct import {
uint32 addr;
uint32 orig;
const char *name;
};
std::vector<import> m_imports;
public:
cafe_loader(elf_reader<elf32> *elf);
void apply();
private:
void applySegments();
void applySegment(uint32 sel,
const char *data,
uint32 addr,
uint32 size,
const char *name,
const char *sclass,
uchar perm,
uchar align,
bool load);
void applyRelocations();
void processImports();
void processExports();
void swapSymbols();
void applySymbols();
};

592
src/wiiu/tinfl.c Normal file
View file

@ -0,0 +1,592 @@
/* tinfl.c v1.11 - public domain inflate with zlib header parsing/adler32 checking (inflate-only subset of miniz.c)
See "unlicense" statement at the end of this file.
Rich Geldreich <richgel99@gmail.com>, last updated May 20, 2011
Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt
The entire decompressor coroutine is implemented in tinfl_decompress(). The other functions are optional high-level helpers.
*/
#ifndef TINFL_HEADER_INCLUDED
#define TINFL_HEADER_INCLUDED
#include <stdlib.h>
typedef unsigned char mz_uint8;
typedef signed short mz_int16;
typedef unsigned short mz_uint16;
typedef unsigned int mz_uint32;
typedef unsigned int mz_uint;
typedef unsigned long long mz_uint64;
#if defined(_M_IX86) || defined(_M_X64)
// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 if integer loads and stores to unaligned addresses are acceptable on the target platform (slightly faster).
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian.
#define MINIZ_LITTLE_ENDIAN 1
#endif
#if defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__)
// Set MINIZ_HAS_64BIT_REGISTERS to 1 if the processor has 64-bit general purpose registers (enables 64-bit bitbuffer in inflator)
#define MINIZ_HAS_64BIT_REGISTERS 1
#endif
// Works around MSVC's spammy "warning C4127: conditional expression is constant" message.
#ifdef _MSC_VER
#define MZ_MACRO_END while (0, 0)
#else
#define MZ_MACRO_END while (0)
#endif
// Decompression flags used by tinfl_decompress().
// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream.
// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input.
// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB).
// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes.
enum
{
TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
TINFL_FLAG_HAS_MORE_INPUT = 2,
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
TINFL_FLAG_COMPUTE_ADLER32 = 8
};
// High level decompression functions:
// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc().
// On entry:
// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress.
// On return:
// Function returns a pointer to the decompressed data, or NULL on failure.
// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data.
// The caller must free() the returned block when it's no longer needed.
void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory.
// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success.
#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer.
// Returns 1 on success or 0 on failure.
typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor;
// Max size of LZ dictionary.
#define TINFL_LZ_DICT_SIZE 32768
// Return status.
typedef enum
{
TINFL_STATUS_BAD_PARAM = -3,
TINFL_STATUS_ADLER32_MISMATCH = -2,
TINFL_STATUS_FAILED = -1,
TINFL_STATUS_DONE = 0,
TINFL_STATUS_NEEDS_MORE_INPUT = 1,
TINFL_STATUS_HAS_MORE_OUTPUT = 2
} tinfl_status;
// Initializes the decompressor to its initial state.
#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END
#define tinfl_get_adler32(r) (r)->m_check_adler32
// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability.
// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output.
tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
// Internal/private bits follow.
enum
{
TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19,
TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
};
typedef struct
{
mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];
mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
} tinfl_huff_table;
#if MINIZ_HAS_64BIT_REGISTERS
#define TINFL_USE_64BIT_BITBUF 1
#endif
#if TINFL_USE_64BIT_BITBUF
typedef mz_uint64 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (64)
#else
typedef mz_uint32 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (32)
#endif
struct tinfl_decompressor_tag
{
mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
tinfl_bit_buf_t m_bit_buf;
size_t m_dist_from_out_buf_start;
tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];
mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
};
#endif // #ifdef TINFL_HEADER_INCLUDED
// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.)
#ifndef TINFL_HEADER_FILE_ONLY
#include <string.h>
// MZ_MALLOC, etc. are only used by the optional high-level helper functions.
#ifdef MINIZ_NO_MALLOC
#define MZ_MALLOC(x) NULL
#define MZ_FREE(x) x, ((void)0)
#define MZ_REALLOC(p, x) NULL
#else
#define MZ_MALLOC(x) malloc(x)
#define MZ_FREE(x) free(x)
#define MZ_REALLOC(p, x) realloc(p, x)
#endif
#define MZ_MAX(a,b) (((a)>(b))?(a):(b))
#define MZ_MIN(a,b) (((a)<(b))?(a):(b))
#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN
#define MZ_READ_LE16(p) *((const mz_uint16 *)(p))
#define MZ_READ_LE32(p) *((const mz_uint32 *)(p))
#else
#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U))
#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
#endif
#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)
#define TINFL_MEMSET(p, c, l) memset(p, c, l)
#define TINFL_CR_BEGIN switch(r->m_state) { case 0:
#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END
#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END
#define TINFL_CR_FINISH }
// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never
// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario.
#define TINFL_GET_BYTE(state_index, c) do { \
if (pIn_buf_cur >= pIn_buf_end) { \
for ( ; ; ) { \
if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \
TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \
if (pIn_buf_cur < pIn_buf_end) { \
c = *pIn_buf_cur++; \
break; \
} \
} else { \
c = 0; \
break; \
} \
} \
} else c = *pIn_buf_cur++; } MZ_MACRO_END
#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n))
#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END
// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2.
// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a
// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the
// bit buffer contains >=15 bits (deflate's max. Huffman code size).
#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \
do { \
temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \
if (temp >= 0) { \
code_len = temp >> 9; \
if ((code_len) && (num_bits >= code_len)) \
break; \
} else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \
code_len = TINFL_FAST_LOOKUP_BITS; \
do { \
temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \
} while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \
} TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \
} while (num_bits < 15);
// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read
// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully
// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32.
// The slow path is only executed at the very end of the input buffer.
#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \
int temp; mz_uint code_len, c; \
if (num_bits < 15) { \
if ((pIn_buf_end - pIn_buf_cur) < 2) { \
TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \
} else { \
bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \
} \
} \
if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \
code_len = temp >> 9, temp &= 511; \
else { \
code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \
} sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END
tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)
{
static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 };
static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
static const int s_min_table_sizes[3] = { 257, 1, 4 };
tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf;
const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;
mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size;
size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;
// Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter).
if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; }
num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start;
TINFL_CR_BEGIN
bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1;
if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
{
TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1);
counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));
if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));
if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); }
}
do
{
TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1;
if (r->m_type == 0)
{
TINFL_SKIP_BITS(5, num_bits & 7);
for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); }
if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); }
while ((counter) && (num_bits))
{
TINFL_GET_BITS(51, dist, 8);
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = (mz_uint8)dist;
counter--;
}
while (counter)
{
size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); }
while (pIn_buf_cur >= pIn_buf_end)
{
if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT)
{
TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT);
}
else
{
TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED);
}
}
n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);
TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n;
}
}
else if (r->m_type == 3)
{
TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);
}
else
{
if (r->m_type == 1)
{
mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i;
r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);
for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8;
}
else
{
for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; }
MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; }
r->m_table_sizes[2] = 19;
}
for ( ; (int)r->m_type >= 0; r->m_type--)
{
int tree_next, tree_cur; tinfl_huff_table *pTable;
mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree);
for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++;
used_syms = 0, total = 0; next_code[0] = next_code[1] = 0;
for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); }
if ((65536 != total) && (used_syms > 1))
{
TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);
}
for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)
{
mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue;
cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1);
if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; }
if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; }
rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);
for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)
{
tree_cur -= ((rev_code >>= 1) & 1);
if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1];
}
tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index;
}
if (r->m_type == 2)
{
for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); )
{
mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; }
if ((dist == 16) && (!counter))
{
TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);
}
num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16];
TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s;
}
if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)
{
TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);
}
TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);
}
}
for ( ; ; )
{
mz_uint8 *pSrc;
for ( ; ; )
{
if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))
{
TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]);
if (counter >= 256)
break;
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = (mz_uint8)counter;
}
else
{
int sym2; mz_uint code_len;
#if TINFL_USE_64BIT_BITBUF
if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; }
#else
if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
#endif
if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
code_len = sym2 >> 9;
else
{
code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
}
counter = sym2; bit_buf >>= code_len; num_bits -= code_len;
if (counter & 256)
break;
#if !TINFL_USE_64BIT_BITBUF
if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; }
#endif
if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)
code_len = sym2 >> 9;
else
{
code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0);
}
bit_buf >>= code_len; num_bits -= code_len;
pOut_buf_cur[0] = (mz_uint8)counter;
if (sym2 & 256)
{
pOut_buf_cur++;
counter = sym2;
break;
}
pOut_buf_cur[1] = (mz_uint8)sym2;
pOut_buf_cur += 2;
}
}
if ((counter &= 511) == 256) break;
num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257];
if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; }
TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]);
num_extra = s_dist_extra[dist]; dist = s_dist_base[dist];
if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; }
dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;
if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))
{
TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);
}
pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);
if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)
{
while (counter--)
{
while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); }
*pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];
}
continue;
}
#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES
else if ((counter >= 9) && (counter <= dist))
{
const mz_uint8 *pSrc_end = pSrc + (counter & ~7);
do
{
((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];
((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];
pOut_buf_cur += 8;
} while ((pSrc += 8) < pSrc_end);
if ((counter &= 7) < 3)
{
if (counter)
{
pOut_buf_cur[0] = pSrc[0];
if (counter > 1)
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur += counter;
}
continue;
}
}
#endif
do
{
pOut_buf_cur[0] = pSrc[0];
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur[2] = pSrc[2];
pOut_buf_cur += 3; pSrc += 3;
} while ((int)(counter -= 3) > 2);
if ((int)counter > 0)
{
pOut_buf_cur[0] = pSrc[0];
if ((int)counter > 1)
pOut_buf_cur[1] = pSrc[1];
pOut_buf_cur += counter;
}
}
}
} while (!(r->m_final & 1));
if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)
{
TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; }
}
TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);
TINFL_CR_FINISH
common_exit:
r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start;
*pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next;
if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))
{
const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size;
mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552;
while (buf_len)
{
for (i = 0; i + 7 < block_len; i += 8, ptr += 8)
{
s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1;
s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1;
}
for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1;
s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552;
}
r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH;
}
return status;
}
// Higher level helper functions.
void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)
{
tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0;
*pOut_len = 0;
tinfl_init(&decomp);
for ( ; ; )
{
size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity;
tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size,
(flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT))
{
MZ_FREE(pBuf); *pOut_len = 0; return NULL;
}
src_buf_ofs += src_buf_size;
*pOut_len += dst_buf_size;
if (status == TINFL_STATUS_DONE) break;
new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128;
pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity);
if (!pNew_buf)
{
MZ_FREE(pBuf); *pOut_len = 0; return NULL;
}
pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity;
}
return pBuf;
}
size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)
{
tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp);
status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len;
}
int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)
{
int result = 0;
tinfl_decompressor decomp;
mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0;
if (!pDict)
return TINFL_STATUS_FAILED;
tinfl_init(&decomp);
for ( ; ; )
{
size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs;
tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size,
(flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)));
in_buf_ofs += in_buf_size;
if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user)))
break;
if (status != TINFL_STATUS_HAS_MORE_OUTPUT)
{
result = (status == TINFL_STATUS_DONE);
break;
}
dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1);
}
MZ_FREE(pDict);
*pIn_buf_size = in_buf_ofs;
return result;
}
#endif // #ifndef TINFL_HEADER_FILE_ONLY
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/

49
src/wiiu/wiiu.cpp Normal file
View file

@ -0,0 +1,49 @@
#include "elf_reader.h"
#include "cafe_loader.h"
#include <idaldr.h>
static int idaapi
accept_file(linput_t *li, char fileformatname[MAX_FILE_FORMAT_NAME], int n)
{
if ( n > 0 )
return 0;
elf_reader<elf32> elf(li);
if (elf.verifyHeader()) {
if (elf.type() == ELF_FILETYPE_CAFE_RPL) {
set_processor_type("ppc", SETPROC_ALL);
qsnprintf(fileformatname, MAX_FILE_FORMAT_NAME, "WII U RPX/RPL");
return ACCEPT_FIRST | 1;
}
}
return 0;
}
static void idaapi
load_file(linput_t *li, ushort neflags, const char *fileformatname)
{
elf_reader<elf32> elf(li); elf.read();
ea_t relocAddr = 0;
if (neflags & NEF_MAN) {
askaddr(&relocAddr, "Please specify a relocation address base.");
}
cafe_loader ldr(&elf); ldr.apply();
}
__declspec(dllexport)
loader_t LDSC =
{
IDP_INTERFACE_VERSION,
0,
accept_file,
load_file,
NULL,
NULL,
NULL
};