first version

This commit is contained in:
Martins Mozeiko
2017-09-23 10:18:46 -07:00
commit 0a7c2955d2
15 changed files with 1556 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
pkg2zip
pkg2zip.exe
*.d
*.o
*.obj
*.pdb
+24
View File
@@ -0,0 +1,24 @@
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>
+45
View File
@@ -0,0 +1,45 @@
# pkg2zip
Utility that decrypts PlayStation Vita pkg file and creates zip package.
Optionally embedds [NoNpDrm](https://github.com/TheOfficialFloW/NoNpDrm) license into work.bin file. You must provide license key.
# Features
* **portable**, written in cross-platform C code, runs on Windows, GNU/Linux, macOS (system dependent functionality is isolated in sys.c file).
* **small**, uses zero dynamic memory allocations and has no external library dependencies.
* **fast**, uses AESNI hardware accelerated AES decryption if supported by CPU (requires [AESNI](https://en.wikipedia.org/wiki/AES_instruction_set) and [SSSE3](https://en.wikipedia.org/wiki/SSSE3) instructions).
* **simple**, creates zip package with same folder structure that Vita expects (just drag & drop all file from zip archive to ux0:). Zip file is created directly from pkg without any intermediate temporary files.
Limitations:
* currently works only for main application pkg files, no DLC.
# Usage
Execute `pkg2zip package.pkg` to create `title [id] [region].zip` file. Title, ID and region is automatically detected from pkg file.
If you have license key (32 hex characters) you can execute `pkg2zip package.pkg hexkey` to embed key into work.bin file.
# Download
Get latest Windows binaries [here](https://github.com/mmozeiko/pkg2zip/releases).
# Building
Execute `make` if you are on GNU/Linux or macOS.
On Windows you can build either with MinGW (get [MinGW-w64](http://www.msys2.org/)) or [Visual Studio 2017 Community Edition](https://www.visualstudio.com/vs/community/).
* for MinGW make sure you have make installed, and then execute `mingw32-make`
* for Visual Studio run `build.cmd`
# Alternatives
* https://github.com/RikuKH3/unpkg_vita
* https://github.com/St4rk/PkgDecrypt
* https://github.com/TheRadziu/PkgDecrypt
# License
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.
+12
View File
@@ -0,0 +1,12 @@
@echo off
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat" -arch=amd64 -host_arch=amd64
set CL=/nologo /errorReport:none /Gm- /GF /GS- /MT /W4
set LINK=/errorReport:none /INCREMENTAL:NO
set CL=%CL% /Ox
rem set CL=%CL% /Od /Zi
rem set LINK=%LINK% /DEBUG
cl.exe pkg2zip*.c /Fepkg2zip.exe
+35
View File
@@ -0,0 +1,35 @@
ifeq ($(OS),Windows_NT)
RM := del /q
EXE := .exe
else
EXE :=
endif
BIN=pkg2zip${EXE}
SRC=${wildcard pkg2zip*.c}
OBJ=${SRC:.c=.o}
DEP=${SRC:.c=.d}
CFLAGS=-pipe -fvisibility=hidden -Wall -Wextra -DNDEBUG -O2
LDFLAGS=-s
.PHONY: all clean
all: ${BIN}
clean:
@${RM} ${BIN} ${OBJ} ${DEP}
${BIN}: ${OBJ}
@echo [L] $@
@${CC} ${LDFLAGS} -o $@ $^
%_x86.o: %_x86.c
@echo [C] $<
@${CC} ${CFLAGS} -maes -mssse3 -MMD -c -o $@ $<
%.o: %.c
@echo [C] $<
@${CC} ${CFLAGS} -MMD -c -o $@ $<
-include ${DEP}
+351
View File
@@ -0,0 +1,351 @@
#include "pkg2zip_aes.h"
#include "pkg2zip_zip.h"
#include "pkg2zip_utils.h"
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static const uint8_t pkg_psp_key[] = {
0x07, 0xf2, 0xc6, 0x82, 0x90, 0xb5, 0x0d, 0x2c, 0x33, 0x81, 0x8d, 0x70, 0x9b, 0x60, 0xe6, 0x2b,
};
static const uint8_t pkg_vita_2[] = {
0xe3, 0x1a, 0x70, 0xc9, 0xce, 0x1d, 0xd7, 0x2b, 0xf3, 0xc0, 0x62, 0x29, 0x63, 0xf2, 0xec, 0xcb,
};
static const uint8_t pkg_vita_3[] = {
0x42, 0x3a, 0xca, 0x3a, 0x2b, 0xd5, 0x64, 0x9f, 0x96, 0x86, 0xab, 0xad, 0x6f, 0xd8, 0x80, 0x1f,
};
static const uint8_t pkg_vita_4[] = {
0xaf, 0x07, 0xfd, 0x59, 0x65, 0x25, 0x27, 0xba, 0xf1, 0x33, 0x89, 0x66, 0x8b, 0x17, 0xd9, 0xea,
};
static const uint8_t rif_header[] = {
0, 1, 0, 1, 0, 1, 0, 2, 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01,
};
static void get_title(const uint8_t* data, size_t data_size, char* title)
{
if (data_size < 16)
{
fatal("ERROR: wrong param.sfo size\n");
}
uint32_t magic = get32le(data);
if (magic != 0x46535000)
{
fatal("ERROR: incorrect param.sfo signature\n");
}
// https://github.com/TheOfficialFloW/VitaShell/blob/1.74/sfo.h#L29
uint32_t keys = get32le(data + 8);
uint32_t values = get32le(data + 12);
size_t count = get32le(data + 16);
int index = -1;
for (size_t i=0; i<count; i++)
{
if (i*16 + 20 + 2 > data_size)
{
fatal("ERROR: truncated param.sfo size\n");
}
char* key = (char*)data + keys + get16le(data + i*16 + 20);
if (strcmp(key, "TITLE") == 0)
{
if (index < 0)
{
index = (int)i;
}
}
else if (strcmp(key, "STITLE") == 0)
{
index = (int)i;
break;
}
}
if (index >= 0)
{
const char* value = (char*)data + values + get32le(data + index*16 + 20 + 12);
size_t i;
size_t max = 255;
for (i=0; i<max && *value; i++, value++)
{
if (*value >= 32 && *value < 127 && strchr("<>\"/\\|?*", *value) == NULL)
{
if (*value == ':')
{
*title++ = ' ';
*title++ = '-';
max--;
}
else
{
*title++ = *value;
}
}
else if (*value == 10)
{
*title++ = ' ';
}
}
*title = 0;
}
}
static const char* get_region(const char* id)
{
if (memcmp(id, "PCSE", 4) == 0 || memcmp(id, "PCSA", 4) == 0)
{
return "USA";
}
else if (memcmp(id, "PCSF", 4) == 0 || memcmp(id, "PCSB", 4) == 0)
{
return "EUR";
}
else if (memcmp(id, "PCSC", 4) == 0 || memcmp(id, "VCJS", 4) == 0 ||
memcmp(id, "PCSG", 4) == 0 || memcmp(id, "VLJS", 4) == 0 ||
memcmp(id, "VLJM", 4) == 0)
{
return "JPN";
}
else if (memcmp(id, "VCAS", 4) == 0 || memcmp(id, "PCSH", 4) == 0 ||
memcmp(id, "VLAS", 4) == 0)
{
return "ASA";
}
else
{
return "unknown region";
}
}
int main(int argc, char* argv[])
{
printf("pkg2zip v1.0\n");
if (argc < 2 || argc > 3)
{
fatal("Usage: %s file.pkg [NoNpDrmKey]\n", argv[0]);
}
printf("[*] loading...\n");
uint64_t pkg_size;
sys_file pkg = sys_open(argv[1], &pkg_size);
uint8_t pkg_header[256];
sys_read(pkg, 0, pkg_header, sizeof(pkg_header));
if (get32be(pkg_header) != 0x7f504b47)
{
fatal("ERROR: pkg file is corrupted\n");
}
uint32_t item_count = get32be(pkg_header + 20);
uint64_t enc_offset = get64be(pkg_header + 32);
if (item_count > ZIP_MAX_FILES)
{
fatal("ERROR: pkg has too many files");
}
if (pkg_size < enc_offset + item_count * 32)
{
fatal("ERROR: pkg file is truncated\n");
}
const char* id = (char*)pkg_header + 0x37;
const uint8_t* iv = pkg_header + 0x70;
int key_type = pkg_header[0xe7] & 7;
uint8_t main_key[16];
if (key_type == 1)
{
memcpy(main_key, pkg_psp_key, sizeof(main_key));
}
else if (key_type == 2)
{
aes128_key key;
aes128_init(&key, pkg_vita_2);
aes128_ecb_encrypt(&key, iv, main_key);
}
else if (key_type == 3)
{
aes128_key key;
aes128_init(&key, pkg_vita_3);
aes128_ecb_encrypt(&key, iv, main_key);
}
else if (key_type == 4)
{
aes128_key key;
aes128_init(&key, pkg_vita_4);
aes128_ecb_encrypt(&key, iv, main_key);
}
uint8_t items[32 * ZIP_MAX_FILES];
uint32_t items_size = 32 * item_count;
sys_read(pkg, enc_offset, items, items_size);
uint8_t temp_flag = 0;
size_t head_size = enc_offset;
char title[256] = "unknown title";
char path[1024];
snprintf(path, sizeof(path), ".~%.*s.zip", 9, id);
printf("[*] creating temporary '%s' archive\n", path);
zip z;
zip_create(&z, path);
snprintf(path, sizeof(path), "app/");
zip_add_folder(&z, path);
snprintf(path, sizeof(path), "app/%.9s/", id);
zip_add_folder(&z, path);
printf("[*] decrypting...\n");
aes128_key key;
aes128_init(&key, main_key);
aes128_ctr_xor(&key, iv, 0, items, items_size);
for (uint32_t item_index=0; item_index<item_count; item_index++)
{
uint8_t* item = items + item_index * 32;
uint32_t name_offset = get32be(item + 0);
uint32_t name_size = get32be(item + 4);
uint64_t data_offset = get64be(item + 8);
uint64_t data_size = get64be(item + 16);
uint8_t flags = item[27];
if (item_index == 0)
{
head_size += data_offset;
}
assert(name_offset % 16 == 0);
assert(data_offset % 16 == 0);
if (pkg_size < enc_offset + name_offset + name_size ||
pkg_size < enc_offset + data_offset + data_size)
{
fatal("ERROR: pkg file is truncated\n");
}
char name[ZIP_MAX_FILENAME];
sys_read(pkg, enc_offset + name_offset, name, name_size);
aes128_ctr_xor(&key, iv, name_offset/16, (uint8_t*)name, name_size);
name[name_size] = 0;
printf("[%u/%u] %s\n", item_index+1, item_count, name);
if (flags == 4 || flags == 18)
{
snprintf(path, sizeof(path), "app/%.9s/%s/", id, name);
zip_add_folder(&z, path);
}
else if (flags <= 3 || (flags >= 14 && flags <= 17) ||
flags == 19 || flags == 21 || flags == 22)
{
if (memcmp("sce_pfs/pflist", name, name_size) != 0)
{
snprintf(path, sizeof(path), "app/%.9s/%s", id, name);
uint64_t offset = data_offset;
zip_begin_file(&z, path);
while (data_size != 0)
{
uint8_t buffer[1 << 16];
uint32_t size = (uint32_t)min64(data_size, sizeof(buffer));
sys_read(pkg, enc_offset + offset, buffer, size);
aes128_ctr_xor(&key, iv, offset / 16, buffer, size);
// process data at beginning of file
if (offset == data_offset)
{
if (memcmp("sce_sys/package/temp.bin", name, name_size) == 0)
{
// https://github.com/TheOfficialFloW/NoNpDrm/blob/v1.1/src/main.c#L116
uint32_t sku_flag = get32be(buffer + 252);
if (sku_flag == 1 || sku_flag == 3)
{
temp_flag = 3;
}
}
else if (memcmp("sce_sys/param.sfo", name, name_size) == 0)
{
get_title(buffer, size, title);
}
}
zip_write_file(&z, buffer, size);
offset += size;
data_size -= size;
}
zip_end_file(&z);
}
}
}
printf("[*] creating head.bin\n");
snprintf(path, sizeof(path), "app/%.9s/sce_sys/package/head.bin", id);
zip_begin_file(&z, path);
while (head_size != 0)
{
uint8_t buffer[1 << 16];
uint32_t size = (uint32_t)min64(head_size, sizeof(buffer));
sys_read(pkg, 0, buffer, size);
zip_write_file(&z, buffer, size);
head_size -= size;
}
zip_end_file(&z);
printf("[*] creating tail.bin\n");
snprintf(path, sizeof(path), "app/%.9s/sce_sys/package/tail.bin", id);
uint8_t tail[480];
zip_begin_file(&z, path);
sys_read(pkg, pkg_size - sizeof(tail), tail, sizeof(tail));
zip_write_file(&z, tail, sizeof(tail));
zip_end_file(&z);
printf("[*] creating work.bin\n");
snprintf(path, sizeof(path), "app/%.9s/sce_sys/package/work.bin", id);
// https://github.com/TheOfficialFloW/NoNpDrm/blob/v1.1/src/main.c#L42
uint8_t work[512] = { 0 };
memcpy(work, rif_header, sizeof(rif_header));
memcpy(work + 0x10, pkg_header + 0x30, 0x30);
if (argc == 3)
{
printf("[*] embedding '%s' key\n", argv[2]);
get_hex_bytes16(argv[2], work + 0x50);
}
work[255] = temp_flag;
zip_begin_file(&z, path);
zip_write_file(&z, work, sizeof(work));
zip_end_file(&z);
zip_close(&z);
snprintf(path, sizeof(path), ".~%.*s.zip", 9, id);
char target[1024];
snprintf(target, sizeof(target), "%s [%.9s] [%s].zip", title, id, get_region(id));
printf("[*] renaming to '%s'\n", target);
sys_rename(path, target);
printf("[*] done!\n");
}
+250
View File
@@ -0,0 +1,250 @@
#include "pkg2zip_aes.h"
#include "pkg2zip_utils.h"
#if defined(_MSC_VER)
#define PLATFORM_SUPPORTS_AESNI 1
#include <intrin.h>
static void get_cpuid(uint32_t level, uint32_t* arr)
{
__cpuidex((int*)arr, level, 0);
}
#elif defined(__x86_64__) || defined(__i386__)
#define PLATFORM_SUPPORTS_AESNI 1
#include <cpuid.h>
static void get_cpuid(uint32_t level, uint32_t* arr)
{
__cpuid_count(level, 0, arr[0], arr[1], arr[2], arr[3]);
}
#else
#define PLATFORM_SUPPORTS_AESNI 0
#endif
#if PLATFORM_SUPPORTS_AESNI
static int aes128_supported_x86()
{
static int init = 0;
static int supported;
if (!init)
{
init = 1;
uint32_t a[4];
get_cpuid(0, a);
if (a[0] >= 1)
{
get_cpuid(1, a);
supported = ((a[2] & (1 << 9)) && (a[2] & (1 << 25)));
}
}
return supported;
}
void aes128_init_x86(aes128_key* context, const uint8_t* key);
void aes128_ecb_encrypt_x86(const aes128_key* context, const uint8_t* input, uint8_t* output);
void aes128_ctr_xor_x86(const aes128_key* context, const uint8_t* iv, uint8_t* buffer, size_t size);
#endif
static const uint8_t rcon[] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36,
};
static const uint8_t Te[256] = {
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
};
static const uint32_t TE[256] = {
0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,
0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,
0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b,
0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f,
0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5,
0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f,
0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497,
0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed,
0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594,
0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3,
0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d,
0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739,
0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883,
0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76,
0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4,
0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b,
0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0,
0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651,
0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85,
0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12,
0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9,
0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7,
0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8,
0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a,
};
static uint8_t byte32(uint32_t x, int n)
{
return (uint8_t)(x >> (8 * n));
}
static uint32_t setup_mix(uint32_t temp)
{
return (Te[byte32(temp, 2)] << 24) ^ (Te[byte32(temp, 1)] << 16) ^ (Te[byte32(temp, 0)] << 8) ^ Te[byte32(temp, 3)];
}
static uint32_t ror32(uint32_t x, int n)
{
return (x >> n) | (x << (32 - n));
}
void aes128_init(aes128_key* context, const uint8_t* key)
{
#if PLATFORM_SUPPORTS_AESNI
if (aes128_supported_x86())
{
aes128_init_x86(context, key);
return;
}
#endif
uint32_t* rk = context->key;
rk[0] = get32be(key + 0);
rk[1] = get32be(key + 4);
rk[2] = get32be(key + 8);
rk[3] = get32be(key + 12);
for (size_t i=0; i<10; i++)
{
uint32_t temp = rk[3];
rk[4] = rk[0] ^ setup_mix(temp) ^ (rcon[i] << 24);
rk[5] = rk[1] ^ rk[4];
rk[6] = rk[2] ^ rk[5];
rk[7] = rk[3] ^ rk[6];
rk += 4;
}
}
static void aes128_encrypt(const aes128_key* context, const uint8_t* input, uint8_t* output)
{
uint32_t t0, t1, t2, t3;
const uint32_t* key = context->key;
uint32_t s0 = get32be(input + 0) ^ *key++;
uint32_t s1 = get32be(input + 4) ^ *key++;
uint32_t s2 = get32be(input + 8) ^ *key++;
uint32_t s3 = get32be(input + 12) ^ *key++;
for (size_t i = 0; i<4; i++)
{
t0 = TE[byte32(s0, 3)] ^ ror32(TE[byte32(s1, 2)], 8) ^ ror32(TE[byte32(s2, 1)], 16) ^ ror32(TE[byte32(s3, 0)], 24) ^ *key++;
t1 = TE[byte32(s1, 3)] ^ ror32(TE[byte32(s2, 2)], 8) ^ ror32(TE[byte32(s3, 1)], 16) ^ ror32(TE[byte32(s0, 0)], 24) ^ *key++;
t2 = TE[byte32(s2, 3)] ^ ror32(TE[byte32(s3, 2)], 8) ^ ror32(TE[byte32(s0, 1)], 16) ^ ror32(TE[byte32(s1, 0)], 24) ^ *key++;
t3 = TE[byte32(s3, 3)] ^ ror32(TE[byte32(s0, 2)], 8) ^ ror32(TE[byte32(s1, 1)], 16) ^ ror32(TE[byte32(s2, 0)], 24) ^ *key++;
s0 = TE[byte32(t0, 3)] ^ ror32(TE[byte32(t1, 2)], 8) ^ ror32(TE[byte32(t2, 1)], 16) ^ ror32(TE[byte32(t3, 0)], 24) ^ *key++;
s1 = TE[byte32(t1, 3)] ^ ror32(TE[byte32(t2, 2)], 8) ^ ror32(TE[byte32(t3, 1)], 16) ^ ror32(TE[byte32(t0, 0)], 24) ^ *key++;
s2 = TE[byte32(t2, 3)] ^ ror32(TE[byte32(t3, 2)], 8) ^ ror32(TE[byte32(t0, 1)], 16) ^ ror32(TE[byte32(t1, 0)], 24) ^ *key++;
s3 = TE[byte32(t3, 3)] ^ ror32(TE[byte32(t0, 2)], 8) ^ ror32(TE[byte32(t1, 1)], 16) ^ ror32(TE[byte32(t2, 0)], 24) ^ *key++;
}
t0 = TE[byte32(s0, 3)] ^ ror32(TE[byte32(s1, 2)], 8) ^ ror32(TE[byte32(s2, 1)], 16) ^ ror32(TE[byte32(s3, 0)], 24) ^ *key++;
t1 = TE[byte32(s1, 3)] ^ ror32(TE[byte32(s2, 2)], 8) ^ ror32(TE[byte32(s3, 1)], 16) ^ ror32(TE[byte32(s0, 0)], 24) ^ *key++;
t2 = TE[byte32(s2, 3)] ^ ror32(TE[byte32(s3, 2)], 8) ^ ror32(TE[byte32(s0, 1)], 16) ^ ror32(TE[byte32(s1, 0)], 24) ^ *key++;
t3 = TE[byte32(s3, 3)] ^ ror32(TE[byte32(s0, 2)], 8) ^ ror32(TE[byte32(s1, 1)], 16) ^ ror32(TE[byte32(s2, 0)], 24) ^ *key++;
s0 = (Te[byte32(t0, 3)] << 24) ^ (Te[byte32(t1, 2)] << 16) ^ (Te[byte32(t2, 1)] << 8) ^ (Te[byte32(t3, 0)]) ^ *key++;
s1 = (Te[byte32(t1, 3)] << 24) ^ (Te[byte32(t2, 2)] << 16) ^ (Te[byte32(t3, 1)] << 8) ^ (Te[byte32(t0, 0)]) ^ *key++;
s2 = (Te[byte32(t2, 3)] << 24) ^ (Te[byte32(t3, 2)] << 16) ^ (Te[byte32(t0, 1)] << 8) ^ (Te[byte32(t1, 0)]) ^ *key++;
s3 = (Te[byte32(t3, 3)] << 24) ^ (Te[byte32(t0, 2)] << 16) ^ (Te[byte32(t1, 1)] << 8) ^ (Te[byte32(t2, 0)]) ^ *key++;
set32be(output + 0, s0);
set32be(output + 4, s1);
set32be(output + 8, s2);
set32be(output + 12, s3);
}
void aes128_ecb_encrypt(const aes128_key* context, const uint8_t* input, uint8_t* output)
{
#if PLATFORM_SUPPORTS_AESNI
if (aes128_supported_x86())
{
aes128_ecb_encrypt_x86(context, input, output);
return;
}
#endif
aes128_encrypt(context, input, output);
}
static void ctr_add(uint8_t* counter, uint64_t n)
{
for (int i=15; i>=0; i--)
{
n = n + counter[i];
counter[i] = (uint8_t)n;
n >>= 8;
}
}
void aes128_ctr_xor(const aes128_key* context, const uint8_t* iv, uint64_t block, uint8_t* buffer, size_t size)
{
uint8_t tmp[16];
uint8_t counter[16];
for (uint32_t i=0; i<16; i++)
{
counter[i] = iv[i];
}
ctr_add(counter, block);
#if PLATFORM_SUPPORTS_AESNI
if (aes128_supported_x86())
{
aes128_ctr_xor_x86(context, counter, buffer, size);
return;
}
#endif
while (size >= 16)
{
aes128_encrypt(context, counter, tmp);
for (uint32_t i=0; i<16; i++)
{
*buffer++ ^= tmp[i];
}
ctr_add(counter, 1);
size -= 16;
}
if (size != 0)
{
aes128_encrypt(context, counter, tmp);
for (size_t i=0; i<size; i++)
{
*buffer++ ^= tmp[i];
}
}
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#if defined(_MSC_VER)
# define MSVC_ALIGN(x) __declspec(align(x))
# define GCC_ALIGN(x)
#else
# define MSVC_ALIGN(x)
# define GCC_ALIGN(x) __attribute__((aligned(x)))
#endif
typedef struct {
MSVC_ALIGN(16) uint32_t key[44] GCC_ALIGN(16);
} aes128_key;
void aes128_init(aes128_key* context, const uint8_t* key);
void aes128_ecb_encrypt(const aes128_key* context, const uint8_t* input, uint8_t* output);
void aes128_ctr_xor(const aes128_key* context, const uint8_t* iv, uint64_t block, uint8_t* buffer, size_t size);
+99
View File
@@ -0,0 +1,99 @@
#include "pkg2zip_aes.h"
#include <string.h>
#include <wmmintrin.h> // AESNI
#include <tmmintrin.h> // SSSE3
#define AES128_INIT(ctx, x, rcon) \
{ \
__m128i a, b; \
_mm_store_si128(ctx, x); \
a = _mm_aeskeygenassist_si128(x, rcon); \
a = _mm_shuffle_epi32(a, 0xff); \
b = _mm_slli_si128(x, 4); \
x = _mm_xor_si128(x, b); \
b = _mm_slli_si128(b, 4); \
x = _mm_xor_si128(x, b); \
b = _mm_slli_si128(b, 4); \
x = _mm_xor_si128(x, b); \
x = _mm_xor_si128(x, a); \
}
void aes128_init_x86(aes128_key* context, const uint8_t* key)
{
__m128i* ctx = (__m128i*)context->key;
__m128i x = _mm_loadu_si128((const __m128i*)key);
AES128_INIT(ctx + 0, x, 0x01);
AES128_INIT(ctx + 1, x, 0x02);
AES128_INIT(ctx + 2, x, 0x04);
AES128_INIT(ctx + 3, x, 0x08);
AES128_INIT(ctx + 4, x, 0x10);
AES128_INIT(ctx + 5, x, 0x20);
AES128_INIT(ctx + 6, x, 0x40);
AES128_INIT(ctx + 7, x, 0x80);
AES128_INIT(ctx + 8, x, 0x1b);
AES128_INIT(ctx + 9, x, 0x36);
_mm_storeu_si128(ctx + 10, x);
}
static __m128i aes128_encrypt_x86(__m128i input, const __m128i* key)
{
__m128i tmp = _mm_xor_si128(input, key[0]);
tmp = _mm_aesenc_si128(tmp, key[1]);
tmp = _mm_aesenc_si128(tmp, key[2]);
tmp = _mm_aesenc_si128(tmp, key[3]);
tmp = _mm_aesenc_si128(tmp, key[4]);
tmp = _mm_aesenc_si128(tmp, key[5]);
tmp = _mm_aesenc_si128(tmp, key[6]);
tmp = _mm_aesenc_si128(tmp, key[7]);
tmp = _mm_aesenc_si128(tmp, key[8]);
tmp = _mm_aesenc_si128(tmp, key[9]);
return _mm_aesenclast_si128(tmp, key[10]);
}
void aes128_ecb_encrypt_x86(const aes128_key* context, const uint8_t* input, uint8_t* output)
{
const __m128i* key = (__m128i*)context->key;
__m128i tmp = aes128_encrypt_x86(_mm_loadu_si128((const __m128i*)input), key);
_mm_storeu_si128((__m128i*)output, tmp);
}
static __m128i ctr_increment(__m128i counter)
{
__m128i swap = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
__m128i tmp = _mm_shuffle_epi8(counter, swap);
tmp = _mm_add_epi64(tmp, _mm_set_epi32(0, 0, 0, 1));
return _mm_shuffle_epi8(tmp, swap);
}
void aes128_ctr_xor_x86(const aes128_key* context, const uint8_t* iv, uint8_t* buffer, size_t size)
{
const __m128i* key = (__m128i*)context->key;
__m128i counter = _mm_loadu_si128((const __m128i*)iv);
while (size >= 16)
{
__m128i block = aes128_encrypt_x86(counter, key);
__m128i tmp = _mm_xor_si128(_mm_loadu_si128((const __m128i*)buffer), block);
_mm_storeu_si128((__m128i*)buffer, tmp);
counter = ctr_increment(counter);
buffer += 16;
size -= 16;
}
if (size != 0)
{
uint8_t full[16];
memcpy(full, buffer, size);
memset(full + size, 0, 16 - size);
__m128i block = aes128_encrypt_x86(counter, key);
__m128i tmp = _mm_xor_si128(_mm_loadu_si128((const __m128i*)full), block);
_mm_storeu_si128((__m128i*)full, tmp);
memcpy(buffer, full, size);
}
}
+162
View File
@@ -0,0 +1,162 @@
#include "pkg2zip_sys.h"
#include "pkg2zip_utils.h"
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
sys_file sys_open(const char* fname, uint64_t* size)
{
WCHAR path[MAX_PATH];
MultiByteToWideChar(CP_UTF8, 0, fname, -1, path, MAX_PATH);
HANDLE handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (handle == INVALID_HANDLE_VALUE)
{
fatal("ERROR: cannot open '%s' file\n", fname);
}
LARGE_INTEGER sz;
if (!GetFileSizeEx(handle, &sz))
{
fatal("ERROR: cannot get size of '%s' file\n", fname);
}
*size = sz.QuadPart;
return handle;
}
sys_file sys_create(const char* fname)
{
WCHAR path[MAX_PATH];
MultiByteToWideChar(CP_UTF8, 0, fname, -1, path, MAX_PATH);
HANDLE handle = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
if (handle == INVALID_HANDLE_VALUE)
{
fatal("ERROR: cannot create '%s' file\n", fname);
}
return handle;
}
void sys_close(sys_file file)
{
if (!CloseHandle(file))
{
fatal("ERROR: failed to close file\n");
}
}
void sys_read(sys_file file, uint64_t offset, void* buffer, uint32_t size)
{
DWORD read;
OVERLAPPED ov;
ov.hEvent = NULL;
ov.Offset = (uint32_t)offset;
ov.OffsetHigh = (uint32_t)(offset >> 32);
if (!ReadFile(file, buffer, size, &read, &ov) || read != size)
{
fatal("ERROR: failed to read %u bytes from file\n", size);
}
}
void sys_write(sys_file file, uint64_t offset, const void* buffer, uint32_t size)
{
DWORD written;
OVERLAPPED ov;
ov.hEvent = NULL;
ov.Offset = (uint32_t)offset;
ov.OffsetHigh = (uint32_t)(offset >> 32);
if (!WriteFile(file, buffer, size, &written, &ov) || written != size)
{
fatal("ERROR: failed to write %u bytes to file\n", size);
}
}
void sys_rename(const char* src, const char* dst)
{
WCHAR wsrc[MAX_PATH];
WCHAR wdst[MAX_PATH];
MultiByteToWideChar(CP_UTF8, 0, src, -1, wsrc, MAX_PATH);
MultiByteToWideChar(CP_UTF8, 0, dst, -1, wdst, MAX_PATH);
if (!MoveFileExW(wsrc, wdst, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING))
{
fatal("ERROR: failed to rename '%s' to '%s'\n", src, dst);
}
}
#else
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
sys_file sys_open(const char* fname, uint64_t* size)
{
int fd = open(fname, O_RDONLY);
if (fd < 0)
{
fatal("ERROR: cannot open '%s' file\n", fname);
}
struct stat st;
if (fstat(fd, &st) != 0)
{
fatal("ERROR: cannot get size of '%s' file\n", fname);
}
*size = st.st_size;
return (void*)(intptr_t)fd;
}
sys_file sys_create(const char* fname)
{
int fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd < 0)
{
fatal("ERROR: cannot create '%s' file\n", fname);
}
return (void*)(intptr_t)fd;
}
void sys_close(sys_file file)
{
if (close((int)(intptr_t)file) != 0)
{
fatal("ERROR: failed to close file\n");
}
}
void sys_read(sys_file file, uint64_t offset, void* buffer, uint32_t size)
{
ssize_t read = pread((int)(intptr_t)file, buffer, size, offset);
if (read != size)
{
fatal("ERROR: failed to read %u bytes from file\n", size);
}
}
void sys_write(sys_file file, uint64_t offset, const void* buffer, uint32_t size)
{
ssize_t wrote = pwrite((int)(intptr_t)file, buffer, size, offset);
if (wrote != size)
{
fatal("ERROR: failed to read %u bytes from file\n", size);
}
}
void sys_rename(const char* src, const char* dst)
{
if (rename(src, dst) != 0)
{
fatal("ERROR: failed to rename '%s' to '%s'\n", src, dst);
}
}
#endif
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
typedef void* sys_file;
sys_file sys_open(const char* fname, uint64_t* size);
sys_file sys_create(const char* fname);
void sys_close(sys_file file);
void sys_read(sys_file file, uint64_t offset, void* buffer, uint32_t size);
void sys_write(sys_file file, uint64_t offset, const void* buffer, uint32_t size);
void sys_rename(const char* src, const char* dst);
+48
View File
@@ -0,0 +1,48 @@
#include "pkg2zip_utils.h"
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
void fatal(const char* msg, ...)
{
va_list args;
va_start(args, msg);
vfprintf(stderr, msg, args);
va_end(args);
exit(EXIT_FAILURE);
}
static int hex2byte(char ch)
{
if (ch >= '0' && ch <= '9')
{
return ch - '0';
}
else if (ch >= 'A' && ch <= 'Z')
{
return ch - 'A' + 10;
}
else if (ch >= 'a' && ch <= 'z')
{
return ch - 'a' + 10;
}
else
{
fatal("ERROR: invalid '%c' hex character\n", ch);
}
}
void get_hex_bytes16(const char* str, uint8_t* bytes)
{
for (size_t i = 0; i<16; i++)
{
if (str[0] == 0 || str[1] == 0)
{
fatal("ERROR: hex string must be 32 hex characters long\n");
}
bytes[i] = (uint8_t)((hex2byte(str[0]) << 4) + hex2byte(str[1]));
str += 2;
}
}
+118
View File
@@ -0,0 +1,118 @@
#pragma once
#include <stdint.h>
#if defined(_MSC_VER)
# define NORETURN __declspec(noreturn)
#else
# define NORETURN __attribute__((noreturn))
#endif
void NORETURN fatal(const char* msg, ...) ;
void get_hex_bytes16(const char* str, uint8_t* bytes);
static inline uint32_t min32(uint32_t a, uint32_t b)
{
return a < b ? a : b;
}
static inline uint64_t min64(uint64_t a, uint64_t b)
{
return a < b ? a : b;
}
static inline uint16_t get16le(const uint8_t* bytes)
{
return (bytes[0]) | (bytes[1] << 8);
}
static inline uint32_t get32le(const uint8_t* bytes)
{
return (bytes[0]) | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
}
static inline uint64_t get64le(const uint8_t* bytes)
{
return (uint64_t)bytes[0]
| ((uint64_t)bytes[1] << 8)
| ((uint64_t)bytes[2] << 16)
| ((uint64_t)bytes[3] << 24)
| ((uint64_t)bytes[4] << 32)
| ((uint64_t)bytes[5] << 40)
| ((uint64_t)bytes[6] << 48)
| ((uint64_t)bytes[7] << 56);
}
static inline uint16_t get16be(const uint8_t* bytes)
{
return (bytes[1]) | (bytes[0] << 8);
}
static inline uint32_t get32be(const uint8_t* bytes)
{
return (bytes[3]) | (bytes[2] << 8) | (bytes[1] << 16) | (bytes[0] << 24);
}
static inline uint64_t get64be(const uint8_t* bytes)
{
return (uint64_t)bytes[7]
| ((uint64_t)bytes[6] << 8)
| ((uint64_t)bytes[5] << 16)
| ((uint64_t)bytes[4] << 24)
| ((uint64_t)bytes[3] << 32)
| ((uint64_t)bytes[2] << 40)
| ((uint64_t)bytes[1] << 48)
| ((uint64_t)bytes[0] << 56);
}
static inline void set16le(uint8_t* bytes, uint16_t x)
{
bytes[0] = (uint8_t)x;
bytes[1] = (uint8_t)(x >> 8);
}
static inline void set32le(uint8_t* bytes, uint32_t x)
{
bytes[0] = (uint8_t)x;
bytes[1] = (uint8_t)(x >> 8);
bytes[2] = (uint8_t)(x >> 16);
bytes[3] = (uint8_t)(x >> 24);
}
static inline void set64le(uint8_t* bytes, uint64_t x)
{
bytes[0] = (uint8_t)x;
bytes[1] = (uint8_t)(x >> 8);
bytes[2] = (uint8_t)(x >> 16);
bytes[3] = (uint8_t)(x >> 24);
bytes[4] = (uint8_t)(x >> 32);
bytes[5] = (uint8_t)(x >> 40);
bytes[6] = (uint8_t)(x >> 48);
bytes[7] = (uint8_t)(x >> 56);
}
static inline void set16be(uint8_t* bytes, uint16_t x)
{
bytes[0] = (uint8_t)(x >> 8);
bytes[1] = (uint8_t)x;
}
static inline void set32be(uint8_t* bytes, uint32_t x)
{
bytes[0] = (uint8_t)(x >> 24);
bytes[1] = (uint8_t)(x >> 16);
bytes[2] = (uint8_t)(x >> 8);
bytes[3] = (uint8_t)x;
}
static inline void set64be(uint8_t* bytes, uint64_t x)
{
bytes[0] = (uint8_t)(x >> 56);
bytes[1] = (uint8_t)(x >> 48);
bytes[2] = (uint8_t)(x >> 40);
bytes[3] = (uint8_t)(x >> 32);
bytes[4] = (uint8_t)(x >> 24);
bytes[5] = (uint8_t)(x >> 16);
bytes[6] = (uint8_t)(x >> 8);
bytes[7] = (uint8_t)x;
}
+344
View File
@@ -0,0 +1,344 @@
#define _CRT_SECURE_NO_DEPRECATE
#include "pkg2zip_zip.h"
#include "pkg2zip_utils.h"
#include <string.h>
#include <time.h>
#define ZIP_VERSION 45
#define ZIP_METHOD_STORE 0
#define ZIP_UTF8_FLAG (1 << 11)
#define ZIP_DOS_ATTRIBUTE_DIRECTORY 0x10
#define ZIP_DOS_ATTRIBUTE_ARCHIVE 0x20
#define ZIP_LOCAL_HEADER_SIZE 30
#define ZIP_GLOBAL_HEADER_SIZE 46
#define ZIP64_EOC_DIR_SIZE 56
#define ZIP64_EOC_DIR_LOCATOR_SIZE 20
#define ZIP_EOC_DIR_SIZE 22
#define ZIP_LOCAL_HEADER_CRC32_OFFSET 14
#define ZIP_LOCAL_HEADER_FILENAME_LENGTH_OFFSET 26
static const uint32_t crc32[256] =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};
void zip_create(zip* z, const char* name)
{
z->file = sys_create(name);
z->total = 0;
z->count = 0;
time_t t = time(NULL);
struct tm* tm = localtime(&t);
z->date = (uint16_t)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday);
z->time = (uint16_t)((tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2));
}
void zip_add_folder(zip* z, const char* name)
{
if (z->count == ZIP_MAX_FILES)
{
fatal("ERROR: too many files\n");
}
size_t name_length = strlen(name);
if (name_length > ZIP_MAX_FILENAME)
{
fatal("ERROR: dirname too long\n");
}
z->offset[z->count] = z->total;
z->size[z->count] = 0;
z->crc32[z->count] = 0;
z->count++;
uint8_t header[ZIP_LOCAL_HEADER_SIZE] = { 0x50, 0x4b, 0x03, 0x04 };
// version needed to extract
set16le(header + 4, ZIP_VERSION);
// general purpose bit flag
set16le(header + 6, ZIP_UTF8_FLAG);
// compression method
set16le(header + 8, ZIP_METHOD_STORE);
// last mod file time
set16le(header + 10, z->time);
// last mod file date
set16le(header + 12, z->date);
// file name length
set16le(header + 26, (uint16_t)name_length);
sys_write(z->file, z->total, header, sizeof(header));
z->total += sizeof(header);
sys_write(z->file, z->total, name, (uint16_t)name_length);
z->total += name_length;
}
void zip_begin_file(zip* z, const char* name)
{
if (z->count == ZIP_MAX_FILES)
{
fatal("ERROR: too many files\n");
}
size_t name_length = strlen(name);
if (name_length > ZIP_MAX_FILENAME)
{
fatal("ERROR: filename too long\n");
}
z->offset[z->count] = z->total;
z->size[z->count] = 0;
z->crc32[z->count] = 0xffffffff;
uint8_t header[ZIP_LOCAL_HEADER_SIZE] = { 0x50, 0x4b, 0x03, 0x04 };
// version needed to extract
set16le(header + 4, ZIP_VERSION);
// general purpose bit flag
set16le(header + 6, ZIP_UTF8_FLAG);
// compression method
set16le(header + 8, ZIP_METHOD_STORE);
// last mod file time
set16le(header + 10, z->time);
// last mod file date
set16le(header + 12, z->date);
// file name length
set16le(header + 26, (uint16_t)name_length);
sys_write(z->file, z->total, header, sizeof(header));
z->total += sizeof(header);
sys_write(z->file, z->total, name, (uint16_t)name_length);
z->total += name_length;
}
void zip_write_file(zip* z, const void* data, uint32_t size)
{
sys_write(z->file, z->total, data, size);
z->total += size;
z->size[z->count] += size;
const uint8_t* bytes = data;
uint32_t tmp = z->crc32[z->count];
for (uint32_t i = 0; i < size; i++)
{
tmp = (tmp >> 8) ^ crc32[(uint8_t)(tmp ^ bytes[i])];
}
z->crc32[z->count] = tmp;
}
void zip_end_file(zip* z)
{
z->crc32[z->count] ^= 0xffffffff;
uint64_t size = z->size[z->count];
if (size > 0)
{
uint8_t update[3 * sizeof(uint32_t)];
// crc-32
set32le(update + 0, z->crc32[z->count]);
// compressed size
set32le(update + 4, (uint32_t)min64(size, 0xffffffff));
// uncompressed size
set32le(update + 8, (uint32_t)min64(size, 0xffffffff));
sys_write(z->file, z->offset[z->count] + ZIP_LOCAL_HEADER_CRC32_OFFSET, update, sizeof(update));
}
z->count++;
}
void zip_close(zip* z)
{
uint64_t central_dir_offset = z->total;
// central directory headers
for (uint32_t i = 0; i < z->count; i++)
{
uint8_t local[ZIP_LOCAL_HEADER_SIZE];
sys_read(z->file, z->offset[i], local, sizeof(local));
uint32_t filename_length = get16le(local + ZIP_LOCAL_HEADER_FILENAME_LENGTH_OFFSET);
uint8_t global[ZIP_GLOBAL_HEADER_SIZE + ZIP_MAX_FILENAME] = { 0x50, 0x4b, 0x01, 0x02 };
sys_read(z->file, z->offset[i] + sizeof(local), global + ZIP_GLOBAL_HEADER_SIZE, filename_length);
int is_folder = global[ZIP_GLOBAL_HEADER_SIZE + filename_length - 1] == '/';
uint8_t extra[28];
uint16_t extra_size = 0;
uint64_t size = z->size[i];
uint64_t offset = z->offset[i];
uint32_t attributes = ZIP_DOS_ATTRIBUTE_ARCHIVE;
if (is_folder)
{
attributes |= ZIP_DOS_ATTRIBUTE_DIRECTORY;
if (offset > 0xffffffff)
{
extra_size += sizeof(uint64_t);
}
}
else
{
if (size > 0xffffffff)
{
extra_size += 2 * sizeof(uint64_t);
}
if (offset > 0xffffffff)
{
extra_size += sizeof(uint64_t);
}
}
if (extra_size)
{
extra_size += 2 * sizeof(uint16_t);
}
// version made by
set16le(global + 4, ZIP_VERSION);
// version needed to extract
set16le(global + 6, ZIP_VERSION);
// general purpose bit flag
set16le(global + 8, ZIP_UTF8_FLAG);
// compression method
set16le(global + 10, ZIP_METHOD_STORE);
// last mod file time
set16le(global + 12, z->time);
// last mod file date
set16le(global + 14, z->date);
// crc-32
set32le(global + 16, z->crc32[i]);
// compressed size
set32le(global + 20, (uint32_t)min64(size, 0xffffffff));
// uncompressed size
set32le(global + 24, (uint32_t)min64(size, 0xffffffff));
// file name length
set16le(global + 28, (uint16_t)filename_length);
// extra field length
set16le(global + 30, extra_size);
// external file attributes
set32le(global + 38, attributes);
// relative offset of local header 4 bytes
set32le(global + 42, (uint32_t)min64(offset, 0xffffffff));
sys_write(z->file, z->total, global, ZIP_GLOBAL_HEADER_SIZE + filename_length);
z->total += ZIP_GLOBAL_HEADER_SIZE + filename_length;
// zip64 Extended Information Extra Field
set16le(extra + 0, 1);
// size of this "extra" block
set16le(extra + 2, extra_size - 2*sizeof(uint16_t));
if (size > 0xffffffff)
{
// original uncompressed file size
set64le(extra + 4, size);
// size of compressed data
set64le(extra + 12, size);
}
if (offset > 0xffffffff)
{
// offset of local header record
set64le(extra + 20, offset);
}
if (extra_size > 2 * sizeof(uint16_t))
{
sys_write(z->file, z->total, extra, extra_size);
z->total += extra_size;
}
}
uint64_t end_of_central_dir_offset = z->total;
uint32_t central_dir_size = (uint32_t)(end_of_central_dir_offset - central_dir_offset);
// zip64 end of central directory record
{
uint8_t header[ZIP64_EOC_DIR_SIZE] = { 0x50, 0x4b, 0x06, 0x06 };
// size of zip64 end of central directory record
set64le(header + 4, sizeof(header) - sizeof(uint32_t) - sizeof(uint64_t));
// version made by
set16le(header + 12, ZIP_VERSION);
// version needed to extract
set16le(header + 14, ZIP_VERSION);
// number of this disk
set32le(header + 16, 0);
// number of the disk with the start of the central directory
set32le(header + 20, 0);
// total number of entries in the central directory on this disk
set64le(header + 24, z->count);
// total number of entries in the central directory
set64le(header + 32, z->count);
// size of the central directory
set64le(header + 40, central_dir_size);
// offset of start of central directory with respect to the starting disk number
set64le(header + 48, central_dir_offset);
sys_write(z->file, z->total, header, sizeof(header));
z->total += sizeof(header);
}
// zip64 end of central directory locator
{
uint8_t header[ZIP64_EOC_DIR_LOCATOR_SIZE] = { 0x50, 0x4b, 0x06, 0x07 };
// relative offset of the zip64 end of central directory record 8 bytes
set64le(header + 8, end_of_central_dir_offset);
// total number of disks
set32le(header + 16, 1);
sys_write(z->file, z->total, header, sizeof(header));
z->total += sizeof(header);
}
// end of central directory record
{
uint8_t header[ZIP_EOC_DIR_SIZE] = { 0x50, 0x4b, 0x05, 0x06 };
// total number of entries in the central directory on this disk
set16le(header + 8, (uint16_t)z->count);
// total number of entries in the central directory
set16le(header + 10, (uint16_t)z->count);
// size of the central directory
set32le(header + 12, central_dir_size);
// offset of start of central directory with respect to the starting disk number
set32le(header + 16, (uint32_t)min64(central_dir_offset, 0xffffffff));
sys_write(z->file, z->total, header, sizeof(header));
z->total += sizeof(header);
}
sys_close(z->file);
}
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include "pkg2zip_sys.h"
#include <stddef.h>
#include <stdint.h>
#define ZIP_MAX_FILES 8192
#define ZIP_MAX_FILENAME 1024
typedef struct {
sys_file file;
uint64_t total;
uint32_t count;
uint16_t time;
uint16_t date;
uint64_t offset[ZIP_MAX_FILES];
uint64_t size[ZIP_MAX_FILES];
uint32_t crc32[ZIP_MAX_FILES];
} zip;
void zip_create(zip* z, const char* name);
void zip_add_folder(zip* z, const char* name);
void zip_begin_file(zip* z, const char* name);
void zip_write_file(zip* z, const void* data, uint32_t size);
void zip_end_file(zip* z);
void zip_close(zip* z);