mirror of
https://github.com/mmozeiko/pkg2zip.git
synced 2026-09-02 19:23:09 +03:00
first version
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
pkg2zip
|
||||
pkg2zip.exe
|
||||
*.d
|
||||
*.o
|
||||
*.obj
|
||||
*.pdb
|
||||
|
||||
@@ -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>
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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}
|
||||
@@ -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
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
@@ -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
|
||||
@@ -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);
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
Reference in New Issue
Block a user