From c12d944076cc1adb5991c12b99c512039a6494bc Mon Sep 17 00:00:00 2001 From: d0k3 Date: Thu, 9 Mar 2017 14:35:36 +0100 Subject: [PATCH] Show splash screen when initializing --- Makefile | 16 +- data/gm9_splash.qlz | Bin 0 -> 23580 bytes data/sm9_splash.qlz | Bin 0 -> 22670 bytes resources/gm9_splash.png | Bin 0 -> 19027 bytes resources/sm9_splash.png | Bin 0 -> 18239 bytes source/common/common.h | 7 + source/common/ui.c | 6 +- source/common/ui.h | 2 +- source/fs/fsutil.h | 4 - source/godmode.c | 49 ++- source/quicklz/qlzcomp.c | 17 + source/quicklz/qlzcomp.h | 6 + source/quicklz/quicklz.c | 848 +++++++++++++++++++++++++++++++++++++++ source/quicklz/quicklz.h | 151 +++++++ 14 files changed, 1086 insertions(+), 20 deletions(-) create mode 100644 data/gm9_splash.qlz create mode 100644 data/sm9_splash.qlz create mode 100644 resources/gm9_splash.png create mode 100644 resources/sm9_splash.png create mode 100644 source/quicklz/qlzcomp.c create mode 100644 source/quicklz/qlzcomp.h create mode 100644 source/quicklz/quicklz.c create mode 100644 source/quicklz/quicklz.h diff --git a/Makefile b/Makefile index cb177bd..24e33f8 100644 --- a/Makefile +++ b/Makefile @@ -21,9 +21,9 @@ ifeq ($(MODE),safe) export TARGET := SafeMode9 endif BUILD := build -SOURCES := source source/common source/fs source/crypto source/fatfs source/nand source/virtual source/game source/gamecart +SOURCES := source source/common source/fs source/crypto source/fatfs source/nand source/virtual source/game source/gamecart source/quicklz DATA := data -INCLUDES := source source/common source/font source/fs source/crypto source/fatfs source/nand source/virtual source/game source/gamecart +INCLUDES := source source/common source/font source/fs source/crypto source/fatfs source/nand source/virtual source/game source/gamecart source/quicklz #--------------------------------------------------------------------------------- # options for code generation @@ -86,7 +86,10 @@ export DEPSDIR := $(CURDIR)/$(BUILD) CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) -BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/gm9*.*))) +ifeq ($(MODE),safe) + BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/sm9*.*))) +endif #--------------------------------------------------------------------------------- # use CXX for linking C++ projects, CC for standard C @@ -188,6 +191,13 @@ $(OUTPUT).elf : $(OFILES) @$(OBJCOPY) --set-section-flags .bss=alloc,load,contents -O binary $< $@ @echo built ... $(notdir $@) +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.qlz.o: %.qlz +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) -include $(DEPENDS) diff --git a/data/gm9_splash.qlz b/data/gm9_splash.qlz new file mode 100644 index 0000000000000000000000000000000000000000..e4b6dd1ce16de79f974a84cd0e060aebbb782c27 GIT binary patch literal 23580 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 BKK1|r literal 0 HcmV?d00001 diff --git a/data/sm9_splash.qlz b/data/sm9_splash.qlz new file mode 100644 index 0000000000000000000000000000000000000000..2fff839f582bde9684c44d0c622c6b0e90e82001 GIT binary patch literal 22670 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 literal 0 HcmV?d00001 diff --git a/resources/sm9_splash.png b/resources/sm9_splash.png new file mode 100644 index 0000000000000000000000000000000000000000..c0d0f53a45caf8b6eef05e6ecd7e345b42e3a407 GIT binary patch literal 18239 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 literal 0 HcmV?d00001 diff --git a/source/common/common.h b/source/common/common.h index d7ee865..c3d745a 100644 --- a/source/common/common.h +++ b/source/common/common.h @@ -62,10 +62,17 @@ #define DIR_BUFFER_SIZE (0x100000) // buffer area defines (temporary, in use by various functions) // -> godmode.c hexviewer +// -> godmode.c loading payloads // -> ncch.c seed setup // -> cia.c ticket / titlekey setup // -> gameutil.c various temporary stuff // -> nandcmac.c for processing agbsave +// -> nandutil.c for storing essential backup +// -> ctrtransfer.c for SecureInfo (temporary) +// -> vgame.c for handling FIRMs +// -> vtickdb.c for parsing ticket.db +// -> qlzcomp.c for temporary compression stuff +// meaning: careful when using this! #define TEMP_BUFFER ((u8*)0x21100000) #define TEMP_BUFFER_SIZE (0x100000) // buffer area defines (in use by fsutil.c, fsinit.c and gameutil.c) diff --git a/source/common/ui.c b/source/common/ui.c index b466963..ca4ff3a 100644 --- a/source/common/ui.c +++ b/source/common/ui.c @@ -94,10 +94,10 @@ u32 GetDrawStringHeight(const char* str) { return height; } -u32 GetDrawStringWidth(char* str) { +u32 GetDrawStringWidth(const char* str) { u32 width = 0; - char* old_lf = str; - char* str_end = str + strnlen(str, STRBUF_SIZE); + char* old_lf = (char*) str; + char* str_end = (char*) str + strnlen(str, STRBUF_SIZE); for (char* lf = strchr(str, '\n'); lf != NULL; lf = strchr(lf + 1, '\n')) { if ((u32) (lf - old_lf) > width) width = lf - old_lf; old_lf = lf; diff --git a/source/common/ui.h b/source/common/ui.h index 45203c5..60b78e0 100644 --- a/source/common/ui.h +++ b/source/common/ui.h @@ -64,7 +64,7 @@ void DrawString(unsigned char *screen, const char *str, int x, int y, int color, void DrawStringF(unsigned char *screen, int x, int y, int color, int bgcolor, const char *format, ...); u32 GetDrawStringHeight(const char* str); -u32 GetDrawStringWidth(char* str); +u32 GetDrawStringWidth(const char* str); void ResizeString(char* dest, const char* orig, int nsize, int tpos, bool align_right); void TruncateString(char* dest, const char* orig, int nsize, int tpos); diff --git a/source/fs/fsutil.h b/source/fs/fsutil.h index 88c726d..0f92674 100644 --- a/source/fs/fsutil.h +++ b/source/fs/fsutil.h @@ -1,10 +1,6 @@ #pragma once #include "common.h" -// temporary -// #include "fsinit.h" -// #include "fsdrive.h" -// #include "fsperm.h" // move / copy flags #define OVERRIDE_PERM (1UL<<0) diff --git a/source/godmode.c b/source/godmode.c index bfce2fb..cd06691 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -17,6 +17,16 @@ #include "ncchinfo.h" #include "image.h" #include "chainload.h" +#include "qlzcomp.h" +#include "timer.h" + +#ifndef SAFEMODE +#include "gm9_splash_qlz.h" +#define QLZ_SPLASH gm9_splash_qlz +#else +#include "sm9_splash_qlz.h" +#define QLZ_SPLASH sm9_splash_qlz +#endif #define N_PANES 2 @@ -1118,7 +1128,24 @@ u32 HomeMoreMenu(char* current_path, DirStruct* current_dir, DirStruct* clipboar return HomeMoreMenu(current_path, current_dir, clipboard); } + +u32 SplashInit() { + const char* namestr = FLAVOR " Explorer v" VERSION; + const char* loadstr = "loading..."; + const u32 pos_xb = 10; + const u32 pos_yb = 10; + const u32 pos_xu = SCREEN_WIDTH_BOT - 10 - GetDrawStringWidth(loadstr); + const u32 pos_yu = SCREEN_HEIGHT - 10 - GetDrawStringHeight(loadstr); + ClearScreenF(true, true, COLOR_STD_BG); + QlzDecompress(TOP_SCREEN, QLZ_SPLASH, 0); + DrawStringF(BOT_SCREEN, pos_xb, pos_yb, COLOR_STD_FONT, COLOR_STD_BG, "%s\n%*.*s\n%s", namestr, strnlen(namestr, 64), strnlen(namestr, 64), + "------------------------------", "https://github.com/d0k3/GodMode9"); + DrawStringF(BOT_SCREEN, pos_xu, pos_yu, COLOR_STD_FONT, COLOR_STD_BG, loadstr); + + return 0; +} + u32 GodMode() { static const u32 quick_stp = 20; u32 exit_mode = GODMODE_EXIT_REBOOT; @@ -1140,19 +1167,19 @@ u32 GodMode() { ShowPrompt(false, "Out of memory!"); // just to be safe return exit_mode; } - while (!InitSDCardFS()) { - const char* optionstr[] = { "Retry initialising", "Poweroff system", "Reboot system", "No SD mode (exp.)", "SD format menu" }; - u32 user_select = ShowSelectPrompt(5, optionstr, "Initialising SD card failed!\nSelect action:" ); - if (user_select == 2) return GODMODE_EXIT_POWEROFF; - else if (user_select == 3) return GODMODE_EXIT_REBOOT; - else if (user_select == 4) break; - else if (user_select == 5) SdFormatMenu(); - ClearScreenF(true, true, COLOR_STD_BG); - } + + SplashInit(); + timer_start(); // show splash for at least 1 sec + + InitSDCardFS(); InitEmuNandBase(true); InitNandCrypto(); InitExtFS(); + // do this now so we don't have to do it later + GetFreeSpace("0:"); + InitVCartDrive(); + // could also check for a9lh via this: ((*(vu32*) 0x101401C0) == 0) if ((!IS_O3DS) && !CheckSlot0x05Crypto()) { if (!ShowPrompt(true, "Warning: slot0x05 crypto fail!\nCould not set up slot0x05keyY.\nContinue?")) { @@ -1165,6 +1192,10 @@ u32 GodMode() { GetDirContents(current_dir, ""); clipboard->n_entries = 0; memset(panedata, 0x00, 0x10000); + + while(timer_sec() < 1); // show splash for at least 1 sec + ClearScreenF(true, true, COLOR_STD_BG); // clear splash + while (true) { // this is the main loop int curr_drvtype = DriveType(current_path); diff --git a/source/quicklz/qlzcomp.c b/source/quicklz/qlzcomp.c new file mode 100644 index 0000000..6475c8b --- /dev/null +++ b/source/quicklz/qlzcomp.c @@ -0,0 +1,17 @@ +#include "qlzcomp.h" +#include "quicklz.h" + +u32 QlzCompress(void* out, const void* in, u32 size_in) { + if (TEMP_BUFFER_SIZE < sizeof(qlz_state_compress)) return 1; + qlz_state_compress *state_compress = (qlz_state_compress*) (void*) TEMP_BUFFER; + memset(state_compress, 0, sizeof(qlz_state_compress)); + return (qlz_compress(in, out, size_in, state_compress) > 0) ? 0 : 1; +} + +u32 QlzDecompress(void* out, const void* in, u32 size_out) { + if (TEMP_BUFFER_SIZE < sizeof(qlz_state_decompress)) return 1; + if (size_out && (qlz_size_decompressed(in) != size_out)) return 1; + qlz_state_decompress *state_decompress = (qlz_state_decompress*) (void*) TEMP_BUFFER; + memset(state_decompress, 0, sizeof(qlz_state_decompress)); + return (qlz_decompress(in, out, state_decompress) > 0) ? 0 : 1; +} diff --git a/source/quicklz/qlzcomp.h b/source/quicklz/qlzcomp.h new file mode 100644 index 0000000..d5c2d4c --- /dev/null +++ b/source/quicklz/qlzcomp.h @@ -0,0 +1,6 @@ +#pragma once + +#include "common.h" + +u32 QlzCompress(void* out, const void* in, u32 size_decomp); +u32 QlzDecompress(void* out, const void* in, u32 size); diff --git a/source/quicklz/quicklz.c b/source/quicklz/quicklz.c new file mode 100644 index 0000000..3742129 --- /dev/null +++ b/source/quicklz/quicklz.c @@ -0,0 +1,848 @@ +// Fast data compression library +// Copyright (C) 2006-2011 Lasse Mikkel Reinhold +// lar@quicklz.com +// +// QuickLZ can be used for free under the GPL 1, 2 or 3 license (where anything +// released into public must be open source) or under a commercial license if such +// has been acquired (see http://www.quicklz.com/order.html). The commercial license +// does not cover derived or ported versions created by third parties under GPL. + +// 1.5.0 final + +#include "quicklz.h" + +#if QLZ_VERSION_MAJOR != 1 || QLZ_VERSION_MINOR != 5 || QLZ_VERSION_REVISION != 0 + #error quicklz.c and quicklz.h have different versions +#endif + +#if (defined(__X86__) || defined(__i386__) || defined(i386) || defined(_M_IX86) || defined(__386__) || defined(__x86_64__) || defined(_M_X64)) + #define X86X64 +#endif + +#define MINOFFSET 2 +#define UNCONDITIONAL_MATCHLEN 6 +#define UNCOMPRESSED_END 4 +#define CWORD_LEN 4 + +#if QLZ_COMPRESSION_LEVEL == 1 && defined QLZ_PTR_64 && QLZ_STREAMING_BUFFER == 0 + #define OFFSET_BASE source + #define CAST (ui32)(size_t) +#else + #define OFFSET_BASE 0 + #define CAST +#endif + +int qlz_get_setting(int setting) +{ + switch (setting) + { + case 0: return QLZ_COMPRESSION_LEVEL; + case 1: return sizeof(qlz_state_compress); + case 2: return sizeof(qlz_state_decompress); + case 3: return QLZ_STREAMING_BUFFER; +#ifdef QLZ_MEMORY_SAFE + case 6: return 1; +#else + case 6: return 0; +#endif + case 7: return QLZ_VERSION_MAJOR; + case 8: return QLZ_VERSION_MINOR; + case 9: return QLZ_VERSION_REVISION; + } + return -1; +} + +#if QLZ_COMPRESSION_LEVEL == 1 +static int same(const unsigned char *src, size_t n) +{ + while(n > 0 && *(src + n) == *src) + n--; + return n == 0 ? 1 : 0; +} +#endif + +static void reset_table_compress(qlz_state_compress *state) +{ + int i; + for(i = 0; i < QLZ_HASH_VALUES; i++) + { +#if QLZ_COMPRESSION_LEVEL == 1 + state->hash[i].offset = 0; +#else + state->hash_counter[i] = 0; +#endif + } +} + +static void reset_table_decompress(qlz_state_decompress *state) +{ + int i; + (void)state; + (void)i; +#if QLZ_COMPRESSION_LEVEL == 2 + for(i = 0; i < QLZ_HASH_VALUES; i++) + { + state->hash_counter[i] = 0; + } +#endif +} + +static __inline ui32 hash_func(ui32 i) +{ +#if QLZ_COMPRESSION_LEVEL == 2 + return ((i >> 9) ^ (i >> 13) ^ i) & (QLZ_HASH_VALUES - 1); +#else + return ((i >> 12) ^ i) & (QLZ_HASH_VALUES - 1); +#endif +} + +static __inline ui32 fast_read(void const *src, ui32 bytes) +{ +#ifndef X86X64 + unsigned char *p = (unsigned char*)src; + switch (bytes) + { + case 4: + return(*p | *(p + 1) << 8 | *(p + 2) << 16 | *(p + 3) << 24); + case 3: + return(*p | *(p + 1) << 8 | *(p + 2) << 16); + case 2: + return(*p | *(p + 1) << 8); + case 1: + return(*p); + } + return 0; +#else + if (bytes >= 1 && bytes <= 4) + return *((ui32*)src); + else + return 0; +#endif +} + +static __inline ui32 hashat(const unsigned char *src) +{ + ui32 fetch, hash; + fetch = fast_read(src, 3); + hash = hash_func(fetch); + return hash; +} + +static __inline void fast_write(ui32 f, void *dst, size_t bytes) +{ +#ifndef X86X64 + unsigned char *p = (unsigned char*)dst; + + switch (bytes) + { + case 4: + *p = (unsigned char)f; + *(p + 1) = (unsigned char)(f >> 8); + *(p + 2) = (unsigned char)(f >> 16); + *(p + 3) = (unsigned char)(f >> 24); + return; + case 3: + *p = (unsigned char)f; + *(p + 1) = (unsigned char)(f >> 8); + *(p + 2) = (unsigned char)(f >> 16); + return; + case 2: + *p = (unsigned char)f; + *(p + 1) = (unsigned char)(f >> 8); + return; + case 1: + *p = (unsigned char)f; + return; + } +#else + switch (bytes) + { + case 4: + *((ui32*)dst) = f; + return; + case 3: + *((ui32*)dst) = f; + return; + case 2: + *((ui16 *)dst) = (ui16)f; + return; + case 1: + *((unsigned char*)dst) = (unsigned char)f; + return; + } +#endif +} + + +size_t qlz_size_decompressed(const char *source) +{ + ui32 n, r; + n = (((*source) & 2) == 2) ? 4 : 1; + r = fast_read(source + 1 + n, n); + r = r & (0xffffffff >> ((4 - n)*8)); + return r; +} + +size_t qlz_size_compressed(const char *source) +{ + ui32 n, r; + n = (((*source) & 2) == 2) ? 4 : 1; + r = fast_read(source + 1, n); + r = r & (0xffffffff >> ((4 - n)*8)); + return r; +} + +size_t qlz_size_header(const char *source) +{ + size_t n = 2*((((*source) & 2) == 2) ? 4 : 1) + 1; + return n; +} + + +static __inline void memcpy_up(unsigned char *dst, const unsigned char *src, ui32 n) +{ + // Caution if modifying memcpy_up! Overlap of dst and src must be special handled. +#ifndef X86X64 + unsigned char *end = dst + n; + while(dst < end) + { + *dst = *src; + dst++; + src++; + } +#else + ui32 f = 0; + do + { + *(ui32 *)(dst + f) = *(ui32 *)(src + f); + f += MINOFFSET + 1; + } + while (f < n); +#endif +} + +static __inline void update_hash(qlz_state_decompress *state, const unsigned char *s) +{ +#if QLZ_COMPRESSION_LEVEL == 1 + ui32 hash; + hash = hashat(s); + state->hash[hash].offset = s; + state->hash_counter[hash] = 1; +#elif QLZ_COMPRESSION_LEVEL == 2 + ui32 hash; + unsigned char c; + hash = hashat(s); + c = state->hash_counter[hash]; + state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = s; + c++; + state->hash_counter[hash] = c; +#endif + (void)state; + (void)s; +} + +#if QLZ_COMPRESSION_LEVEL <= 2 +static void update_hash_upto(qlz_state_decompress *state, unsigned char **lh, const unsigned char *max) +{ + while(*lh < max) + { + (*lh)++; + update_hash(state, *lh); + } +} +#endif + +static size_t qlz_compress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_compress *state) +{ + const unsigned char *last_byte = source + size - 1; + const unsigned char *src = source; + unsigned char *cword_ptr = destination; + unsigned char *dst = destination + CWORD_LEN; + ui32 cword_val = 1U << 31; + const unsigned char *last_matchstart = last_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END; + ui32 fetch = 0; + unsigned int lits = 0; + + (void) lits; + + if(src <= last_matchstart) + fetch = fast_read(src, 3); + + while(src <= last_matchstart) + { + if ((cword_val & 1) == 1) + { + // store uncompressed if compression ratio is too low + if (src > source + (size >> 1) && dst - destination > src - source - ((src - source) >> 5)) + return 0; + + fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN); + + cword_ptr = dst; + dst += CWORD_LEN; + cword_val = 1U << 31; + fetch = fast_read(src, 3); + } +#if QLZ_COMPRESSION_LEVEL == 1 + { + const unsigned char *o; + ui32 hash, cached; + + hash = hash_func(fetch); + cached = fetch ^ state->hash[hash].cache; + state->hash[hash].cache = fetch; + + o = state->hash[hash].offset + OFFSET_BASE; + state->hash[hash].offset = CAST(src - OFFSET_BASE); + +#ifdef X86X64 + if ((cached & 0xffffff) == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6)))) + { + if(cached != 0) + { +#else + if (cached == 0 && o != OFFSET_BASE && (src - o > MINOFFSET || (src == o + 1 && lits >= 3 && src > source + 3 && same(src - 3, 6)))) + { + if (*(o + 3) != *(src + 3)) + { +#endif + hash <<= 4; + cword_val = (cword_val >> 1) | (1U << 31); + fast_write((3 - 2) | hash, dst, 2); + src += 3; + dst += 2; + } + else + { + const unsigned char *old_src = src; + size_t matchlen; + hash <<= 4; + + cword_val = (cword_val >> 1) | (1U << 31); + src += 4; + + if(*(o + (src - old_src)) == *src) + { + src++; + if(*(o + (src - old_src)) == *src) + { + size_t q = last_byte - UNCOMPRESSED_END - (src - 5) + 1; + size_t remaining = q > 255 ? 255 : q; + src++; + while(*(o + (src - old_src)) == *src && (size_t)(src - old_src) < remaining) + src++; + } + } + + matchlen = src - old_src; + if (matchlen < 18) + { + fast_write((ui32)(matchlen - 2) | hash, dst, 2); + dst += 2; + } + else + { + fast_write((ui32)(matchlen << 16) | hash, dst, 3); + dst += 3; + } + } + fetch = fast_read(src, 3); + lits = 0; + } + else + { + lits++; + *dst = *src; + src++; + dst++; + cword_val = (cword_val >> 1); +#ifdef X86X64 + fetch = fast_read(src, 3); +#else + fetch = (fetch >> 8 & 0xffff) | (*(src + 2) << 16); +#endif + } + } +#elif QLZ_COMPRESSION_LEVEL >= 2 + { + const unsigned char *o, *offset2; + ui32 hash, matchlen, k, m, best_k = 0; + unsigned char c; + size_t remaining = (last_byte - UNCOMPRESSED_END - src + 1) > 255 ? 255 : (last_byte - UNCOMPRESSED_END - src + 1); + (void)best_k; + + + //hash = hashat(src); + fetch = fast_read(src, 3); + hash = hash_func(fetch); + + c = state->hash_counter[hash]; + + offset2 = state->hash[hash].offset[0]; + if(offset2 < src - MINOFFSET && c > 0 && ((fast_read(offset2, 3) ^ fetch) & 0xffffff) == 0) + { + matchlen = 3; + if(*(offset2 + matchlen) == *(src + matchlen)) + { + matchlen = 4; + while(*(offset2 + matchlen) == *(src + matchlen) && matchlen < remaining) + matchlen++; + } + } + else + matchlen = 0; + for(k = 1; k < QLZ_POINTERS && c > k; k++) + { + o = state->hash[hash].offset[k]; +#if QLZ_COMPRESSION_LEVEL == 3 + if(((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET) +#elif QLZ_COMPRESSION_LEVEL == 2 + if(*(src + matchlen) == *(o + matchlen) && ((fast_read(o, 3) ^ fetch) & 0xffffff) == 0 && o < src - MINOFFSET) +#endif + { + m = 3; + while(*(o + m) == *(src + m) && m < remaining) + m++; +#if QLZ_COMPRESSION_LEVEL == 3 + if ((m > matchlen) || (m == matchlen && o > offset2)) +#elif QLZ_COMPRESSION_LEVEL == 2 + if (m > matchlen) +#endif + { + offset2 = o; + matchlen = m; + best_k = k; + } + } + } + o = offset2; + state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src; + c++; + state->hash_counter[hash] = c; + +#if QLZ_COMPRESSION_LEVEL == 3 + if(matchlen > 2 && src - o < 131071) + { + ui32 u; + size_t offset = src - o; + + for(u = 1; u < matchlen; u++) + { + hash = hashat(src + u); + c = state->hash_counter[hash]++; + state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src + u; + } + + cword_val = (cword_val >> 1) | (1U << 31); + src += matchlen; + + if(matchlen == 3 && offset <= 63) + { + *dst = (unsigned char)(offset << 2); + dst++; + } + else if (matchlen == 3 && offset <= 16383) + { + ui32 f = (ui32)((offset << 2) | 1); + fast_write(f, dst, 2); + dst += 2; + } + else if (matchlen <= 18 && offset <= 1023) + { + ui32 f = ((matchlen - 3) << 2) | ((ui32)offset << 6) | 2; + fast_write(f, dst, 2); + dst += 2; + } + + else if(matchlen <= 33) + { + ui32 f = ((matchlen - 2) << 2) | ((ui32)offset << 7) | 3; + fast_write(f, dst, 3); + dst += 3; + } + else + { + ui32 f = ((matchlen - 3) << 7) | ((ui32)offset << 15) | 3; + fast_write(f, dst, 4); + dst += 4; + } + } + else + { + *dst = *src; + src++; + dst++; + cword_val = (cword_val >> 1); + } +#elif QLZ_COMPRESSION_LEVEL == 2 + + if(matchlen > 2) + { + cword_val = (cword_val >> 1) | (1U << 31); + src += matchlen; + + if (matchlen < 10) + { + ui32 f = best_k | ((matchlen - 2) << 2) | (hash << 5); + fast_write(f, dst, 2); + dst += 2; + } + else + { + ui32 f = best_k | (matchlen << 16) | (hash << 5); + fast_write(f, dst, 3); + dst += 3; + } + } + else + { + *dst = *src; + src++; + dst++; + cword_val = (cword_val >> 1); + } +#endif + } +#endif + } + while (src <= last_byte) + { + if ((cword_val & 1) == 1) + { + fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN); + cword_ptr = dst; + dst += CWORD_LEN; + cword_val = 1U << 31; + } +#if QLZ_COMPRESSION_LEVEL < 3 + if (src <= last_byte - 3) + { +#if QLZ_COMPRESSION_LEVEL == 1 + ui32 hash, fetch; + fetch = fast_read(src, 3); + hash = hash_func(fetch); + state->hash[hash].offset = CAST(src - OFFSET_BASE); + state->hash[hash].cache = fetch; +#elif QLZ_COMPRESSION_LEVEL == 2 + ui32 hash; + unsigned char c; + hash = hashat(src); + c = state->hash_counter[hash]; + state->hash[hash].offset[c & (QLZ_POINTERS - 1)] = src; + c++; + state->hash_counter[hash] = c; +#endif + } +#endif + *dst = *src; + src++; + dst++; + cword_val = (cword_val >> 1); + } + + while((cword_val & 1) != 1) + cword_val = (cword_val >> 1); + + fast_write((cword_val >> 1) | (1U << 31), cword_ptr, CWORD_LEN); + + // min. size must be 9 bytes so that the qlz_size functions can take 9 bytes as argument + return dst - destination < 9 ? 9 : dst - destination; +} + +static size_t qlz_decompress_core(const unsigned char *source, unsigned char *destination, size_t size, qlz_state_decompress *state, const unsigned char *history) +{ + const unsigned char *src = source + qlz_size_header((const char *)source); + unsigned char *dst = destination; + const unsigned char *last_destination_byte = destination + size - 1; + ui32 cword_val = 1; + const unsigned char *last_matchstart = last_destination_byte - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END; + unsigned char *last_hashed = destination - 1; + const unsigned char *last_source_byte = source + qlz_size_compressed((const char *)source) - 1; + static const ui32 bitlut[16] = {4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0}; + + (void) last_source_byte; + (void) last_hashed; + (void) state; + (void) history; + + for(;;) + { + ui32 fetch; + + if (cword_val == 1) + { +#ifdef QLZ_MEMORY_SAFE + if(src + CWORD_LEN - 1 > last_source_byte) + return 0; +#endif + cword_val = fast_read(src, CWORD_LEN); + src += CWORD_LEN; + } + +#ifdef QLZ_MEMORY_SAFE + if(src + 4 - 1 > last_source_byte) + return 0; +#endif + + fetch = fast_read(src, 4); + + if ((cword_val & 1) == 1) + { + ui32 matchlen; + const unsigned char *offset2; + +#if QLZ_COMPRESSION_LEVEL == 1 + ui32 hash; + cword_val = cword_val >> 1; + hash = (fetch >> 4) & 0xfff; + offset2 = (const unsigned char *)(size_t)state->hash[hash].offset; + + if((fetch & 0xf) != 0) + { + matchlen = (fetch & 0xf) + 2; + src += 2; + } + else + { + matchlen = *(src + 2); + src += 3; + } + +#elif QLZ_COMPRESSION_LEVEL == 2 + ui32 hash; + unsigned char c; + cword_val = cword_val >> 1; + hash = (fetch >> 5) & 0x7ff; + c = (unsigned char)(fetch & 0x3); + offset2 = state->hash[hash].offset[c]; + + if((fetch & (28)) != 0) + { + matchlen = ((fetch >> 2) & 0x7) + 2; + src += 2; + } + else + { + matchlen = *(src + 2); + src += 3; + } + +#elif QLZ_COMPRESSION_LEVEL == 3 + ui32 offset; + cword_val = cword_val >> 1; + if ((fetch & 3) == 0) + { + offset = (fetch & 0xff) >> 2; + matchlen = 3; + src++; + } + else if ((fetch & 2) == 0) + { + offset = (fetch & 0xffff) >> 2; + matchlen = 3; + src += 2; + } + else if ((fetch & 1) == 0) + { + offset = (fetch & 0xffff) >> 6; + matchlen = ((fetch >> 2) & 15) + 3; + src += 2; + } + else if ((fetch & 127) != 3) + { + offset = (fetch >> 7) & 0x1ffff; + matchlen = ((fetch >> 2) & 0x1f) + 2; + src += 3; + } + else + { + offset = (fetch >> 15); + matchlen = ((fetch >> 7) & 255) + 3; + src += 4; + } + + offset2 = dst - offset; +#endif + +#ifdef QLZ_MEMORY_SAFE + if(offset2 < history || offset2 > dst - MINOFFSET - 1) + return 0; + + if(matchlen > (ui32)(last_destination_byte - dst - UNCOMPRESSED_END + 1)) + return 0; +#endif + + memcpy_up(dst, offset2, matchlen); + dst += matchlen; + +#if QLZ_COMPRESSION_LEVEL <= 2 + update_hash_upto(state, &last_hashed, dst - matchlen); + last_hashed = dst - 1; +#endif + } + else + { + if (dst < last_matchstart) + { + unsigned int n = bitlut[cword_val & 0xf]; +#ifdef X86X64 + *(ui32 *)dst = *(ui32 *)src; +#else + memcpy_up(dst, src, 4); +#endif + cword_val = cword_val >> n; + dst += n; + src += n; +#if QLZ_COMPRESSION_LEVEL <= 2 + update_hash_upto(state, &last_hashed, dst - 3); +#endif + } + else + { + while(dst <= last_destination_byte) + { + if (cword_val == 1) + { + src += CWORD_LEN; + cword_val = 1U << 31; + } +#ifdef QLZ_MEMORY_SAFE + if(src >= last_source_byte + 1) + return 0; +#endif + *dst = *src; + dst++; + src++; + cword_val = cword_val >> 1; + } + +#if QLZ_COMPRESSION_LEVEL <= 2 + update_hash_upto(state, &last_hashed, last_destination_byte - 3); // todo, use constant +#endif + return size; + } + + } + } +} + +size_t qlz_compress(const void *source, char *destination, size_t size, qlz_state_compress *state) +{ + size_t r; + ui32 compressed; + size_t base; + + if(size == 0 || size > 0xffffffff - 400) + return 0; + + if(size < 216) + base = 3; + else + base = 9; + +#if QLZ_STREAMING_BUFFER > 0 + if (state->stream_counter + size - 1 >= QLZ_STREAMING_BUFFER) +#endif + { + reset_table_compress(state); + r = base + qlz_compress_core((const unsigned char *)source, (unsigned char*)destination + base, size, state); +#if QLZ_STREAMING_BUFFER > 0 + reset_table_compress(state); +#endif + if(r == base) + { + memcpy(destination + base, source, size); + r = size + base; + compressed = 0; + } + else + { + compressed = 1; + } + state->stream_counter = 0; + } +#if QLZ_STREAMING_BUFFER > 0 + else + { + unsigned char *src = state->stream_buffer + state->stream_counter; + + memcpy(src, source, size); + r = base + qlz_compress_core(src, (unsigned char*)destination + base, size, state); + + if(r == base) + { + memcpy(destination + base, src, size); + r = size + base; + compressed = 0; + reset_table_compress(state); + } + else + { + compressed = 1; + } + state->stream_counter += size; + } +#endif + if(base == 3) + { + *destination = (unsigned char)(0 | compressed); + *(destination + 1) = (unsigned char)r; + *(destination + 2) = (unsigned char)size; + } + else + { + *destination = (unsigned char)(2 | compressed); + fast_write((ui32)r, destination + 1, 4); + fast_write((ui32)size, destination + 5, 4); + } + + *destination |= (QLZ_COMPRESSION_LEVEL << 2); + *destination |= (1 << 6); + *destination |= ((QLZ_STREAMING_BUFFER == 0 ? 0 : (QLZ_STREAMING_BUFFER == 100000 ? 1 : (QLZ_STREAMING_BUFFER == 1000000 ? 2 : 3))) << 4); + +// 76543210 +// 01SSLLHC + + return r; +} + +size_t qlz_decompress(const char *source, void *destination, qlz_state_decompress *state) +{ + size_t dsiz = qlz_size_decompressed(source); + +#if QLZ_STREAMING_BUFFER > 0 + if (state->stream_counter + qlz_size_decompressed(source) - 1 >= QLZ_STREAMING_BUFFER) +#endif + { + if((*source & 1) == 1) + { + reset_table_decompress(state); + dsiz = qlz_decompress_core((const unsigned char *)source, (unsigned char *)destination, dsiz, state, (const unsigned char *)destination); + } + else + { + memcpy(destination, source + qlz_size_header(source), dsiz); + } + state->stream_counter = 0; + reset_table_decompress(state); + } +#if QLZ_STREAMING_BUFFER > 0 + else + { + unsigned char *dst = state->stream_buffer + state->stream_counter; + if((*source & 1) == 1) + { + dsiz = qlz_decompress_core((const unsigned char *)source, dst, dsiz, state, (const unsigned char *)state->stream_buffer); + } + else + { + memcpy(dst, source + qlz_size_header(source), dsiz); + reset_table_decompress(state); + } + memcpy(destination, dst, dsiz); + state->stream_counter += dsiz; + } +#endif + return dsiz; +} + diff --git a/source/quicklz/quicklz.h b/source/quicklz/quicklz.h new file mode 100644 index 0000000..131a25b --- /dev/null +++ b/source/quicklz/quicklz.h @@ -0,0 +1,151 @@ +#ifndef QLZ_HEADER +#define QLZ_HEADER + +// Fast data compression library +// Copyright (C) 2006-2011 Lasse Mikkel Reinhold +// lar@quicklz.com +// +// QuickLZ can be used for free under the GPL 1, 2 or 3 license (where anything +// released into public must be open source) or under a commercial license if such +// has been acquired (see http://www.quicklz.com/order.html). The commercial license +// does not cover derived or ported versions created by third parties under GPL. + +// You can edit following user settings. Data must be decompressed with the same +// setting of QLZ_COMPRESSION_LEVEL and QLZ_STREAMING_BUFFER as it was compressed +// (see manual). If QLZ_STREAMING_BUFFER > 0, scratch buffers must be initially +// zeroed out (see manual). First #ifndef makes it possible to define settings from +// the outside like the compiler command line. + +// 1.5.0 final + +#ifndef QLZ_COMPRESSION_LEVEL + + // 1 gives fastest compression speed. 3 gives fastest decompression speed and best + // compression ratio. + //#define QLZ_COMPRESSION_LEVEL 1 + //#define QLZ_COMPRESSION_LEVEL 2 + #define QLZ_COMPRESSION_LEVEL 3 + + // If > 0, zero out both states prior to first call to qlz_compress() or qlz_decompress() + // and decompress packets in the same order as they were compressed + #define QLZ_STREAMING_BUFFER 0 + //#define QLZ_STREAMING_BUFFER 1000 + //#define QLZ_STREAMING_BUFFER 100000 + //#define QLZ_STREAMING_BUFFER 1000000 + + // Guarantees that decompression of corrupted data cannot crash. Decreases decompression + // speed 10-20%. Compression speed not affected. + //#define QLZ_MEMORY_SAFE +#endif + +#define QLZ_VERSION_MAJOR 1 +#define QLZ_VERSION_MINOR 5 +#define QLZ_VERSION_REVISION 0 + +// Using size_t, memset() and memcpy() +#include + +// Verify compression level +#if QLZ_COMPRESSION_LEVEL != 1 && QLZ_COMPRESSION_LEVEL != 2 && QLZ_COMPRESSION_LEVEL != 3 +#error QLZ_COMPRESSION_LEVEL must be 1, 2 or 3 +#endif + +typedef unsigned int ui32; +typedef unsigned short int ui16; + +// Decrease QLZ_POINTERS for level 3 to increase compression speed. Do not touch any other values! +#if QLZ_COMPRESSION_LEVEL == 1 +#define QLZ_POINTERS 1 +#define QLZ_HASH_VALUES 4096 +#elif QLZ_COMPRESSION_LEVEL == 2 +#define QLZ_POINTERS 4 +#define QLZ_HASH_VALUES 2048 +#elif QLZ_COMPRESSION_LEVEL == 3 +#define QLZ_POINTERS 16 +#define QLZ_HASH_VALUES 4096 +#endif + +// Detect if pointer size is 64-bit. It's not fatal if some 64-bit target is not detected because this is only for adding an optional 64-bit optimization. +#if defined _LP64 || defined __LP64__ || defined __64BIT__ || _ADDR64 || defined _WIN64 || defined __arch64__ || __WORDSIZE == 64 || (defined __sparc && defined __sparcv9) || defined __x86_64 || defined __amd64 || defined __x86_64__ || defined _M_X64 || defined _M_IA64 || defined __ia64 || defined __IA64__ + #define QLZ_PTR_64 +#endif + +// hash entry +typedef struct +{ +#if QLZ_COMPRESSION_LEVEL == 1 + ui32 cache; +#if defined QLZ_PTR_64 && QLZ_STREAMING_BUFFER == 0 + unsigned int offset; +#else + const unsigned char *offset; +#endif +#else + const unsigned char *offset[QLZ_POINTERS]; +#endif + +} qlz_hash_compress; + +typedef struct +{ +#if QLZ_COMPRESSION_LEVEL == 1 + const unsigned char *offset; +#else + const unsigned char *offset[QLZ_POINTERS]; +#endif +} qlz_hash_decompress; + + +// states +typedef struct +{ + #if QLZ_STREAMING_BUFFER > 0 + unsigned char stream_buffer[QLZ_STREAMING_BUFFER]; + #endif + size_t stream_counter; + qlz_hash_compress hash[QLZ_HASH_VALUES]; + unsigned char hash_counter[QLZ_HASH_VALUES]; +} qlz_state_compress; + + +#if QLZ_COMPRESSION_LEVEL == 1 || QLZ_COMPRESSION_LEVEL == 2 + typedef struct + { +#if QLZ_STREAMING_BUFFER > 0 + unsigned char stream_buffer[QLZ_STREAMING_BUFFER]; +#endif + qlz_hash_decompress hash[QLZ_HASH_VALUES]; + unsigned char hash_counter[QLZ_HASH_VALUES]; + size_t stream_counter; + } qlz_state_decompress; +#elif QLZ_COMPRESSION_LEVEL == 3 + typedef struct + { +#if QLZ_STREAMING_BUFFER > 0 + unsigned char stream_buffer[QLZ_STREAMING_BUFFER]; +#endif +#if QLZ_COMPRESSION_LEVEL <= 2 + qlz_hash_decompress hash[QLZ_HASH_VALUES]; +#endif + size_t stream_counter; + } qlz_state_decompress; +#endif + + +#if defined (__cplusplus) +extern "C" { +#endif + +// Public functions of QuickLZ +size_t qlz_size_decompressed(const char *source); +size_t qlz_size_compressed(const char *source); +size_t qlz_compress(const void *source, char *destination, size_t size, qlz_state_compress *state); +size_t qlz_decompress(const char *source, void *destination, qlz_state_decompress *state); +int qlz_get_setting(int setting); + +#if defined (__cplusplus) +} +#endif + +#endif +