diff --git a/Makefile.am b/Makefile.am index 595de14..1bae97d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,7 +9,7 @@ elf2nro_SOURCES = src/elf2nro.c src/elf64.h src/romfs.c src/filepath.c src/filep elf2nso_SOURCES = src/elf2nso.c src/sha256.c src/sha256.h src/elf64.h src/elf_common.h -elf2kip_SOURCES = src/elf2kip.c src/cJSON.c src/cJSON.h src/sha256.c src/sha256.h src/elf64.h src/elf_common.h +elf2kip_SOURCES = src/elf2kip.c src/cJSON.c src/cJSON.h src/blz.c src/blz.h src/elf64.h src/elf_common.h nacptool_SOURCES = src/nacptool.c diff --git a/src/blz.c b/src/blz.c new file mode 100644 index 0000000..118ff4e --- /dev/null +++ b/src/blz.c @@ -0,0 +1,338 @@ +/*----------------------------------------------------------------------------*/ +/*-- blz.c - Bottom LZ coding for Nintendo GBA/DS --*/ +/*-- Copyright (C) 2011 CUE --*/ +/*-- --*/ +/*-- 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, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- 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 for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ +#include "blz.h" +#include +#include +#include + +/*----------------------------------------------------------------------------*/ +#define CMD_DECODE 0x00 // decode +#define CMD_ENCODE 0x01 // encode + +#define BLZ_SHIFT 1 // bits to shift +#define BLZ_MASK 0x80 // bits to check: + // ((((1 << BLZ_SHIFT) - 1) << (8 - BLZ_SHIFT) + +#define BLZ_THRESHOLD 2 // max number of bytes to not encode +#define BLZ_N 0x1002 // max offset ((1 << 12) + 2) +#define BLZ_F 0x12 // max coded ((1 << 4) + BLZ_THRESHOLD) + +#define RAW_MINIM 0x00000000 // empty file, 0 bytes +#define RAW_MAXIM 0x00FFFFFF // 3-bytes length, 16MB - 1 + +#define BLZ_MINIM 0x00000004 // header only (empty RAW file) +#define BLZ_MAXIM 0x01400000 // 0x0120000A, padded to 20MB: + // * length, RAW_MAXIM + // * flags, (RAW_MAXIM + 7) / 8 + // * header, 11 + // 0x00FFFFFF + 0x00200000 + 12 + padding + +/*----------------------------------------------------------------------------*/ +#define BREAK(text) { printf(text); return; } +#define EXIT(text) { printf(text); exit(-1); } + +/*----------------------------------------------------------------------------*/ +u8 *Memory(int length, int size); + +u8 *BLZ_Code(u8 *raw_buffer, int raw_len, u32 *new_len, int best); +void BLZ_Invert(u8 *buffer, int length); + +/*----------------------------------------------------------------------------*/ +u8 *Memory(int length, int size) { + u8 *fb; + + fb = (u8 *) calloc(length * size, size); + if (fb == NULL) EXIT("\nMemory error\n"); + + return(fb); +} + +/*----------------------------------------------------------------------------*/ +void BLZ_Decode(char *filename) { + // u8 *pak_buffer, *raw_buffer, *pak, *raw, *pak_end, *raw_end; + // u32 pak_len, raw_len, len, pos, inc_len, hdr_len, enc_len, dec_len; + // u8 flags, mask; + + // printf("- decoding '%s'", filename); + + // // pak_buffer = Load(filename, &pak_len, BLZ_MINIM, BLZ_MAXIM); + + // inc_len = *(u32 *)(pak_buffer + pak_len - 4); + // if (!inc_len) { + // enc_len = 0; + // dec_len = pak_len - 4; + // pak_len = 0; + // raw_len = dec_len; + // } else { + // if (pak_len < 8) EXIT("File has a bad header\n"); + // hdr_len = pak_buffer[pak_len - 5]; + // if ((hdr_len < 0x08) || (hdr_len > 0x0B)) EXIT("Bad header length\n"); + // if (pak_len <= hdr_len) EXIT("Bad length\n"); + // enc_len = *(u32 *)(pak_buffer + pak_len - 8) & 0x00FFFFFF; + // dec_len = pak_len - enc_len; + // pak_len = enc_len - hdr_len; + // raw_len = dec_len + enc_len + inc_len; + // if (raw_len > RAW_MAXIM) EXIT("Bad decoded length\n"); + // } + + // raw_buffer = (u8 *) Memory(raw_len, sizeof(char)); + + // pak = pak_buffer; + // raw = raw_buffer; + // pak_end = pak_buffer + dec_len + pak_len; + // raw_end = raw_buffer + raw_len; + + // for (len = 0; len < dec_len; len++) *(raw++) = *(pak++); + + // BLZ_Invert(pak_buffer + dec_len, pak_len); + + // mask = 0; + + // while (raw < raw_end) { + // if (!(mask >>= BLZ_SHIFT)) { + // if (pak == pak_end) break; + // flags = *pak++; + // mask = BLZ_MASK; + // } + + // if (!(flags & mask)) { + // if (pak == pak_end) break; + // *raw++ = *pak++; + // } else { + // if (pak + 1 >= pak_end) break; + // pos = *pak++ << 8; + // pos |= *pak++; + // len = (pos >> 12) + BLZ_THRESHOLD + 1; + // if (raw + len > raw_end) { + // printf(", WARNING: wrong decoded length!"); + // len = raw_end - raw; + // } + // pos = (pos & 0xFFF) + 3; + // while (len--) *(raw++) = *(raw - pos); + // } + // } + + // BLZ_Invert(raw_buffer + dec_len, raw_len - dec_len); + + // raw_len = raw - raw_buffer; + + // if (raw != raw_end) printf(", WARNING: unexpected end of encoded file!"); + + // // Save(filename, raw_buffer, raw_len); + + // free(raw_buffer); + // free(pak_buffer); + + // printf("\n"); +} + +u8 *Load(char *filename, u32 *length, int min, int max) { + FILE *fp; + int fs; + u8 *fb; + + if ((fp = fopen(filename, "rb")) == NULL) EXIT("\nFile open error\n"); + fseek(fp, 0, SEEK_END); + fs = ftell(fp); + fseek(fp, 0, SEEK_SET); + if ((fs < min) || (fs > max)) EXIT("\nFile size error\n"); + fb = Memory(fs + 3, sizeof(char)); + if (fread(fb, 1, fs, fp) != fs) EXIT("\nFile read error\n"); + if (fclose(fp) == EOF) EXIT("\nFile close error\n"); + + *length = fs; + + return(fb); +} + +/*----------------------------------------------------------------------------*/ +u8* BLZ_Encode(char *filename, u32* pak_len, int mode) { + u8 *raw_buffer, *pak_buffer, *new_buffer; + u32 raw_len, new_len; + + raw_buffer = Load(filename, &raw_len, RAW_MINIM, RAW_MAXIM); + + pak_buffer = NULL; + *pak_len = BLZ_MAXIM + 1; + + new_buffer = BLZ_Code(raw_buffer, raw_len, &new_len, mode); + if (new_len < *pak_len) { + if (pak_buffer != NULL) free(pak_buffer); + pak_buffer = new_buffer; + *pak_len = new_len; + } + + return pak_buffer; +} + +/*----------------------------------------------------------------------------*/ +u8 *BLZ_Code(u8 *raw_buffer, int raw_len, u32 *new_len, int best) { + u8 *pak_buffer, *pak, *raw, *raw_end, *flg = NULL, *tmp; + u32 pak_len, inc_len, hdr_len, enc_len, len, pos, max; + u32 len_best, pos_best = 0, len_next, pos_next, len_post, pos_post; + u32 pak_tmp, raw_tmp; + u8 mask; + +#define SEARCH(l,p) { \ + l = BLZ_THRESHOLD; \ + \ + max = raw - raw_buffer >= BLZ_N ? BLZ_N : raw - raw_buffer; \ + for (pos = 3; pos <= max; pos++) { \ + for (len = 0; len < BLZ_F; len++) { \ + if (raw + len == raw_end) break; \ + if (len >= pos) break; \ + if (*(raw + len) != *(raw + len - pos)) break; \ + } \ + \ + if (len > l) { \ + p = pos; \ + if ((l = len) == BLZ_F) break; \ + } \ + } \ +} + + pak_tmp = 0; + raw_tmp = raw_len; + + pak_len = raw_len + ((raw_len + 7) / 8) + 15; + pak_buffer = (u8 *) Memory(pak_len, sizeof(char)); + + BLZ_Invert(raw_buffer, raw_len); + + pak = pak_buffer; + raw = raw_buffer; + raw_end = raw_buffer + raw_len; + + mask = 0; + + while (raw < raw_end) { + if (!(mask >>= BLZ_SHIFT)) { + *(flg = pak++) = 0; + mask = BLZ_MASK; + } + + SEARCH(len_best, pos_best); + + // LZ-CUE optimization start + if (best) { + if (len_best > BLZ_THRESHOLD) { + if (raw + len_best < raw_end) { + raw += len_best; + SEARCH(len_next, pos_next); + raw -= len_best - 1; + SEARCH(len_post, pos_post); + raw--; + + if (len_next <= BLZ_THRESHOLD) len_next = 1; + if (len_post <= BLZ_THRESHOLD) len_post = 1; + + if (len_best + len_next <= 1 + len_post) len_best = 1; + } + } + } + // LZ-CUE optimization end + + *flg <<= 1; + if (len_best > BLZ_THRESHOLD) { + raw += len_best; + *flg |= 1; + *pak++ = ((len_best - (BLZ_THRESHOLD+1)) << 4) | ((pos_best - 3) >> 8); + *pak++ = (pos_best - 3) & 0xFF; + } else { + *pak++ = *raw++; + } + + if (pak - pak_buffer + raw_len - (raw - raw_buffer) < pak_tmp + raw_tmp) { + pak_tmp = pak - pak_buffer; + raw_tmp = raw_len - (raw - raw_buffer); + } + } + + while (mask && (mask != 1)) { + mask >>= BLZ_SHIFT; + *flg <<= 1; + } + + pak_len = pak - pak_buffer; + + BLZ_Invert(raw_buffer, raw_len); + BLZ_Invert(pak_buffer, pak_len); + + if (!pak_tmp || (raw_len + 4 < ((pak_tmp + raw_tmp + 3) & -4) + 8)) { + pak = pak_buffer; + raw = raw_buffer; + raw_end = raw_buffer + raw_len; + + while (raw < raw_end) *pak++ = *raw++; + + while ((pak - pak_buffer) & 3) *pak++ = 0; + + *(u32 *)pak = 0; pak += 4; + } else { + tmp = (u8 *) Memory(raw_tmp + pak_tmp + 11, sizeof(char)); + + for (len = 0; len < raw_tmp; len++) + tmp[len] = raw_buffer[len]; + + for (len = 0; len < pak_tmp; len++) + tmp[raw_tmp + len] = pak_buffer[len + pak_len - pak_tmp]; + + pak = pak_buffer; + pak_buffer = tmp; + + free(pak); + + pak = pak_buffer + raw_tmp + pak_tmp; + + enc_len = pak_tmp; + hdr_len = 12; + inc_len = raw_len - pak_tmp - raw_tmp; + + while ((pak - pak_buffer) & 3) { + *pak++ = 0xFF; + hdr_len++; + } + + *(u32 *)pak = enc_len + hdr_len; pak += 4; + *(u32 *)pak = hdr_len; pak += 4; + *(u32 *)pak = inc_len - hdr_len; pak += 4; + } + + *new_len = pak - pak_buffer; + + return(pak_buffer); +} + +/*----------------------------------------------------------------------------*/ +void BLZ_Invert(u8 *buffer, int length) { + u8 *bottom, ch; + + bottom = buffer + length - 1; + + while (buffer < bottom) { + ch = *buffer; + *buffer++ = *bottom; + *bottom-- = ch; + } +} + +/*----------------------------------------------------------------------------*/ +/*-- EOF Copyright (C) 2011 CUE --*/ +/*----------------------------------------------------------------------------*/ diff --git a/src/blz.h b/src/blz.h new file mode 100644 index 0000000..8263889 --- /dev/null +++ b/src/blz.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; + +#define BLZ_NORMAL 0 // normal mode +#define BLZ_BEST 1 // best mode + +u8 *BLZ_Code(u8 *raw_buffer, int raw_len, u32 *new_len, int best); \ No newline at end of file diff --git a/src/elf2kip.c b/src/elf2kip.c index 05ff80a..a0fb083 100644 --- a/src/elf2kip.c +++ b/src/elf2kip.c @@ -6,6 +6,7 @@ #include #include #include "cJSON.h" +#include "blz.h" #include "elf64.h" typedef uint64_t u64; @@ -428,9 +429,7 @@ int main(int argc, char* argv[]) { KipHeader kip_hdr = {0}; memcpy(kip_hdr.Magic, "KIP1", 4); - /* TODO: Enable compression for each section. */ - /* TODO: kip_hdr.Flags = 0x1F; */ - kip_hdr.Flags = 0x18; + kip_hdr.Flags = 0x1F; if (sizeof(KipHeader) != 0x100) { fprintf(stderr, "Bad compile environment!\n"); @@ -474,9 +473,11 @@ int main(int argc, char* argv[]) { Elf64_Phdr* phdrs = (Elf64_Phdr*) &elf[hdr->e_phoff]; size_t i, j = 0; size_t file_off = 0; + size_t dst_off = 0; size_t tmpsize; uint8_t* buf[3]; + uint8_t* cmp[3]; size_t FileOffsets[3]; for (i=0; i<4; i++) { @@ -496,7 +497,7 @@ int main(int argc, char* argv[]) { } - kip_hdr.Segments[i].DstOff = file_off; + kip_hdr.Segments[i].DstOff = dst_off; // .bss is special if (i == 3) { @@ -513,7 +514,6 @@ int main(int argc, char* argv[]) { FileOffsets[i] = phdr->p_vaddr; kip_hdr.Segments[i].DecompSz = (phdr->p_filesz + 0xFFF) & ~0xFFF; - kip_hdr.Segments[i].CompSz = kip_hdr.Segments[i].DecompSz; buf[i] = malloc(kip_hdr.Segments[i].DecompSz); if (buf[i] == NULL) { @@ -524,9 +524,11 @@ int main(int argc, char* argv[]) { memset(buf[i], 0, kip_hdr.Segments[i].DecompSz); memcpy(buf[i], &elf[phdr->p_offset], phdr->p_filesz); + cmp[i] = BLZ_Code(buf[i], phdr->p_filesz, &kip_hdr.Segments[i].CompSz, BLZ_BEST); - file_off += kip_hdr.Segments[i].DecompSz; - file_off = (file_off + 0xFFF) & ~0xFFF; + file_off += kip_hdr.Segments[i].CompSz; + dst_off += kip_hdr.Segments[i].DecompSz; + dst_off = (dst_off + 0xFFF) & ~0xFFF; } FILE* out = fopen(argv[3], "wb"); @@ -541,7 +543,7 @@ int main(int argc, char* argv[]) { for (i=0; i<3; i++) { fseek(out, sizeof(kip_hdr) + kip_hdr.Segments[i].DstOff, SEEK_SET); - fwrite(buf[i], kip_hdr.Segments[i].CompSz, 1, out); + fwrite(cmp[i], kip_hdr.Segments[i].CompSz, 1, out); } fseek(out, 0, SEEK_SET);