From f045ea366017e8acccbcecee52e1c718ebacc9d4 Mon Sep 17 00:00:00 2001 From: Maschell Date: Fri, 16 Oct 2015 14:10:51 +0200 Subject: [PATCH] initial commit --- client/Makefile | 57 ++ client/main.c | 310 +++++++++ client/main.h | 67 ++ client/saviine.c | 253 +++++++ client/saviine532.ld | 70 ++ common/common.h | 45 ++ common/fs_defs.h | 79 +++ common/types.h | 22 + installer/Makefile | 29 + installer/bin/code532.bin | Bin 0 -> 7852 bytes installer/bin/saviine.o | Bin 0 -> 9324 bytes installer/saviine.c | 247 +++++++ installer/saviine532.h | 415 +++++++++++ server/saviine_server.exe | Bin 0 -> 26624 bytes server/src/.vs/cafiine_server/v14/.suo | Bin 0 -> 39424 bytes server/src/Program.cs | 335 +++++++++ server/src/Properties/AssemblyInfo.cs | 36 + server/src/System/IO/EndianBinaryReader.cs | 644 ++++++++++++++++++ server/src/System/IO/EndianBinaryWriter.cs | 643 +++++++++++++++++ server/src/app.config | 3 + server/src/cafiine_server.v12.suo | Bin 0 -> 28672 bytes ...gnTimeResolveAssemblyReferencesInput.cache | Bin 0 -> 6190 bytes ...saviine_server.csproj.FileListAbsolute.txt | 6 + ...erver.csprojResolveAssemblyReference.cache | Bin 0 -> 4634 bytes server/src/obj/x86/Debug/saviine_server.exe | Bin 0 -> 26624 bytes server/src/obj/x86/Debug/saviine_server.pdb | Bin 0 -> 65024 bytes server/src/saviine_server.csproj | 94 +++ server/src/saviine_server.csproj.user | 13 + server/src/saviine_server.sln | 22 + server/src/saviine_server.suo | Bin 0 -> 2560 bytes server/src/saviine_server.v12.suo | Bin 0 -> 22016 bytes 31 files changed, 3390 insertions(+) create mode 100644 client/Makefile create mode 100644 client/main.c create mode 100644 client/main.h create mode 100644 client/saviine.c create mode 100644 client/saviine532.ld create mode 100644 common/common.h create mode 100644 common/fs_defs.h create mode 100644 common/types.h create mode 100644 installer/Makefile create mode 100644 installer/bin/code532.bin create mode 100644 installer/bin/saviine.o create mode 100644 installer/saviine.c create mode 100644 installer/saviine532.h create mode 100644 server/saviine_server.exe create mode 100644 server/src/.vs/cafiine_server/v14/.suo create mode 100644 server/src/Program.cs create mode 100644 server/src/Properties/AssemblyInfo.cs create mode 100644 server/src/System/IO/EndianBinaryReader.cs create mode 100644 server/src/System/IO/EndianBinaryWriter.cs create mode 100644 server/src/app.config create mode 100644 server/src/cafiine_server.v12.suo create mode 100644 server/src/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache create mode 100644 server/src/obj/x86/Debug/saviine_server.csproj.FileListAbsolute.txt create mode 100644 server/src/obj/x86/Debug/saviine_server.csprojResolveAssemblyReference.cache create mode 100644 server/src/obj/x86/Debug/saviine_server.exe create mode 100644 server/src/obj/x86/Debug/saviine_server.pdb create mode 100644 server/src/saviine_server.csproj create mode 100644 server/src/saviine_server.csproj.user create mode 100644 server/src/saviine_server.sln create mode 100644 server/src/saviine_server.suo create mode 100644 server/src/saviine_server.v12.suo diff --git a/client/Makefile b/client/Makefile new file mode 100644 index 0000000..9368c14 --- /dev/null +++ b/client/Makefile @@ -0,0 +1,57 @@ + +PATH := $(DEVKITPPC)/bin:$(PATH) + +PREFIX ?= powerpc-eabi- +LD := $(PREFIX)ld +AS := $(PREFIX)as +CC := $(PREFIX)gcc +OBJDUMP ?= $(PREFIX)objdump +OBJCOPY ?= $(PREFIX)objcopy + +SFLAGS := -mgekko -mregnames + +# -O2: optimise lots +# -Wall: generate lots of warnings +# -x c: compile as C code +# -std=gnu99: use the C99 standard with GNU extensions +# -ffreestanding: we don't have libc; don't expect we do +# -mrvl: enable wii/gamecube compilation +# -mcpu=750: enable processor specific compilation +# -meabi: enable eabi specific compilation +# -mhard-float: enable hardware floating point instructions +# -fshort-wchar: use 16 bit whcar_t type in keeping with Wii executables +# -msdata-none: do not use r2 or r13 as small data areas +# -memb: enable embedded application specific compilation +# -ffunction-sections: split up functions so linker can garbage collect +# -fdata-sections: split up data so linker can garbage collect +CFLAGS := -O2 -Wall -x c -std=gnu99 \ + -ffreestanding \ + -mrvl -mcpu=750 -meabi -mhard-float -fshort-wchar \ + -msdata=none -memb -ffunction-sections -fdata-sections \ + -Wno-unknown-pragmas -Wno-strict-aliasing \ + +SRC := $(wildcard *.S) $(wildcard *.c) +OBJ := $(patsubst %.S,%.o,$(patsubst %.c,%.o,$(SRC))) + +all: ../installer/saviine532.h + +../installer/saviine%.h: saviine%.text.bin saviine%.magic.bin + xxd -i saviine$*.magic.bin | sed "s/unsigned/static const unsigned/g;s/ine$*/ine/g" > $@ + xxd -i saviine$*.text.bin | sed "s/unsigned/static const unsigned/g;s/ine$*/ine/g" >> $@ + +saviine%.text.bin: saviine%.elf + $(OBJCOPY) -j .text -O binary $< $@ +saviine%.magic.bin: saviine%.elf + $(OBJCOPY) -j .magic -O binary $< $@ + +saviine%.elf: saviine%.ld $(OBJ) + $(LD) -T $< $(OBJ) + +%.o: %.c + $(CC) -c $(CFLAGS) -o $@ $+ +%.o: %.S + $(AS) $(SFLAGS) -o $@ $+ + +clean: + rm -f $(wildcard *.o) $(wildcard *.elf) $(wildcard ../installer/saviine532.h) + diff --git a/client/main.c b/client/main.c new file mode 100644 index 0000000..16dfe4c --- /dev/null +++ b/client/main.c @@ -0,0 +1,310 @@ +#include "main.h" +#include "../common/fs_defs.h" +#define DECL(res, name, ...) \ + extern res name(__VA_ARGS__); \ + res (* real_ ## name)(__VA_ARGS__) __attribute__((section(".magicptr"))); \ + res my_ ## name(__VA_ARGS__) + +#define BYTE_LOG_STR 0xfb + + +extern FSStatus FSOpenDir(FSClient *pClient, FSCmdBlock *pCmd, const char *path, int *dh, FSRetFlag errHandling); +extern FSStatus FSReadDir(FSClient *pClient, FSCmdBlock *pCmd, int dh, FSDirEntry *dir_entry, FSRetFlag errHandling); +extern FSStatus FSChangeDir(FSClient *pClient, FSCmdBlock *pCmd, const char *path, FSRetFlag errHandling); +extern FSStatus FSCloseDir(FSClient *pClient, FSCmdBlock *pCmd, int dh, FSRetFlag errHandling); +extern FSStatus FSReadFile(FSClient *pClient, FSCmdBlock *pCmd, void *buffer, int size, int count, int fd, int flag, int error); +extern FSStatus FSSetPosFile(FSClient *pClient, FSCmdBlock *pCmd, int fd, int pos, int error); +extern FSStatus FSCloseFile (FSClient *pClient, FSCmdBlock *pCmd, int fd, int error); +extern FSStatus FSOpenFile(FSClient *pClient, FSCmdBlock *pCmd, char *path, const char *mode, int *handle, int error); + +DECL(int, FSAInit, void) { + if ((int)bss_ptr == 0x0a000000) { + bss_ptr = memalign(sizeof(struct bss_t), 0x40); + memset(bss_ptr, 0, sizeof(struct bss_t)); + } + return real_FSAInit(); +} +DECL(int, FSAShutdown, void) { + return real_FSAShutdown(); +} +DECL(int, FSAAddClient, void *r3) { + int res = real_FSAAddClient(r3); + + if ((int)bss_ptr != 0x0a000000 && res < MAX_CLIENT && res >= 0) { + cafiine_connect(&bss.socket_fsa[res]); + } + + return res; +} +DECL(int, FSADelClient, int client) { + if ((int)bss_ptr != 0x0a000000 && client < MAX_CLIENT && client >= 0) { + cafiine_disconnect(bss.socket_fsa[client]); + } + + return real_FSADelClient(client); +} +DECL(int, FSAOpenFile, int client, const char *path, const char *mode, int *handle) { + return real_FSAOpenFile(client, path, mode, handle); +} + +static int client_num_alloc(void *pClient) { + int i; + + for (i = 0; i < MAX_CLIENT; i++) + if (bss.pClient_fs[i] == 0) { + bss.pClient_fs[i] = pClient; + return i; + } + return -1; +} +static void clietn_num_free(int client) { + bss.pClient_fs[client] = 0; +} +static int client_num(void *pClient) { + int i; + + for (i = 0; i < MAX_CLIENT; i++) + if (bss.pClient_fs[i] == pClient) + return i; + return -1; +} + + +struct fs_async_t { + void (*callback)(int status, int command, void *request, void *response, void *context); + void *context; + void *queue; +}; + +#define DUMP_BLOCK_SIZE (0x200 * 100) +#define DUMP_BLOCK_SIZE_SLOW (0x20 * 100) +static int dump_dir(void *pClient,int client, void *pCmd, char *path, int error, int handle){ + int dir_handle = handle; + int my_handle = handle +1; + int ret = 0; + if ((ret = FSOpenDir(pClient, pCmd, path, &dir_handle, FS_RET_ALL_ERROR)) == FS_STATUS_OK) + { + char new_mode[2]; + new_mode[0] = 'r'; + new_mode[1] = '\0'; + + FSDirEntry dir_entry; + while (FSReadDir(pClient, pCmd, dir_handle, &dir_entry, FS_RET_ALL_ERROR) == FS_STATUS_OK) + { + char full_path[255]; + int i=0; + char *path_ptr = (char *)path; + while(*path_ptr) { + full_path[i++] = *path_ptr++; + } + full_path[i++] = '/'; + char *dir_name_ptr = (char *)dir_entry.name; + while(*dir_name_ptr) { + full_path[i++] = *dir_name_ptr++; + } + full_path[i++] = '\0'; + + log_string(bss.socket_fsa[client], full_path, BYTE_LOG_STR); + if((dir_entry.stat.flag&FS_STAT_FLAG_IS_DIRECTORY) == FS_STAT_FLAG_IS_DIRECTORY){ + char type[4]; + type[0] = 'd'; + type[1] = 'i'; + type[2] = 'r'; + type[3] = '\0'; + log_string(bss.socket_fsa[client], type, BYTE_LOG_STR); + dump_dir(pClient,client,pCmd,full_path,error,my_handle); + }else{ + char type[5]; + type[0] = 'f'; + type[1] = 'i'; + type[2] = 'l'; + type[3] = 'e'; + type[4] = '\0'; + log_string(bss.socket_fsa[client], type, BYTE_LOG_STR); + //DUMP + ret = FSOpenFile(pClient, pCmd, full_path, new_mode, &my_handle, error); + if (ret >= 0) { + int my_ret = 1; + log_string(bss.socket_fsa[client], full_path, BYTE_LOG_STR); + int size = (my_ret == 1 ? DUMP_BLOCK_SIZE : DUMP_BLOCK_SIZE_SLOW); + cafiine_send_handle(bss.socket_fsa[client], client, full_path, my_handle); + void* buffer = memalign(sizeof(char) * size, 0x40); + int ret2; + while ((ret2 = FSReadFile(pClient, pCmd, buffer, 1, size, my_handle, 0, error)) > 0) + cafiine_send_file(bss.socket_fsa[client], buffer, ret2, my_handle); + cafiine_fclose(bss.socket_fsa[client], &ret2, my_handle); + FSSetPosFile(pClient, pCmd, my_handle, 0, error); + free(buffer); + FSCloseFile(pClient, pCmd, my_handle, -1); + } + } + } + FSCloseDir(pClient, pCmd, dir_handle, FS_RET_NO_ERROR); + }else{ + /* + char error[3]; + error[0] = '-'; + error[1] = (ret*-1) + '0'; + error[2] = '\0'; */ + //log_string(bss.socket_fsa[client], foo, BYTE_LOG_STR); + return -1; + } + return 0; +} + +DECL(int, FSInit, void) { + if ((int)bss_ptr == 0x0a000000) { + bss_ptr = memalign(sizeof(struct bss_t), 0x40); + memset(bss_ptr, 0, sizeof(struct bss_t)); + } + return real_FSInit(); +} +DECL(int, FSShutdown, void) { + return real_FSShutdown(); +} +DECL(int, FSAddClientEx, void *r3, void *r4, void *r5) { + int res = real_FSAddClientEx(r3, r4, r5); + + if ((int)bss_ptr != 0x0a000000 && res >= 0) { + int client = client_num_alloc(r3); + if (client < MAX_CLIENT && client >= 0) { + cafiine_connect(&bss.socket_fs[client]); + } + } + return res; +} +DECL(int, FSDelClient, void *pClient) { + if ((int)bss_ptr != 0x0a000000) { + int client = client_num(pClient); + if (client < MAX_CLIENT && client >= 0) { + cafiine_disconnect(bss.socket_fs[client]); + clietn_num_free(client); + } + } + return real_FSDelClient(pClient); +} + +static void dump_saves(void *pClient, void *pCmd,int error, int client){ + int i = 0; + char save_path[255]; + char save_user[9]; + char save_base[11]; + char save_common[7]; + + save_base[i++] = '/'; + save_base[i++] = 'v'; + save_base[i++] = 'o'; + save_base[i++] = 'l'; + save_base[i++] = '/'; + save_base[i++] = 's'; + save_base[i++] = 'a'; + save_base[i++] = 'v'; + save_base[i++] = 'e'; + save_base[i++] = '/'; + save_base[i++] = '\0'; + i = 0; + + save_user[i++] = '8'; + save_user[i++] = '0'; + save_user[i++] = '0'; + save_user[i++] = '0'; + save_user[i++] = '0'; + save_user[i++] = '0'; + save_user[i++] = '0'; + save_user[i++] = '0'; + save_user[i++] = '\0'; + + i = 0; + save_common[i++] = 'c'; + save_common[i++] = 'o'; + save_common[i++] = 'm'; + save_common[i++] = 'm'; + save_common[i++] = 'o'; + save_common[i++] = 'n'; + save_common[i++] = '\0'; + + i = 0; + char *save_base_ptr = (char *)save_base; + while(*save_base_ptr) { + save_path[i++] = *save_base_ptr++; + } + + int k = i; + char *save_user_ptr = (char *)save_user; + while(*save_user_ptr) { + save_path[i++] = *save_user_ptr++; + } + save_path[i++] = '\0'; + int id = 1; + do{ + //log_string(bss.socket_fsa[client], save_path, BYTE_LOG_STR); + if (dump_dir(pClient,client,pCmd,save_path,error,50) == 0);// id = 257; // break if successful + int first = id/16; + int seconds = id%16; + if(first <= 9) + save_path[16] = '0' + first; + else + save_path[16] = 'a' + (first - 10); + + if(seconds <= 9) + save_path[17] = '0' + seconds; + else + save_path[17] = 'a' + (seconds - 10); + + id++; + }while(id < 257); + + i = k; + + char *save_common_ptr = (char *)save_common; + while(*save_common_ptr) { + save_path[i++] = *save_common_ptr++; + } + save_path[i++] = '\0'; + + log_string(bss.socket_fsa[client], save_path, BYTE_LOG_STR); + dump_dir(pClient,client,pCmd,save_path,error,60); + + i = 0; + char info[6]; + info[i++] = 'd'; + info[i++] = 'o'; + info[i++] = 'n'; + info[i++] = 'e'; + info[i++] = '!'; + info[i++] = '\0'; + log_string(bss.socket_fsa[client], info, BYTE_LOG_STR); +} + +DECL(int, FSGetStat, void *pClient, void *pCmd, char *path, void *stats, int error) { + if ((int)bss_ptr != 0x0a000000) { + int client = client_num(pClient); + + if (client < MAX_CLIENT && client >= 0) { + if(bss.savesDumped == 0){ + dump_saves(pClient,pCmd,error,client); + bss.savesDumped = 2; + } + } + } + return real_FSGetStat(pClient, pCmd, path, stats, error); +} + +#define MAKE_MAGIC(x) { x, my_ ## x, &real_ ## x } + +struct magic_t { + const void *real; + const void *replacement; + const void *call; +} methods[] __attribute__((section(".magic"))) = { + MAKE_MAGIC(FSAInit), + MAKE_MAGIC(FSAShutdown), + MAKE_MAGIC(FSAAddClient), + MAKE_MAGIC(FSADelClient), + MAKE_MAGIC(FSAOpenFile), + MAKE_MAGIC(FSGetStat), + MAKE_MAGIC(FSInit), + MAKE_MAGIC(FSShutdown), + MAKE_MAGIC(FSAddClientEx), + MAKE_MAGIC(FSDelClient), +}; diff --git a/client/main.h b/client/main.h new file mode 100644 index 0000000..f21c115 --- /dev/null +++ b/client/main.h @@ -0,0 +1,67 @@ +/* string.h */ +#define NULL ((void *)0) + +void *memcpy(void *dst, const void *src, int bytes); +void *memset(void *dst, int val, int bytes); + +/* malloc.h */ +extern void *(* const MEMAllocFromDefaultHeapEx)(int size, int align); +extern void *(* const MEMFreeToDefaultHeap)(void *ptr); +#define memalign (*MEMAllocFromDefaultHeapEx) +#define free (*MEMFreeToDefaultHeap) +/* socket.h */ +#define AF_INET 2 +#define SOCK_STREAM 1 +#define IPPROTO_TCP 6 + + + +extern void socket_lib_init(); +extern int socket(int domain, int type, int protocol); +extern int socketclose(int socket); +extern int connect(int socket, void *addr, int addrlen); +extern int send(int socket, const void *buffer, int size, int flags); +extern int recv(int socket, void *buffer, int size, int flags); + +struct in_addr { + unsigned int s_addr; +}; +struct sockaddr_in { + short sin_family; + unsigned short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; + +/* OS stuff */ +extern const long long title_id; + +/* Forward declarations */ +#define MAX_CLIENT 32 +#define MASK_FD 0x0fff00ff + +struct bss_t { + int socket_fsa[MAX_CLIENT]; + void *pClient_fs[MAX_CLIENT]; + int socket_fs[MAX_CLIENT]; + char save_path[255]; + volatile int savesDumped; + volatile int lock; +}; + +#define bss_ptr (*(struct bss_t **)0x100000e4) +#define bss (*bss_ptr) + +void cafiine_connect(int *socket); +void cafiine_disconnect(int socket); +int cafiine_fopen(int socket, int *result, const char *path, const char *mode, int *handle); +void cafiine_send_handle(int sock, int client, const char *path, int handle); +void cafiine_send_file(int sock, char *file, int size, int fd); +int cafiine_fread(int socket, int *result, void *buffer, int size, int count, int fd); +int cafiine_fclose(int socket, int *result, int fd); +int cafiine_fsetpos(int socket, int *result, int fd, int set); +int cafiine_fgetpos(int socket, int *result, int fd, int *pos); +int cafiine_fstat(int sock, int *result, int fd, void *ptr); +int cafiine_feof(int sock, int *result, int fd); +void cafiine_send_ping(int sock, int val1, int val2); +void log_string(int sock, const char* str, char flag_byte); \ No newline at end of file diff --git a/client/saviine.c b/client/saviine.c new file mode 100644 index 0000000..ba9110b --- /dev/null +++ b/client/saviine.c @@ -0,0 +1,253 @@ +#include "main.h" + +static int recvwait(int sock, void *buffer, int len); +static int recvbyte(int sock); +static int sendwait(int sock, const void *buffer, int len); + +static int cafiine_handshake(int sock); + +#define CHECK_ERROR(cond) if (cond) { goto error; } + +#define BYTE_NORMAL 0xff +#define BYTE_SPECIAL 0xfe +//#define BYTE_OPEN 0x00 +//#define BYTE_READ 0x01 +#define BYTE_CLOSE 0x02 +//#define BYTE_OK 0x03 +//#define BYTE_SETPOS 0x04 +//#define BYTE_STATFILE 0x05 +//#define BYTE_EOF 0x06 +//#define BYTE_GETPOS 0x07 +//#define BYTE_REQUEST 0x08 +//#define BYTE_REQUEST_SLOW 0x09 +#define BYTE_HANDLE 0x0A +#define BYTE_DUMP 0x0B +#define BYTE_PING 0x0C + +#define BYTE_LOG_STR 0xfb + +void GX2WaitForVsync(void); + +void cafiine_connect(int *psock) { + extern unsigned int server_ip; + struct sockaddr_in addr; + int sock, ret; + + socket_lib_init(); + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + CHECK_ERROR(sock == -1); + + addr.sin_family = AF_INET; + addr.sin_port = 7332; + addr.sin_addr.s_addr = server_ip; + + ret = connect(sock, (void *)&addr, sizeof(addr)); + CHECK_ERROR(ret < 0); + ret = cafiine_handshake(sock); + CHECK_ERROR(ret < 0); + CHECK_ERROR(ret == BYTE_NORMAL); + + *psock = sock; + return; +error: + if (sock != -1) + socketclose(sock); + *psock = -1; + return; +} + +void cafiine_disconnect(int sock) { + CHECK_ERROR(sock == -1); + socketclose(sock); +error: + return; +} + +static int cafiine_handshake(int sock) { + int ret; + unsigned char buffer[16]; + + memcpy(buffer, &title_id, 16); + + ret = sendwait(sock, buffer, sizeof(buffer)); + CHECK_ERROR(ret < 0); + ret = recvbyte(sock); + CHECK_ERROR(ret < 0); + return ret; +error: + return ret; +} + + +void cafiine_send_handle(int sock, int client, const char *path, int handle) +{ + while (bss.lock) GX2WaitForVsync(); + bss.lock = 1; + + CHECK_ERROR(sock == -1); + + // create and send buffer with : [cmd id][handle][path length][path data ...] + { + int ret; + int len_path = 0; + while (path[len_path++]); + char buffer[1 + 4 + 4 + len_path]; + + buffer[0] = BYTE_HANDLE; + *(int *)(buffer + 1) = handle; + *(int *)(buffer + 5) = len_path; + for (ret = 0; ret < len_path; ret++) + buffer[9 + ret] = path[ret]; + + // send buffer, wait for reply + ret = sendwait(sock, buffer, 1 + 4 + 4 + len_path); + CHECK_ERROR(ret < 0); + + // wait reply + ret = recvbyte(sock); + CHECK_ERROR(ret != BYTE_SPECIAL); + } + +error: + bss.lock = 0; + return; +} + +void cafiine_send_file(int sock, char *file, int size, int fd) { + while (bss.lock) GX2WaitForVsync(); + bss.lock = 1; + + CHECK_ERROR(sock == -1); + + int ret; + + // create and send buffer with : [cmd id][fd][size][buffer data ...] + { + char buffer[1 + 4 + 4 + size]; + + buffer[0] = BYTE_DUMP; + *(int *)(buffer + 1) = fd; + *(int *)(buffer + 5) = size; + for (ret = 0; ret < size; ret++) + buffer[9 + ret] = file[ret]; + + // send buffer, wait for reply + ret = sendwait(sock, buffer, 1 + 4 + 4 + size); + + // wait reply + ret = recvbyte(sock); + CHECK_ERROR(ret != BYTE_SPECIAL); + } + +error: + bss.lock = 0; + return; +} + + + +int cafiine_fclose(int sock, int *result, int fd) { + while (bss.lock) GX2WaitForVsync(); + bss.lock = 1; + + CHECK_ERROR(sock == -1); + + int ret; + char buffer[1 + 4]; + buffer[0] = BYTE_CLOSE; + *(int *)(buffer + 1) = fd; + ret = sendwait(sock, buffer, 1 + 4); + CHECK_ERROR(ret < 0); + ret = recvbyte(sock); + CHECK_ERROR(ret < 0); + CHECK_ERROR(ret == BYTE_NORMAL); + ret = recvwait(sock, result, 4); + CHECK_ERROR(ret < 0); + + bss.lock = 0; + return 0; +error: + bss.lock = 0; + return -1; +} + +void cafiine_send_ping(int sock, int val1, int val2) { + while (bss.lock) GX2WaitForVsync(); + bss.lock = 1; + + int ret; + char buffer[1 + 4 + 4]; + buffer[0] = BYTE_PING; + *(int *)(buffer + 1) = val1; + *(int *)(buffer + 5) = val2; + + ret = sendwait(sock, buffer, 1 + 4 + 4); + CHECK_ERROR(ret < 0); + + error: + bss.lock = 0; + return; +} + +static int recvwait(int sock, void *buffer, int len) { + int ret; + while (len > 0) { + ret = recv(sock, buffer, len, 0); + CHECK_ERROR(ret < 0); + len -= ret; + buffer += ret; + } + return 0; +error: + return ret; +} + +static int recvbyte(int sock) { + unsigned char buffer[1]; + int ret; + + ret = recvwait(sock, buffer, 1); + if (ret < 0) return ret; + return buffer[0]; +} + +static int sendwait(int sock, const void *buffer, int len) { + int ret; + while (len > 0) { + ret = send(sock, buffer, len, 0); + CHECK_ERROR(ret < 0); + len -= ret; + buffer += ret; + } + return 0; +error: + return ret; +} + +void log_string(int sock, const char* str, char flag_byte) { + if(sock == -1) { + return; + } + while (bss.lock) GX2WaitForVsync(); + bss.lock = 1; + + int i; + int len_str = 0; + while (str[len_str++]); + + // + { + char buffer[1 + 4 + len_str]; + buffer[0] = flag_byte; + *(int *)(buffer + 1) = len_str; + for (i = 0; i < len_str; i++) + buffer[5 + i] = str[i]; + + buffer[5 + i] = 0; + + sendwait(sock, buffer, 1 + 4 + len_str); + } + + bss.lock = 0; +} diff --git a/client/saviine532.ld b/client/saviine532.ld new file mode 100644 index 0000000..218ae3b --- /dev/null +++ b/client/saviine532.ld @@ -0,0 +1,70 @@ +OUTPUT(cafiine532.elf); + +SECTIONS { + .text 0x011de000 : { + server_ip = .; + . = . + 4; + *(.text._start); + *(.text*); + *(.magicptr*); + } + .magic : { + *(.magic*); + } + /DISCARD/ : { + *(*); + } +} + +/* FSA methods */ +PROVIDE(FSAInit = 0x10608ac); +PROVIDE(FSAShutdown = 0x1060974); +PROVIDE(FSAAddClient = 0x106546c); +PROVIDE(FSADelClient = 0x1060aa4); +PROVIDE(FSAOpenFile = 0x10621f8); + +/* FS methods */ +PROVIDE(FSInit = 0x10683c8); +PROVIDE(FSShutdown = 0x1068538); +PROVIDE(FSAddClientEx = 0x10685fc); +PROVIDE(FSDelClient = 0x1068a08); +PROVIDE(FSOpenFile = 0x106ef7c); +PROVIDE(FSCloseFile = 0x106f088); +PROVIDE(FSReadFile = 0x106f108); +PROVIDE(FSReadFileWithPos = 0x106f194); +PROVIDE(FSGetPosFile = 0x106f4c0); +PROVIDE(FSSetPosFile = 0x106f530); +PROVIDE(FSGetStatFile = 0x106f5a0); +PROVIDE(FSIsEof = 0x106f610); + +/* */ +PROVIDE(FSGetStat = 0x0106fdc8); +PROVIDE(FSGetStatAsync = 0x0106bff4); +PROVIDE(FSOpenFileAsync = 0x0106a434); + +PROVIDE(FSOpenDir = 0x0106f690); +PROVIDE(FSReadDir = 0x0106f780); +PROVIDE(FSCloseDir = 0x0106f700); +PROVIDE(FSChangeDir = 0x0106eefc); +PROVIDE(FSCloseDir = 0x0106f700); + + +/* GX2 methods */ +PROVIDE(GX2WaitForVsync = 0x1151964); + +/* Socket methods */ +PROVIDE(socket_lib_init = 0x10c02f4); +PROVIDE(socket = 0x10c21c8); +PROVIDE(socketclose = 0x10c2314); +PROVIDE(connect = 0x10c0828); +PROVIDE(send = 0x10c16ac); +PROVIDE(recv = 0x10c0aec); + +/* Standard library methods */ +PROVIDE(memcpy = 0x1035a68); +PROVIDE(memset = 0x1035a54); +PROVIDE(MEMAllocFromDefaultHeapEx = 0x1004e9c0); +PROVIDE(MEMFreeToDefaultHeap = 0x100b487c); + +/* OS data */ +PROVIDE(title_id = 0x100136D0); \ No newline at end of file diff --git a/common/common.h b/common/common.h new file mode 100644 index 0000000..4327bbf --- /dev/null +++ b/common/common.h @@ -0,0 +1,45 @@ +#ifndef COMMON_H +#define COMMON_H + +/* DATA_ADDRESS : address where flags start */ +#define DATA_ADDR ((void *)0x011e3800) +#define BOUNCE_FLAG_ADDR (DATA_ADDR - 0x04) // bounce flag +#define IS_ACTIVE_ADDR (DATA_ADDR - 0x08) // is replacement active +#define RPL_REPLACE_ADDR (DATA_ADDR - 0x0C) // is it a new rpl to add +#define RPL_ENTRY_INDEX_ADDR (DATA_ADDR - 0x10) // entry index of the rpx in our table + +/* RPX Address : where the rpx is copied or retrieve, depends if we dump or replace */ +/* Note : from phys 0x30789C5D to 0x31E20000, memory seems empty (space reserved for root.rpx) which let us approximatly 22.5mB of memory free to put the rpx and additional rpls */ +#define MEM_BASE ((void*)0xC0800000) +#define MEM_SIZE ((void*)(MEM_BASE - 0x04)) +#define MEM_OFFSET ((void*)(MEM_BASE - 0x08)) +#define MEM_PART ((void*)(MEM_BASE - 0x0C)) +#define RPX_NAME ((void*)(MEM_BASE - 0x10)) +#define RPX_NAME_PENDING ((void*)(MEM_BASE - 0x14)) +#define GAME_DIR_NAME ((void*)(MEM_BASE - 0x100)) + +/* RPX_RPL_ARRAY contains an array of multiple rpl/rpl structures: */ +/* Note : The first entry is always the one referencing the rpx (cf. struct s_rpx_rpl) */ +#define RPX_RPL_ARRAY ((void*)0xC07A0000) +#define RPX_RPL_ENTRY_COUNT ((void*)(RPX_RPL_ARRAY - 0x04)) + +/* RPX Name : from which app/game, our rpx is launched */ +#define RPX_CHECK_NAME 0x63726F73 // 0xEFE00000 contains the rpx name, 0x63726F73 => cros (for smash brox : cross_f.rpx) + +/* Union for rpx name */ +typedef union uRpxName { + int name_full; + char name[4]; +} uRpxName; + +/* Struct used to organize rpx/rpl data in memory */ +typedef struct s_rpx_rpl +{ + int address; + int size; + int offset; + char name[64]; // TODO: maybe set the exact same size than dir_entry->name +} s_rpx_rpl; + +#endif /* COMMON_H */ + diff --git a/common/fs_defs.h b/common/fs_defs.h new file mode 100644 index 0000000..95b3180 --- /dev/null +++ b/common/fs_defs.h @@ -0,0 +1,79 @@ +#ifndef FS_DEFS_H +#define FS_DEFS_H + +#include "../common/types.h" + +/* FS defines and types */ +#define FS_MAX_LOCALPATH_SIZE 511 +#define FS_MAX_MOUNTPATH_SIZE 128 +#define FS_MAX_FULLPATH_SIZE (FS_MAX_LOCALPATH_SIZE + FS_MAX_MOUNTPATH_SIZE) +#define FS_MAX_ARGPATH_SIZE FS_MAX_FULLPATH_SIZE + +#define FS_STATUS_OK 0 +#define FS_RET_UNSUPPORTED_CMD 0x0400 +#define FS_RET_NO_ERROR 0x0000 +#define FS_RET_ALL_ERROR (uint)(-1) + +/* directory entry stat flag */ +#define FS_STAT_ATTRIBUTES_SIZE (48) /* size of FS-specific attributes field */ +#define FS_STAT_FLAG_IS_DIRECTORY 0x80000000 /* entry is directory */ + +/* max length of file/dir name */ +#define FS_MAX_ENTNAME_SIZE 256 + +/* typedef FSStatus, FSRetFlag*/ +typedef int FSStatus; +typedef uint FSRetFlag; +typedef uint FSFlag; + +/* FS Mount */ +typedef enum +{ + FS_SOURCETYPE_EXTERNAL = 0, // Manual mounted external device + FS_SOURCETYPE_HFIO, // Host file IO + FS_SOURCETYPE_MAX +} FSSourceType; + +typedef struct +{ + FSSourceType type; + char path[FS_MAX_ARGPATH_SIZE]; +} FSMountSource; + +/* FS Client context buffer */ +typedef struct +{ + uint8_t buffer[5888]; +} FSClient; + +/* FS command block buffer */ +typedef struct +{ + uint8_t buffer[2688]; +} FSCmdBlock; + +/* File/Dir status */ +typedef struct +{ + uint flag; + uint permission; + uint owner_id; + uint group_id; + uint size; + uint alloc_size; + uint64_t quota_size; + uint ent_id; + uint64_t ctime; + uint64_t mtime; + uint8_t attributes[FS_STAT_ATTRIBUTES_SIZE]; +} __attribute__((packed)) FSStat; + +/* Directory entry */ +typedef struct +{ + FSStat stat; + char name[FS_MAX_ENTNAME_SIZE]; +} FSDirEntry; + +#endif /* FS_DEFS_H */ + diff --git a/common/types.h b/common/types.h new file mode 100644 index 0000000..eefac9b --- /dev/null +++ b/common/types.h @@ -0,0 +1,22 @@ +#ifndef TYPES_H +#define TYPES_H + +/* Define base types */ +typedef unsigned long long uint64_t; +typedef long long int64_t; +typedef unsigned int uint32_t; +typedef unsigned int uint; +typedef int int32_t; +typedef unsigned short uint16_t; +typedef short int16_t; +typedef unsigned char uint8_t; +typedef char int8_t; + +typedef _Bool bool; +#define true 1 +#define false 0 +#define null 0 + + +#endif /* TYPES_H */ + diff --git a/installer/Makefile b/installer/Makefile new file mode 100644 index 0000000..31acf83 --- /dev/null +++ b/installer/Makefile @@ -0,0 +1,29 @@ +CC=powerpc-eabi-gcc +CFLAGS=-std=gnu99 -nostdinc -fno-builtin -c +LD=powerpc-eabi-ld +LDFLAGS=-Ttext 1800000 --oformat binary +project := . +root:=$(CURDIR) +build := $(root)/bin +libs := $(root)/../../libwiiu/bin +www :=$(root)/../../www +framework:=$(root)/../../framework + +all: clean setup client main532 + +setup: + mkdir -p $(root)/bin/ + +client: + cd ../client/ && make + +main532: + $(CC) $(CFLAGS) -DVER=532 $(project)/saviine.c + #-Wa,-a,-ad + cp -r $(root)/*.o $(build) + rm $(root)/*.o + #$(LD) $(LDFLAGS) -o $(build)/code532.bin $(build)/saviine.o $(libs)/532/*.o `find $(build) -name "*.o" ! -name "saviine.o"` + $(LD) $(LDFLAGS) -o $(build)/code532.bin $(build)/saviine.o `find $(build) -name "*.o" ! -name "saviine.o"` + +clean: + rm -rf $(build)/* diff --git a/installer/bin/code532.bin b/installer/bin/code532.bin new file mode 100644 index 0000000000000000000000000000000000000000..b4771ff2cd742980f9da5bf3cf05fb7900903f2d GIT binary patch literal 7852 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 literal 0 HcmV?d00001 diff --git a/installer/saviine.c b/installer/saviine.c new file mode 100644 index 0000000..71b4c76 --- /dev/null +++ b/installer/saviine.c @@ -0,0 +1,247 @@ +#include "../../libwiiu/src/coreinit.h" +#include "../../libwiiu/src/socket.h" +#include "../../libwiiu/src/vpad.h" + +#define assert(x) \ + do { \ + if (!(x)) \ + OSFatal("Assertion failed " #x ".\n"); \ + } while (0) + +#if VER == 532 + #include "saviine532.h" + + #define socket_lib_finish ((void (*)(void))0x10c0404) + + /* Where to install the saviine handler. */ + #define INSTALL_ADDR ((void *)0x011de000) + #define KERN_ADDRESS_TBL 0xFFEAAA10 +#endif + +void kern_write(void *addr, uint32_t value); +#define DEFAULT_SERVER_IP 0xC0A8010A +#define PRINT_TEXT1(x, y, str) { OSScreenPutFontEx(1, x, y, str); } +#define PRINT_TEXT2(x, y, _fmt, ...) { __os_snprintf(msg, 80, _fmt, __VA_ARGS__); OSScreenPutFontEx(1, x, y, msg); } +#define BTN_PRESSED (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN) + +typedef union u_serv_ip +{ + uint8_t digit[4]; + uint32_t full; +} u_serv_ip; + +void start() { + /* Load a good stack */ + asm( + "lis %r1, 0x1ab5 ;" + "ori %r1, %r1, 0xd138 ;" + ); + + /* Get a handle to coreinit.rpl. */ + unsigned int coreinit_handle; + OSDynLoad_Acquire("coreinit.rpl", &coreinit_handle); + + /* Load a few useful symbols. */ + void (*_Exit)(void) __attribute__ ((noreturn)); + void (*DCFlushRange)(const void *, int); + void *(*OSEffectiveToPhysical)(const void *); + + OSDynLoad_FindExport(coreinit_handle, 0, "_Exit", &_Exit); + OSDynLoad_FindExport(coreinit_handle, 0, "DCFlushRange", &DCFlushRange); + OSDynLoad_FindExport(coreinit_handle, 0, "OSEffectiveToPhysical", &OSEffectiveToPhysical); + + assert(_Exit); + assert(DCFlushRange); + assert(OSEffectiveToPhysical); + + /* Exploit proper begins here. */ + if (OSEffectiveToPhysical((void *)0xa0000000) != (void *)0x31000000) { + OSFatal("no ksploit"); + } else { + /* Second run. */ + + /* ****************************************************************** */ + /* IP Settings */ + /* ****************************************************************** */ + + // Set server ip + u_serv_ip ip; + ip.full = DEFAULT_SERVER_IP; + + // Prepare screen + void (*OSScreenInit)(); + unsigned int (*OSScreenGetBufferSizeEx)(unsigned int bufferNum); + unsigned int (*OSScreenSetBufferEx)(unsigned int bufferNum, void * addr); + unsigned int (*OSScreenClearBufferEx)(unsigned int bufferNum, unsigned int temp); + unsigned int (*OSScreenFlipBuffersEx)(unsigned int bufferNum); + unsigned int (*OSScreenPutFontEx)(unsigned int bufferNum, unsigned int posX, unsigned int posY, void * buffer); + + OSDynLoad_FindExport(coreinit_handle, 0, "OSScreenInit", &OSScreenInit); + OSDynLoad_FindExport(coreinit_handle, 0, "OSScreenGetBufferSizeEx", &OSScreenGetBufferSizeEx); + OSDynLoad_FindExport(coreinit_handle, 0, "OSScreenSetBufferEx", &OSScreenSetBufferEx); + OSDynLoad_FindExport(coreinit_handle, 0, "OSScreenClearBufferEx", &OSScreenClearBufferEx); + OSDynLoad_FindExport(coreinit_handle, 0, "OSScreenFlipBuffersEx", &OSScreenFlipBuffersEx); + OSDynLoad_FindExport(coreinit_handle, 0, "OSScreenPutFontEx", &OSScreenPutFontEx); + + int screen_buf0_size = 0; + int screen_buf1_size = 0; + uint32_t screen_color = 0; // (r << 24) | (g << 16) | (b << 8) | a; + char msg[80]; + + // Init screen + OSScreenInit(); + + // Set screens buffers + screen_buf0_size = OSScreenGetBufferSizeEx(0); + screen_buf1_size = OSScreenGetBufferSizeEx(1); + + OSScreenSetBufferEx(0, (void *)0xF4000000); + OSScreenSetBufferEx(1, (void *)0xF4000000 + screen_buf0_size); + + // Clear screens + OSScreenClearBufferEx(0, screen_color); + OSScreenClearBufferEx(1, screen_color); + + // Flush the cache + DCFlushRange((void *)0xF4000000, screen_buf0_size); + DCFlushRange((void *)0xF4000000 + screen_buf0_size, screen_buf1_size); + + // Flip buffers + OSScreenFlipBuffersEx(0); + OSScreenFlipBuffersEx(1); + + // Prepare vpad + unsigned int vpad_handle; + int (*VPADRead)(int controller, VPADData *buffer, unsigned int num, int *error); + OSDynLoad_Acquire("vpad.rpl", &vpad_handle); + OSDynLoad_FindExport(vpad_handle, 0, "VPADRead", &VPADRead); + + // Set server ip with buttons + uint8_t sel_ip = 3; + int error; + uint8_t button_pressed = 1; + VPADData vpad_data; + VPADRead(0, &vpad_data, 1, &error); //Read initial vpad status + while (1) + { + // Refresh screen if needed + if (button_pressed) { OSScreenFlipBuffersEx(1); OSScreenClearBufferEx(1, 0); } + + // Print message + PRINT_TEXT1(18, 1, "-- SAVIINE (a cafiine mod) --"); + PRINT_TEXT2(0, 5, "%s : %3d.%3d.%3d.%3d", "1. Server IP", ip.digit[0], ip.digit[1], ip.digit[2], ip.digit[3]); + PRINT_TEXT1(0, 6, "2. Press A to install saviine"); + PRINT_TEXT1(42, 17, "home button to exit ..."); + + // Print ip digit selector + uint8_t x_shift = 15 + 4 * sel_ip; + PRINT_TEXT1(x_shift, 4, "vvv"); + + // Read vpad + VPADRead(0, &vpad_data, 1, &error); + + // Update screen + if (button_pressed) + { + OSScreenFlipBuffersEx(1); + OSScreenClearBufferEx(1, 0); + } + // Check for buttons + else + { + // Home Button + if (vpad_data.btn_hold & BUTTON_HOME) + goto quit; + + // A Button + if (vpad_data.btn_hold & BUTTON_A) + break; + + // Left/Right Buttons + if (vpad_data.btn_hold & BUTTON_LEFT ) sel_ip = !sel_ip ? sel_ip = 3 : --sel_ip; + if (vpad_data.btn_hold & BUTTON_RIGHT) sel_ip = ++sel_ip % 4; + + // Up/Down Buttons + if (vpad_data.btn_hold & BUTTON_UP ) ip.digit[sel_ip] = ++ip.digit[sel_ip]; + if (vpad_data.btn_hold & BUTTON_DOWN) ip.digit[sel_ip] = --ip.digit[sel_ip]; + } + + // Button pressed ? + button_pressed = (vpad_data.btn_hold & BTN_PRESSED) ? 1 : 0; + } + + /* ****************************************************************** */ + /* Saviine installation */ + /* ****************************************************************** */ + + /* Copy in our resident cafiine client. */ + unsigned int len = sizeof(cafiine_text_bin); + unsigned char *loc = (unsigned char *)((char *)INSTALL_ADDR + 0xa0000000); + + while (len--) { + loc[len] = cafiine_text_bin[len]; + } + + /* server IP address */ + ((unsigned int *)loc)[0] = ip.full; //PC_IP; + + DCFlushRange(loc, sizeof(cafiine_text_bin)); + + struct magic_t { + void *real; + void *replacement; + void *call; + } *magic = (struct magic_t *)cafiine_magic_bin; + len = sizeof(cafiine_magic_bin) / sizeof(struct magic_t); + + int *space = (int *)(loc + sizeof(cafiine_text_bin)); + /* Patch branches to it. */ + while (len--) { + *(int *)(0xa0000000 | (int)magic[len].call) = (int)space - 0xa0000000; + + *space = *(int *)(0xa0000000 | (int)magic[len].real); + space++; + + *space = 0x48000002 | (((int)magic[len].real + 4) & 0x03fffffc); + space++; + DCFlushRange(space - 2, 8); + + *(int *)(0xa0000000 | (int)magic[len].real) = 0x48000002 | ((int)magic[len].replacement & 0x03fffffc); + DCFlushRange((int *)(0xa0000000 | (int)magic[len].real), 4); + } + + unsigned int *socket_finish = (unsigned int *)((char *)socket_lib_finish + 0xa0000000); + socket_finish[0] = 0x38600000; + socket_finish[1] = 0x4e800020; + + /* The fix for Splatoon and such */ + kern_write(KERN_ADDRESS_TBL + (0x12 * 4), 0x00000000); + kern_write(KERN_ADDRESS_TBL + (0x13 * 4), 0x14000000); + } + +quit: + _Exit(); +} + +/* Write a 32-bit word with kernel permissions */ +void kern_write(void *addr, uint32_t value) +{ + asm( + "li 3,1\n" + "li 4,0\n" + "mr 5,%1\n" + "li 6,0\n" + "li 7,0\n" + "lis 8,1\n" + "mr 9,%0\n" + "mr %1,1\n" + "li 0,0x3500\n" + "sc\n" + "nop\n" + "mr 1,%1\n" + : + :"r"(addr), "r"(value) + :"memory", "ctr", "lr", "0", "3", "4", "5", "6", "7", "8", "9", "10", + "11", "12" + ); +} diff --git a/installer/saviine532.h b/installer/saviine532.h new file mode 100644 index 0000000..f75c14f --- /dev/null +++ b/installer/saviine532.h @@ -0,0 +1,415 @@ +static const unsigned char cafiine_magic_bin[] = { + 0x01, 0x06, 0x08, 0xac, 0x01, 0x1d, 0xe8, 0x04, 0x01, 0x1d, 0xf2, 0x98, + 0x01, 0x06, 0x09, 0x74, 0x01, 0x1d, 0xe7, 0xd4, 0x01, 0x1d, 0xf2, 0x8c, + 0x01, 0x06, 0x54, 0x6c, 0x01, 0x1d, 0xe9, 0x24, 0x01, 0x1d, 0xf2, 0xa0, + 0x01, 0x06, 0x0a, 0xa4, 0x01, 0x1d, 0xea, 0x44, 0x01, 0x1d, 0xf2, 0xa8, + 0x01, 0x06, 0x21, 0xf8, 0x01, 0x1d, 0xe7, 0xe4, 0x01, 0x1d, 0xf2, 0x90, + 0x01, 0x06, 0xfd, 0xc8, 0x01, 0x1d, 0xf1, 0x70, 0x01, 0x1d, 0xf2, 0xb0, + 0x01, 0x06, 0x83, 0xc8, 0x01, 0x1d, 0xe8, 0x94, 0x01, 0x1d, 0xf2, 0x9c, + 0x01, 0x06, 0x85, 0x38, 0x01, 0x1d, 0xe7, 0xf4, 0x01, 0x1d, 0xf2, 0x94, + 0x01, 0x06, 0x85, 0xfc, 0x01, 0x1d, 0xe9, 0x8c, 0x01, 0x1d, 0xf2, 0xa4, + 0x01, 0x06, 0x8a, 0x08, 0x01, 0x1d, 0xea, 0xa8, 0x01, 0x1d, 0xf2, 0xac +}; +static const unsigned int cafiine_magic_bin_len = 120; +static const unsigned char cafiine_text_bin[] = { + 0x00, 0x00, 0x00, 0x00, 0x94, 0x21, 0xff, 0xe8, 0x7c, 0x08, 0x02, 0xa6, + 0x93, 0xe1, 0x00, 0x14, 0x7c, 0xbf, 0x2b, 0x79, 0x93, 0xa1, 0x00, 0x0c, + 0x7c, 0x7d, 0x1b, 0x78, 0x93, 0xc1, 0x00, 0x10, 0x7c, 0x9e, 0x23, 0x78, + 0x90, 0x01, 0x00, 0x1c, 0x41, 0xa1, 0x00, 0x0c, 0x48, 0x00, 0x00, 0x4c, + 0x40, 0x9d, 0x00, 0x48, 0x7f, 0xc4, 0xf3, 0x78, 0x7f, 0xe5, 0xfb, 0x78, + 0x7f, 0xa3, 0xeb, 0x78, 0x38, 0xc0, 0x00, 0x00, 0x4b, 0xee, 0x36, 0x69, + 0x2c, 0x03, 0x00, 0x00, 0x7f, 0xe3, 0xf8, 0x50, 0x7f, 0xde, 0x1a, 0x14, + 0x2f, 0x9f, 0x00, 0x00, 0x40, 0x80, 0xff, 0xd8, 0x80, 0x01, 0x00, 0x1c, + 0x83, 0xa1, 0x00, 0x0c, 0x7c, 0x08, 0x03, 0xa6, 0x83, 0xc1, 0x00, 0x10, + 0x83, 0xe1, 0x00, 0x14, 0x38, 0x21, 0x00, 0x18, 0x4e, 0x80, 0x00, 0x20, + 0x80, 0x01, 0x00, 0x1c, 0x38, 0x60, 0x00, 0x00, 0x83, 0xa1, 0x00, 0x0c, + 0x7c, 0x08, 0x03, 0xa6, 0x83, 0xc1, 0x00, 0x10, 0x83, 0xe1, 0x00, 0x14, + 0x38, 0x21, 0x00, 0x18, 0x4e, 0x80, 0x00, 0x20, 0x94, 0x21, 0xff, 0xe8, + 0x7c, 0x08, 0x02, 0xa6, 0x93, 0xe1, 0x00, 0x14, 0x7c, 0xbf, 0x2b, 0x79, + 0x93, 0xa1, 0x00, 0x0c, 0x7c, 0x7d, 0x1b, 0x78, 0x93, 0xc1, 0x00, 0x10, + 0x7c, 0x9e, 0x23, 0x78, 0x90, 0x01, 0x00, 0x1c, 0x41, 0xa1, 0x00, 0x0c, + 0x48, 0x00, 0x00, 0x4c, 0x40, 0x9d, 0x00, 0x48, 0x7f, 0xc4, 0xf3, 0x78, + 0x7f, 0xe5, 0xfb, 0x78, 0x7f, 0xa3, 0xeb, 0x78, 0x38, 0xc0, 0x00, 0x00, + 0x4b, 0xee, 0x2a, 0x15, 0x2c, 0x03, 0x00, 0x00, 0x7f, 0xe3, 0xf8, 0x50, + 0x7f, 0xde, 0x1a, 0x14, 0x2f, 0x9f, 0x00, 0x00, 0x40, 0x80, 0xff, 0xd8, + 0x80, 0x01, 0x00, 0x1c, 0x83, 0xa1, 0x00, 0x0c, 0x7c, 0x08, 0x03, 0xa6, + 0x83, 0xc1, 0x00, 0x10, 0x83, 0xe1, 0x00, 0x14, 0x38, 0x21, 0x00, 0x18, + 0x4e, 0x80, 0x00, 0x20, 0x80, 0x01, 0x00, 0x1c, 0x38, 0x60, 0x00, 0x00, + 0x83, 0xa1, 0x00, 0x0c, 0x7c, 0x08, 0x03, 0xa6, 0x83, 0xc1, 0x00, 0x10, + 0x83, 0xe1, 0x00, 0x14, 0x38, 0x21, 0x00, 0x18, 0x4e, 0x80, 0x00, 0x20, + 0x7c, 0x08, 0x02, 0xa6, 0x94, 0x21, 0xff, 0xc0, 0x93, 0xc1, 0x00, 0x38, + 0x7c, 0x7e, 0x1b, 0x78, 0x90, 0x01, 0x00, 0x44, 0x93, 0xe1, 0x00, 0x3c, + 0x4b, 0xee, 0x21, 0xb1, 0x38, 0x60, 0x00, 0x02, 0x38, 0x80, 0x00, 0x01, + 0x38, 0xa0, 0x00, 0x06, 0x4b, 0xee, 0x40, 0x75, 0x2f, 0x83, 0xff, 0xff, + 0x7c, 0x7f, 0x1b, 0x78, 0x41, 0x9e, 0x00, 0xa8, 0x3d, 0x20, 0x01, 0x1e, + 0x39, 0x40, 0x00, 0x02, 0x81, 0x29, 0xe0, 0x00, 0x38, 0x81, 0x00, 0x18, + 0xb1, 0x41, 0x00, 0x18, 0x38, 0xa0, 0x00, 0x10, 0x39, 0x40, 0x1c, 0xa4, + 0x91, 0x21, 0x00, 0x1c, 0xb1, 0x41, 0x00, 0x1a, 0x4b, 0xee, 0x26, 0xa1, + 0x2f, 0x83, 0x00, 0x00, 0x41, 0x9c, 0x00, 0x70, 0x3c, 0x80, 0x10, 0x01, + 0x38, 0xa0, 0x00, 0x10, 0x38, 0x84, 0x36, 0xd0, 0x38, 0x61, 0x00, 0x08, + 0x4b, 0xe5, 0x78, 0xc5, 0x7f, 0xe3, 0xfb, 0x78, 0x38, 0x81, 0x00, 0x08, + 0x38, 0xa0, 0x00, 0x10, 0x4b, 0xff, 0xfe, 0x51, 0x2f, 0x83, 0x00, 0x00, + 0x41, 0x9c, 0x00, 0x44, 0x7f, 0xe3, 0xfb, 0x78, 0x38, 0x81, 0x00, 0x28, + 0x38, 0xa0, 0x00, 0x01, 0x4b, 0xff, 0xfe, 0xcd, 0x2f, 0x83, 0x00, 0x00, + 0x41, 0x9c, 0x00, 0x2c, 0x89, 0x21, 0x00, 0x28, 0x2b, 0x89, 0x00, 0xff, + 0x41, 0x9e, 0x00, 0x20, 0x80, 0x01, 0x00, 0x44, 0x93, 0xfe, 0x00, 0x00, + 0x7c, 0x08, 0x03, 0xa6, 0x83, 0xc1, 0x00, 0x38, 0x83, 0xe1, 0x00, 0x3c, + 0x38, 0x21, 0x00, 0x40, 0x4e, 0x80, 0x00, 0x20, 0x7f, 0xe3, 0xfb, 0x78, + 0x4b, 0xee, 0x41, 0x11, 0x80, 0x01, 0x00, 0x44, 0x39, 0x20, 0xff, 0xff, + 0x91, 0x3e, 0x00, 0x00, 0x7c, 0x08, 0x03, 0xa6, 0x83, 0xc1, 0x00, 0x38, + 0x83, 0xe1, 0x00, 0x3c, 0x38, 0x21, 0x00, 0x40, 0x4e, 0x80, 0x00, 0x20, + 0x2f, 0x83, 0xff, 0xff, 0x4d, 0x9e, 0x00, 0x20, 0x4b, 0xee, 0x40, 0xe4, + 0x7c, 0x08, 0x02, 0xa6, 0x94, 0x21, 0xff, 0xd0, 0x93, 0xc1, 0x00, 0x28, + 0x3f, 0xc0, 0x10, 0x00, 0x90, 0x01, 0x00, 0x34, 0x63, 0xde, 0x00, 0xe4, + 0x93, 0x61, 0x00, 0x1c, 0x7c, 0xdb, 0x33, 0x78, 0x81, 0x3e, 0x00, 0x00, + 0x93, 0x81, 0x00, 0x20, 0x7c, 0x7c, 0x1b, 0x78, 0x81, 0x49, 0x02, 0x84, + 0x93, 0xa1, 0x00, 0x24, 0x7c, 0xbd, 0x2b, 0x78, 0x2f, 0x8a, 0x00, 0x00, + 0x93, 0xe1, 0x00, 0x2c, 0x7c, 0x3f, 0x0b, 0x78, 0x41, 0x9e, 0x00, 0x18, + 0x4b, 0xf7, 0x36, 0xe9, 0x81, 0x3e, 0x00, 0x00, 0x81, 0x49, 0x02, 0x84, + 0x2f, 0x8a, 0x00, 0x00, 0x40, 0x9e, 0xff, 0xf0, 0x2f, 0x9c, 0xff, 0xff, + 0x39, 0x40, 0x00, 0x01, 0x91, 0x49, 0x02, 0x84, 0x41, 0x9e, 0x00, 0xa4, + 0x39, 0x1d, 0xff, 0xff, 0x39, 0x40, 0x00, 0x00, 0x48, 0x00, 0x00, 0x08, + 0x7d, 0x2a, 0x4b, 0x78, 0x8c, 0xe8, 0x00, 0x01, 0x39, 0x2a, 0x00, 0x01, + 0x2f, 0x87, 0x00, 0x00, 0x40, 0x9e, 0xff, 0xf0, 0x39, 0x0a, 0x00, 0x19, + 0x80, 0xe1, 0x00, 0x00, 0x55, 0x08, 0x00, 0x36, 0x7d, 0x26, 0x4b, 0x79, + 0x7d, 0x08, 0x00, 0xd0, 0x7c, 0x3e, 0x0b, 0x78, 0x7c, 0xe1, 0x41, 0x6e, + 0x39, 0x00, 0x00, 0x0a, 0x7c, 0xc9, 0x03, 0xa6, 0x38, 0xaa, 0x00, 0x0a, + 0x38, 0x81, 0x00, 0x08, 0x99, 0x01, 0x00, 0x08, 0x91, 0x24, 0x00, 0x05, + 0x39, 0x20, 0x00, 0x00, 0x93, 0x64, 0x00, 0x01, 0x40, 0x81, 0x00, 0x80, + 0x7c, 0xfd, 0x48, 0xae, 0x7d, 0x04, 0x4a, 0x14, 0x39, 0x29, 0x00, 0x01, + 0x98, 0xe8, 0x00, 0x09, 0x42, 0x00, 0xff, 0xf0, 0x7f, 0x83, 0xe3, 0x78, + 0x4b, 0xff, 0xfc, 0xed, 0x2f, 0x83, 0x00, 0x00, 0x41, 0x9c, 0x00, 0x14, + 0x7f, 0x83, 0xe3, 0x78, 0x38, 0x9f, 0x00, 0x08, 0x38, 0xa0, 0x00, 0x01, + 0x4b, 0xff, 0xfd, 0x69, 0x81, 0x21, 0x00, 0x00, 0x91, 0x3e, 0x00, 0x00, + 0x7f, 0xc1, 0xf3, 0x78, 0x39, 0x7f, 0x00, 0x30, 0x80, 0x0b, 0x00, 0x04, + 0x3d, 0x20, 0x10, 0x00, 0x83, 0xeb, 0xff, 0xfc, 0x7c, 0x08, 0x03, 0xa6, + 0x61, 0x29, 0x00, 0xe4, 0x81, 0x29, 0x00, 0x00, 0x39, 0x40, 0x00, 0x00, + 0x83, 0x6b, 0xff, 0xec, 0x91, 0x49, 0x02, 0x84, 0x83, 0x8b, 0xff, 0xf0, + 0x83, 0xab, 0xff, 0xf4, 0x83, 0xcb, 0xff, 0xf8, 0x7d, 0x61, 0x5b, 0x78, + 0x4e, 0x80, 0x00, 0x20, 0x39, 0x40, 0x00, 0x01, 0x7d, 0x49, 0x03, 0xa6, + 0x4b, 0xff, 0xff, 0x7c, 0x7c, 0x08, 0x02, 0xa6, 0x94, 0x21, 0xff, 0xd0, + 0x93, 0xc1, 0x00, 0x28, 0x3f, 0xc0, 0x10, 0x00, 0x90, 0x01, 0x00, 0x34, + 0x63, 0xde, 0x00, 0xe4, 0x93, 0x41, 0x00, 0x18, 0x7c, 0xda, 0x33, 0x78, + 0x81, 0x3e, 0x00, 0x00, 0x93, 0x61, 0x00, 0x1c, 0x7c, 0x7b, 0x1b, 0x78, + 0x81, 0x49, 0x02, 0x84, 0x93, 0x81, 0x00, 0x20, 0x7c, 0xbc, 0x2b, 0x78, + 0x2f, 0x8a, 0x00, 0x00, 0x93, 0xa1, 0x00, 0x24, 0x93, 0xe1, 0x00, 0x2c, + 0x7c, 0x9d, 0x23, 0x78, 0x7c, 0x3f, 0x0b, 0x78, 0x41, 0x9e, 0x00, 0x18, + 0x4b, 0xf7, 0x35, 0x8d, 0x81, 0x3e, 0x00, 0x00, 0x81, 0x49, 0x02, 0x84, + 0x2f, 0x8a, 0x00, 0x00, 0x40, 0x9e, 0xff, 0xf0, 0x2f, 0x9b, 0xff, 0xff, + 0x39, 0x40, 0x00, 0x01, 0x91, 0x49, 0x02, 0x84, 0x41, 0x9e, 0x00, 0x7c, + 0x39, 0x3c, 0x00, 0x18, 0x81, 0x41, 0x00, 0x00, 0x55, 0x29, 0x00, 0x36, + 0x2f, 0x9c, 0x00, 0x00, 0x7d, 0x29, 0x00, 0xd0, 0x7c, 0x3e, 0x0b, 0x78, + 0x7d, 0x41, 0x49, 0x6e, 0x39, 0x20, 0x00, 0x0b, 0x7f, 0x89, 0x03, 0xa6, + 0x38, 0xbc, 0x00, 0x09, 0x38, 0x81, 0x00, 0x08, 0x99, 0x21, 0x00, 0x08, + 0x93, 0x44, 0x00, 0x01, 0x39, 0x20, 0x00, 0x00, 0x93, 0x84, 0x00, 0x05, + 0x40, 0x9d, 0x00, 0x18, 0x7d, 0x1d, 0x48, 0xae, 0x7d, 0x44, 0x4a, 0x14, + 0x39, 0x29, 0x00, 0x01, 0x99, 0x0a, 0x00, 0x09, 0x42, 0x00, 0xff, 0xf0, + 0x7f, 0x63, 0xdb, 0x78, 0x4b, 0xff, 0xfb, 0xb1, 0x7f, 0x63, 0xdb, 0x78, + 0x38, 0x9f, 0x00, 0x08, 0x38, 0xa0, 0x00, 0x01, 0x4b, 0xff, 0xfc, 0x35, + 0x81, 0x21, 0x00, 0x00, 0x91, 0x3e, 0x00, 0x00, 0x7f, 0xc1, 0xf3, 0x78, + 0x39, 0x7f, 0x00, 0x30, 0x80, 0x0b, 0x00, 0x04, 0x3d, 0x20, 0x10, 0x00, + 0x83, 0xeb, 0xff, 0xfc, 0x7c, 0x08, 0x03, 0xa6, 0x61, 0x29, 0x00, 0xe4, + 0x81, 0x29, 0x00, 0x00, 0x39, 0x40, 0x00, 0x00, 0x83, 0x4b, 0xff, 0xe8, + 0x91, 0x49, 0x02, 0x84, 0x83, 0x6b, 0xff, 0xec, 0x83, 0x8b, 0xff, 0xf0, + 0x83, 0xab, 0xff, 0xf4, 0x83, 0xcb, 0xff, 0xf8, 0x7d, 0x61, 0x5b, 0x78, + 0x4e, 0x80, 0x00, 0x20, 0x7c, 0x08, 0x02, 0xa6, 0x94, 0x21, 0xff, 0xd8, + 0x93, 0xe1, 0x00, 0x24, 0x3f, 0xe0, 0x10, 0x00, 0x90, 0x01, 0x00, 0x2c, + 0x63, 0xff, 0x00, 0xe4, 0x93, 0x81, 0x00, 0x18, 0x7c, 0x9c, 0x23, 0x78, + 0x81, 0x3f, 0x00, 0x00, 0x93, 0xa1, 0x00, 0x1c, 0x7c, 0xbd, 0x2b, 0x78, + 0x81, 0x49, 0x02, 0x84, 0x93, 0xc1, 0x00, 0x20, 0x7c, 0x7e, 0x1b, 0x78, + 0x2f, 0x8a, 0x00, 0x00, 0x41, 0x9e, 0x00, 0x18, 0x4b, 0xf7, 0x34, 0x71, + 0x81, 0x3f, 0x00, 0x00, 0x81, 0x49, 0x02, 0x84, 0x2f, 0x8a, 0x00, 0x00, + 0x40, 0x9e, 0xff, 0xf0, 0x2f, 0x9e, 0xff, 0xff, 0x39, 0x40, 0x00, 0x01, + 0x91, 0x49, 0x02, 0x84, 0x41, 0x9e, 0x00, 0x9c, 0x39, 0x20, 0x00, 0x02, + 0x7f, 0xc3, 0xf3, 0x78, 0x38, 0x81, 0x00, 0x08, 0x38, 0xa0, 0x00, 0x05, + 0x99, 0x21, 0x00, 0x08, 0x93, 0xa1, 0x00, 0x09, 0x4b, 0xff, 0xfa, 0xd5, + 0x2f, 0x83, 0x00, 0x00, 0x41, 0x9c, 0x00, 0x78, 0x7f, 0xc3, 0xf3, 0x78, + 0x38, 0x81, 0x00, 0x10, 0x38, 0xa0, 0x00, 0x01, 0x4b, 0xff, 0xfb, 0x51, + 0x2f, 0x83, 0x00, 0x00, 0x41, 0x9c, 0x00, 0x60, 0x89, 0x21, 0x00, 0x10, + 0x2b, 0x89, 0x00, 0xff, 0x41, 0x9e, 0x00, 0x54, 0x7f, 0xc3, 0xf3, 0x78, + 0x7f, 0x84, 0xe3, 0x78, 0x38, 0xa0, 0x00, 0x04, 0x4b, 0xff, 0xfb, 0x2d, + 0x2f, 0x83, 0x00, 0x00, 0x41, 0x9c, 0x00, 0x3c, 0x80, 0x01, 0x00, 0x2c, + 0x3d, 0x20, 0x10, 0x00, 0x61, 0x29, 0x00, 0xe4, 0x39, 0x40, 0x00, 0x00, + 0x7c, 0x08, 0x03, 0xa6, 0x81, 0x29, 0x00, 0x00, 0x83, 0x81, 0x00, 0x18, + 0x38, 0x60, 0x00, 0x00, 0x83, 0xa1, 0x00, 0x1c, 0x83, 0xc1, 0x00, 0x20, + 0x83, 0xe1, 0x00, 0x24, 0x91, 0x49, 0x02, 0x84, 0x38, 0x21, 0x00, 0x28, + 0x4e, 0x80, 0x00, 0x20, 0x80, 0x01, 0x00, 0x2c, 0x3d, 0x20, 0x10, 0x00, + 0x61, 0x29, 0x00, 0xe4, 0x39, 0x40, 0x00, 0x00, 0x7c, 0x08, 0x03, 0xa6, + 0x81, 0x29, 0x00, 0x00, 0x83, 0x81, 0x00, 0x18, 0x38, 0x60, 0xff, 0xff, + 0x83, 0xa1, 0x00, 0x1c, 0x83, 0xc1, 0x00, 0x20, 0x83, 0xe1, 0x00, 0x24, + 0x91, 0x49, 0x02, 0x84, 0x38, 0x21, 0x00, 0x28, 0x4e, 0x80, 0x00, 0x20, + 0x7c, 0x08, 0x02, 0xa6, 0x94, 0x21, 0xff, 0xd8, 0x93, 0xe1, 0x00, 0x24, + 0x3f, 0xe0, 0x10, 0x00, 0x90, 0x01, 0x00, 0x2c, 0x63, 0xff, 0x00, 0xe4, + 0x93, 0x81, 0x00, 0x18, 0x7c, 0x7c, 0x1b, 0x78, 0x81, 0x3f, 0x00, 0x00, + 0x93, 0xa1, 0x00, 0x1c, 0x7c, 0x9d, 0x23, 0x78, 0x81, 0x49, 0x02, 0x84, + 0x93, 0xc1, 0x00, 0x20, 0x7c, 0xbe, 0x2b, 0x78, 0x2f, 0x8a, 0x00, 0x00, + 0x41, 0x9e, 0x00, 0x18, 0x4b, 0xf7, 0x33, 0x3d, 0x81, 0x3f, 0x00, 0x00, + 0x81, 0x49, 0x02, 0x84, 0x2f, 0x8a, 0x00, 0x00, 0x40, 0x9e, 0xff, 0xf0, + 0x39, 0x40, 0x00, 0x01, 0x7f, 0x83, 0xe3, 0x78, 0x91, 0x49, 0x02, 0x84, + 0x38, 0x81, 0x00, 0x08, 0x39, 0x20, 0x00, 0x0c, 0x38, 0xa0, 0x00, 0x09, + 0x99, 0x21, 0x00, 0x08, 0x93, 0xa1, 0x00, 0x09, 0x93, 0xc1, 0x00, 0x0d, + 0x4b, 0xff, 0xf9, 0xa5, 0x80, 0x01, 0x00, 0x2c, 0x3d, 0x20, 0x10, 0x00, + 0x61, 0x29, 0x00, 0xe4, 0x39, 0x40, 0x00, 0x00, 0x7c, 0x08, 0x03, 0xa6, + 0x81, 0x29, 0x00, 0x00, 0x83, 0x81, 0x00, 0x18, 0x83, 0xa1, 0x00, 0x1c, + 0x83, 0xc1, 0x00, 0x20, 0x83, 0xe1, 0x00, 0x24, 0x91, 0x49, 0x02, 0x84, + 0x38, 0x21, 0x00, 0x28, 0x4e, 0x80, 0x00, 0x20, 0x2f, 0x83, 0xff, 0xff, + 0x7c, 0x08, 0x02, 0xa6, 0x94, 0x21, 0xff, 0xe0, 0x93, 0x81, 0x00, 0x10, + 0x7c, 0x7c, 0x1b, 0x78, 0x93, 0xe1, 0x00, 0x1c, 0x7c, 0x3f, 0x0b, 0x78, + 0x90, 0x01, 0x00, 0x24, 0x93, 0x61, 0x00, 0x0c, 0x93, 0xa1, 0x00, 0x14, + 0x93, 0xc1, 0x00, 0x18, 0x41, 0x9e, 0x00, 0xdc, 0x3f, 0xa0, 0x10, 0x00, + 0x7c, 0x9e, 0x23, 0x78, 0x63, 0xbd, 0x00, 0xe4, 0x7c, 0xbb, 0x2b, 0x78, + 0x81, 0x3d, 0x00, 0x00, 0x81, 0x49, 0x02, 0x84, 0x2f, 0x8a, 0x00, 0x00, + 0x41, 0x9e, 0x00, 0x18, 0x4b, 0xf7, 0x32, 0x7d, 0x81, 0x3d, 0x00, 0x00, + 0x81, 0x49, 0x02, 0x84, 0x2f, 0x8a, 0x00, 0x00, 0x40, 0x9e, 0xff, 0xf0, + 0x39, 0x40, 0x00, 0x01, 0x39, 0x1e, 0xff, 0xff, 0x91, 0x49, 0x02, 0x84, + 0x39, 0x20, 0x00, 0x00, 0x48, 0x00, 0x00, 0x08, 0x7d, 0x49, 0x53, 0x78, + 0x8c, 0xe8, 0x00, 0x01, 0x39, 0x49, 0x00, 0x01, 0x2f, 0x87, 0x00, 0x00, + 0x40, 0x9e, 0xff, 0xf0, 0x39, 0x09, 0x00, 0x15, 0x80, 0xe1, 0x00, 0x00, + 0x55, 0x08, 0x00, 0x36, 0x7d, 0x46, 0x53, 0x79, 0x7d, 0x08, 0x00, 0xd0, + 0x7c, 0x3d, 0x0b, 0x78, 0x7c, 0xe1, 0x41, 0x6e, 0x38, 0xa9, 0x00, 0x06, + 0x7c, 0xc9, 0x03, 0xa6, 0x39, 0x20, 0x00, 0x00, 0x38, 0x81, 0x00, 0x08, + 0x9b, 0x61, 0x00, 0x08, 0x91, 0x44, 0x00, 0x01, 0x40, 0x81, 0x00, 0x70, + 0x7d, 0x1e, 0x48, 0xae, 0x7d, 0x44, 0x4a, 0x14, 0x39, 0x29, 0x00, 0x01, + 0x99, 0x0a, 0x00, 0x05, 0x42, 0x00, 0xff, 0xf0, 0x39, 0x20, 0x00, 0x00, + 0x7f, 0x83, 0xe3, 0x78, 0x99, 0x2a, 0x00, 0x06, 0x4b, 0xff, 0xf8, 0x89, + 0x3d, 0x40, 0x10, 0x00, 0x61, 0x4a, 0x00, 0xe4, 0x81, 0x21, 0x00, 0x00, + 0x81, 0x4a, 0x00, 0x00, 0x91, 0x3d, 0x00, 0x00, 0x39, 0x20, 0x00, 0x00, + 0x91, 0x2a, 0x02, 0x84, 0x7f, 0xa1, 0xeb, 0x78, 0x39, 0x7f, 0x00, 0x20, + 0x80, 0x0b, 0x00, 0x04, 0x83, 0xeb, 0xff, 0xfc, 0x7c, 0x08, 0x03, 0xa6, + 0x83, 0x6b, 0xff, 0xec, 0x83, 0x8b, 0xff, 0xf0, 0x83, 0xab, 0xff, 0xf4, + 0x83, 0xcb, 0xff, 0xf8, 0x7d, 0x61, 0x5b, 0x78, 0x4e, 0x80, 0x00, 0x20, + 0x39, 0x40, 0x00, 0x01, 0x7d, 0x49, 0x03, 0xa6, 0x4b, 0xff, 0xff, 0x8c, + 0x3d, 0x20, 0x01, 0x1e, 0x81, 0x29, 0xf2, 0x8c, 0x7d, 0x29, 0x03, 0xa6, + 0x4e, 0x80, 0x04, 0x20, 0x3d, 0x20, 0x01, 0x1e, 0x81, 0x29, 0xf2, 0x90, + 0x7d, 0x29, 0x03, 0xa6, 0x4e, 0x80, 0x04, 0x20, 0x3d, 0x20, 0x01, 0x1e, + 0x81, 0x29, 0xf2, 0x94, 0x7d, 0x29, 0x03, 0xa6, 0x4e, 0x80, 0x04, 0x20, + 0x7c, 0x08, 0x02, 0xa6, 0x94, 0x21, 0xff, 0xf0, 0x3d, 0x40, 0x0a, 0x00, + 0x93, 0xe1, 0x00, 0x0c, 0x3f, 0xe0, 0x10, 0x00, 0x90, 0x01, 0x00, 0x14, + 0x63, 0xff, 0x00, 0xe4, 0x81, 0x3f, 0x00, 0x00, 0x7f, 0x89, 0x50, 0x00, + 0x41, 0x9e, 0x00, 0x24, 0x3d, 0x20, 0x01, 0x1e, 0x80, 0x01, 0x00, 0x14, + 0x81, 0x29, 0xf2, 0x98, 0x83, 0xe1, 0x00, 0x0c, 0x7c, 0x08, 0x03, 0xa6, + 0x38, 0x21, 0x00, 0x10, 0x7d, 0x29, 0x03, 0xa6, 0x4e, 0x80, 0x04, 0x20, + 0x3d, 0x20, 0x10, 0x05, 0x38, 0x80, 0x00, 0x40, 0x81, 0x29, 0xe9, 0xc0, + 0x38, 0x60, 0x02, 0x88, 0x7d, 0x29, 0x03, 0xa6, 0x4e, 0x80, 0x04, 0x21, + 0x38, 0x80, 0x00, 0x00, 0x90, 0x7f, 0x00, 0x00, 0x38, 0xa0, 0x02, 0x88, + 0x4b, 0xe5, 0x71, 0xe5, 0x3d, 0x20, 0x01, 0x1e, 0x80, 0x01, 0x00, 0x14, + 0x81, 0x29, 0xf2, 0x98, 0x83, 0xe1, 0x00, 0x0c, 0x7c, 0x08, 0x03, 0xa6, + 0x38, 0x21, 0x00, 0x10, 0x7d, 0x29, 0x03, 0xa6, 0x4e, 0x80, 0x04, 0x20, + 0x7c, 0x08, 0x02, 0xa6, 0x94, 0x21, 0xff, 0xf0, 0x3d, 0x40, 0x0a, 0x00, + 0x93, 0xe1, 0x00, 0x0c, 0x3f, 0xe0, 0x10, 0x00, 0x90, 0x01, 0x00, 0x14, + 0x63, 0xff, 0x00, 0xe4, 0x81, 0x3f, 0x00, 0x00, 0x7f, 0x89, 0x50, 0x00, + 0x41, 0x9e, 0x00, 0x24, 0x3d, 0x20, 0x01, 0x1e, 0x80, 0x01, 0x00, 0x14, + 0x81, 0x29, 0xf2, 0x9c, 0x83, 0xe1, 0x00, 0x0c, 0x7c, 0x08, 0x03, 0xa6, + 0x38, 0x21, 0x00, 0x10, 0x7d, 0x29, 0x03, 0xa6, 0x4e, 0x80, 0x04, 0x20, + 0x3d, 0x20, 0x10, 0x05, 0x38, 0x80, 0x00, 0x40, 0x81, 0x29, 0xe9, 0xc0, + 0x38, 0x60, 0x02, 0x88, 0x7d, 0x29, 0x03, 0xa6, 0x4e, 0x80, 0x04, 0x21, + 0x38, 0x80, 0x00, 0x00, 0x90, 0x7f, 0x00, 0x00, 0x38, 0xa0, 0x02, 0x88, + 0x4b, 0xe5, 0x71, 0x55, 0x3d, 0x20, 0x01, 0x1e, 0x80, 0x01, 0x00, 0x14, + 0x81, 0x29, 0xf2, 0x9c, 0x83, 0xe1, 0x00, 0x0c, 0x7c, 0x08, 0x03, 0xa6, + 0x38, 0x21, 0x00, 0x10, 0x7d, 0x29, 0x03, 0xa6, 0x4e, 0x80, 0x04, 0x20, + 0x7c, 0x08, 0x02, 0xa6, 0x3d, 0x20, 0x01, 0x1e, 0x94, 0x21, 0xff, 0xf0, + 0x81, 0x29, 0xf2, 0xa0, 0x93, 0xe1, 0x00, 0x0c, 0x7d, 0x29, 0x03, 0xa6, + 0x90, 0x01, 0x00, 0x14, 0x4e, 0x80, 0x04, 0x21, 0x3d, 0x20, 0x10, 0x00, + 0x61, 0x29, 0x00, 0xe4, 0x3d, 0x40, 0x0a, 0x00, 0x81, 0x29, 0x00, 0x00, + 0x7c, 0x7f, 0x1b, 0x78, 0x7f, 0x89, 0x50, 0x00, 0x41, 0x9e, 0x00, 0x18, + 0x2b, 0x83, 0x00, 0x1f, 0x41, 0x9d, 0x00, 0x10, 0x54, 0x63, 0x10, 0x3a, + 0x7c, 0x69, 0x1a, 0x14, 0x4b, 0xff, 0xf7, 0xbd, 0x80, 0x01, 0x00, 0x14, + 0x7f, 0xe3, 0xfb, 0x78, 0x83, 0xe1, 0x00, 0x0c, 0x7c, 0x08, 0x03, 0xa6, + 0x38, 0x21, 0x00, 0x10, 0x4e, 0x80, 0x00, 0x20, 0x7c, 0x08, 0x02, 0xa6, + 0x3d, 0x20, 0x01, 0x1e, 0x94, 0x21, 0xff, 0xf0, 0x81, 0x29, 0xf2, 0xa4, + 0x93, 0xc1, 0x00, 0x08, 0x7c, 0x7e, 0x1b, 0x78, 0x93, 0xe1, 0x00, 0x0c, + 0x7d, 0x29, 0x03, 0xa6, 0x90, 0x01, 0x00, 0x14, 0x4e, 0x80, 0x04, 0x21, + 0x3d, 0x20, 0x10, 0x00, 0x61, 0x29, 0x00, 0xe4, 0x7c, 0x7f, 0x1b, 0x78, + 0x80, 0xe9, 0x00, 0x00, 0x3d, 0x20, 0x0a, 0x00, 0x7f, 0x87, 0x48, 0x00, + 0x41, 0x9e, 0x00, 0x5c, 0x2f, 0x83, 0x00, 0x00, 0x41, 0x9c, 0x00, 0x54, + 0x39, 0x00, 0x00, 0x20, 0x39, 0x47, 0x00, 0x7c, 0x39, 0x20, 0x00, 0x00, + 0x7d, 0x09, 0x03, 0xa6, 0x48, 0x00, 0x00, 0x0c, 0x39, 0x29, 0x00, 0x01, + 0x42, 0x40, 0x00, 0x38, 0x85, 0x0a, 0x00, 0x04, 0x2f, 0x88, 0x00, 0x00, + 0x40, 0x9e, 0xff, 0xf0, 0x39, 0x49, 0x00, 0x20, 0x39, 0x29, 0x00, 0x40, + 0x55, 0x4a, 0x10, 0x3a, 0x55, 0x29, 0x10, 0x3a, 0x7f, 0xc7, 0x51, 0x2e, + 0x3d, 0x40, 0x10, 0x00, 0x61, 0x4a, 0x00, 0xe4, 0x80, 0x6a, 0x00, 0x00, + 0x7c, 0x63, 0x4a, 0x14, 0x4b, 0xff, 0xf7, 0x09, 0x80, 0x01, 0x00, 0x14, + 0x7f, 0xe3, 0xfb, 0x78, 0x83, 0xc1, 0x00, 0x08, 0x7c, 0x08, 0x03, 0xa6, + 0x83, 0xe1, 0x00, 0x0c, 0x38, 0x21, 0x00, 0x10, 0x4e, 0x80, 0x00, 0x20, + 0x7c, 0x08, 0x02, 0xa6, 0x94, 0x21, 0xff, 0xf0, 0x3d, 0x20, 0x10, 0x00, + 0x3d, 0x40, 0x0a, 0x00, 0x61, 0x29, 0x00, 0xe4, 0x93, 0xe1, 0x00, 0x0c, + 0x90, 0x01, 0x00, 0x14, 0x7c, 0x7f, 0x1b, 0x78, 0x81, 0x29, 0x00, 0x00, + 0x7f, 0x89, 0x50, 0x00, 0x41, 0x9e, 0x00, 0x18, 0x2b, 0x83, 0x00, 0x1f, + 0x41, 0x9d, 0x00, 0x10, 0x54, 0x6a, 0x10, 0x3a, 0x7c, 0x69, 0x50, 0x2e, + 0x4b, 0xff, 0xf7, 0xa9, 0x3d, 0x20, 0x01, 0x1e, 0x80, 0x01, 0x00, 0x14, + 0x81, 0x29, 0xf2, 0xa8, 0x7f, 0xe3, 0xfb, 0x78, 0x7c, 0x08, 0x03, 0xa6, + 0x83, 0xe1, 0x00, 0x0c, 0x38, 0x21, 0x00, 0x10, 0x7d, 0x29, 0x03, 0xa6, + 0x4e, 0x80, 0x04, 0x20, 0x7c, 0x08, 0x02, 0xa6, 0x94, 0x21, 0xff, 0xf0, + 0x3d, 0x20, 0x10, 0x00, 0x61, 0x29, 0x00, 0xe4, 0x93, 0xc1, 0x00, 0x08, + 0x90, 0x01, 0x00, 0x14, 0x7c, 0x7e, 0x1b, 0x78, 0x93, 0xe1, 0x00, 0x0c, + 0x81, 0x09, 0x00, 0x00, 0x3d, 0x20, 0x0a, 0x00, 0x7f, 0x88, 0x48, 0x00, + 0x41, 0x9e, 0x00, 0x58, 0x39, 0x40, 0x00, 0x20, 0x39, 0x28, 0x00, 0x7c, + 0x3b, 0xe0, 0x00, 0x00, 0x7d, 0x49, 0x03, 0xa6, 0x48, 0x00, 0x00, 0x0c, + 0x3b, 0xff, 0x00, 0x01, 0x42, 0x40, 0x00, 0x3c, 0x85, 0x49, 0x00, 0x04, + 0x7f, 0x9e, 0x50, 0x00, 0x40, 0x9e, 0xff, 0xf0, 0x39, 0x3f, 0x00, 0x40, + 0x3b, 0xff, 0x00, 0x20, 0x55, 0x29, 0x10, 0x3a, 0x57, 0xff, 0x10, 0x3a, + 0x7c, 0x68, 0x48, 0x2e, 0x4b, 0xff, 0xf7, 0x15, 0x3d, 0x20, 0x10, 0x00, + 0x61, 0x29, 0x00, 0xe4, 0x39, 0x40, 0x00, 0x00, 0x81, 0x29, 0x00, 0x00, + 0x7d, 0x49, 0xf9, 0x2e, 0x3d, 0x20, 0x01, 0x1e, 0x80, 0x01, 0x00, 0x14, + 0x81, 0x29, 0xf2, 0xac, 0x7f, 0xc3, 0xf3, 0x78, 0x83, 0xe1, 0x00, 0x0c, + 0x7c, 0x08, 0x03, 0xa6, 0x83, 0xc1, 0x00, 0x08, 0x7d, 0x29, 0x03, 0xa6, + 0x38, 0x21, 0x00, 0x10, 0x4e, 0x80, 0x04, 0x20, 0x94, 0x21, 0xfd, 0x48, + 0x7c, 0x08, 0x02, 0xa6, 0x39, 0x28, 0x00, 0x01, 0x92, 0xa1, 0x02, 0x8c, + 0x7c, 0xd5, 0x33, 0x78, 0x93, 0x01, 0x02, 0x98, 0x7c, 0x98, 0x23, 0x78, + 0x93, 0x61, 0x02, 0xa4, 0x7c, 0xa4, 0x2b, 0x78, 0x93, 0xe1, 0x02, 0xb4, + 0x7c, 0xfb, 0x3b, 0x78, 0x7c, 0xbf, 0x2b, 0x78, 0x38, 0xe0, 0xff, 0xff, + 0x7c, 0xc5, 0x33, 0x78, 0x38, 0xc1, 0x02, 0x7c, 0x93, 0xc1, 0x02, 0xb0, + 0x7c, 0x7e, 0x1b, 0x78, 0x90, 0x01, 0x02, 0xbc, 0x92, 0x81, 0x02, 0x88, + 0x92, 0xc1, 0x02, 0x90, 0x92, 0xe1, 0x02, 0x94, 0x93, 0x21, 0x02, 0x9c, + 0x93, 0x41, 0x02, 0xa0, 0x93, 0x81, 0x02, 0xa8, 0x93, 0xa1, 0x02, 0xac, + 0x91, 0x01, 0x02, 0x7c, 0x91, 0x21, 0x02, 0x78, 0x4b, 0xe9, 0x0a, 0xd1, + 0x2c, 0x03, 0x00, 0x00, 0x40, 0x82, 0x02, 0xf4, 0x39, 0x40, 0x00, 0x72, + 0x3f, 0x80, 0x10, 0x00, 0x99, 0x41, 0x02, 0x80, 0x3b, 0x20, 0x00, 0x2f, + 0x98, 0x61, 0x02, 0x81, 0x3b, 0x40, 0x00, 0x00, 0x63, 0x9c, 0x00, 0xe4, + 0x57, 0x1d, 0x10, 0x3a, 0x3e, 0xc0, 0x10, 0x05, 0x3e, 0xe0, 0x10, 0x0b, + 0x80, 0xa1, 0x02, 0x7c, 0x7f, 0xc3, 0xf3, 0x78, 0x7f, 0xe4, 0xfb, 0x78, + 0x38, 0xc1, 0x00, 0x08, 0x38, 0xe0, 0xff, 0xff, 0x4b, 0xe9, 0x0b, 0x79, + 0x2f, 0x83, 0x00, 0x00, 0x40, 0x9e, 0x01, 0xf8, 0x89, 0x75, 0x00, 0x00, + 0x2f, 0x8b, 0x00, 0x00, 0x41, 0x9e, 0x02, 0x94, 0x39, 0x81, 0x01, 0x6b, + 0x39, 0x20, 0x00, 0x00, 0x39, 0x29, 0x00, 0x01, 0x9d, 0x6c, 0x00, 0x01, + 0x7d, 0x75, 0x48, 0xae, 0x2f, 0x8b, 0x00, 0x00, 0x40, 0x9e, 0xff, 0xf0, + 0x39, 0x89, 0x00, 0x01, 0x89, 0x61, 0x00, 0x6c, 0x39, 0x41, 0x00, 0x08, + 0x7d, 0x2a, 0x4a, 0x14, 0x2f, 0x8b, 0x00, 0x00, 0x9b, 0x29, 0x01, 0x64, + 0x41, 0x9e, 0x00, 0x28, 0x39, 0x21, 0x01, 0x6c, 0x38, 0xcc, 0xff, 0xff, + 0x7c, 0xc9, 0x32, 0x14, 0x39, 0x21, 0x00, 0x6c, 0x9d, 0x66, 0x00, 0x01, + 0x39, 0x8c, 0x00, 0x01, 0x8d, 0x69, 0x00, 0x01, 0x2f, 0x8b, 0x00, 0x00, + 0x40, 0x9e, 0xff, 0xf0, 0x39, 0x41, 0x00, 0x08, 0x38, 0x81, 0x01, 0x6c, + 0x7d, 0x8a, 0x62, 0x14, 0x38, 0xa0, 0x00, 0xfb, 0x9b, 0x4c, 0x01, 0x64, + 0x81, 0x3c, 0x00, 0x00, 0x7c, 0x69, 0xe8, 0x2e, 0x4b, 0xff, 0xfa, 0x01, + 0x81, 0x21, 0x00, 0x08, 0x2f, 0x89, 0x00, 0x00, 0x41, 0x9c, 0x01, 0xbc, + 0x39, 0x20, 0x00, 0x66, 0x9b, 0x41, 0x02, 0x70, 0x99, 0x21, 0x02, 0x6c, + 0x39, 0x20, 0x00, 0x69, 0x99, 0x21, 0x02, 0x6d, 0x39, 0x20, 0x00, 0x6c, + 0x99, 0x21, 0x02, 0x6e, 0x39, 0x20, 0x00, 0x65, 0x99, 0x21, 0x02, 0x6f, + 0x38, 0x81, 0x02, 0x6c, 0x38, 0xa0, 0x00, 0xfb, 0x81, 0x3c, 0x00, 0x00, + 0x7c, 0x69, 0xe8, 0x2e, 0x4b, 0xff, 0xf9, 0xbd, 0x7f, 0xc3, 0xf3, 0x78, + 0x7f, 0xe4, 0xfb, 0x78, 0x38, 0xa1, 0x01, 0x6c, 0x38, 0xc1, 0x02, 0x80, + 0x38, 0xe1, 0x02, 0x78, 0x7f, 0x68, 0xdb, 0x78, 0x4b, 0xe9, 0x02, 0x85, + 0x2f, 0x83, 0x00, 0x00, 0x41, 0xbc, 0xfe, 0xf4, 0x81, 0x3c, 0x00, 0x00, + 0x38, 0x81, 0x01, 0x6c, 0x38, 0xa0, 0x00, 0xfb, 0x7c, 0x69, 0xe8, 0x2e, + 0x4b, 0xff, 0xf9, 0x85, 0x81, 0x3c, 0x00, 0x00, 0x80, 0xc1, 0x02, 0x78, + 0x7f, 0x04, 0xc3, 0x78, 0x7c, 0x69, 0xe8, 0x2e, 0x38, 0xa1, 0x01, 0x6c, + 0x4b, 0xff, 0xf5, 0x09, 0x81, 0x36, 0xe9, 0xc0, 0x38, 0x60, 0x00, 0x00, + 0x38, 0x80, 0x00, 0x40, 0x60, 0x63, 0xc8, 0x00, 0x7d, 0x29, 0x03, 0xa6, + 0x4e, 0x80, 0x04, 0x21, 0x7c, 0x74, 0x1b, 0x78, 0x48, 0x00, 0x00, 0x10, + 0x80, 0xc1, 0x02, 0x78, 0x7c, 0x69, 0xe8, 0x2e, 0x4b, 0xff, 0xf6, 0x31, + 0x81, 0x01, 0x02, 0x78, 0x38, 0xe0, 0x00, 0x00, 0x7f, 0xe4, 0xfb, 0x78, + 0x7e, 0x85, 0xa3, 0x78, 0x39, 0x20, 0x00, 0x00, 0x38, 0xc0, 0x00, 0x01, + 0x60, 0xe7, 0xc8, 0x00, 0x7f, 0x6a, 0xdb, 0x78, 0x7f, 0xc3, 0xf3, 0x78, + 0x4b, 0xe9, 0x03, 0x89, 0x7e, 0x84, 0xa3, 0x78, 0x2f, 0x83, 0x00, 0x00, + 0x7c, 0x65, 0x1b, 0x78, 0x90, 0x61, 0x02, 0x74, 0x81, 0x3c, 0x00, 0x00, + 0x41, 0x9d, 0xff, 0xb8, 0x7c, 0x69, 0xe8, 0x2e, 0x38, 0x81, 0x02, 0x74, + 0x80, 0xa1, 0x02, 0x78, 0x4b, 0xff, 0xf7, 0x0d, 0x80, 0xa1, 0x02, 0x78, + 0x7f, 0x67, 0xdb, 0x78, 0x7f, 0xe4, 0xfb, 0x78, 0x38, 0xc0, 0x00, 0x00, + 0x7f, 0xc3, 0xf3, 0x78, 0x4b, 0xe9, 0x07, 0x71, 0x81, 0x37, 0x48, 0x7c, + 0x7e, 0x83, 0xa3, 0x78, 0x7d, 0x29, 0x03, 0xa6, 0x4e, 0x80, 0x04, 0x21, + 0x80, 0xa1, 0x02, 0x78, 0x7f, 0xe4, 0xfb, 0x78, 0x38, 0xc0, 0xff, 0xff, + 0x7f, 0xc3, 0xf3, 0x78, 0x4b, 0xe9, 0x02, 0xa5, 0x80, 0xa1, 0x02, 0x7c, + 0x7f, 0xc3, 0xf3, 0x78, 0x7f, 0xe4, 0xfb, 0x78, 0x38, 0xc1, 0x00, 0x08, + 0x38, 0xe0, 0xff, 0xff, 0x4b, 0xe9, 0x09, 0x85, 0x2f, 0x83, 0x00, 0x00, + 0x41, 0x9e, 0xfe, 0x10, 0x80, 0xa1, 0x02, 0x7c, 0x7f, 0xc3, 0xf3, 0x78, + 0x7f, 0xe4, 0xfb, 0x78, 0x38, 0xc0, 0x00, 0x00, 0x4b, 0xe9, 0x08, 0xe9, + 0x38, 0x60, 0x00, 0x00, 0x80, 0x01, 0x02, 0xbc, 0x82, 0x81, 0x02, 0x88, + 0x7c, 0x08, 0x03, 0xa6, 0x82, 0xa1, 0x02, 0x8c, 0x82, 0xc1, 0x02, 0x90, + 0x82, 0xe1, 0x02, 0x94, 0x83, 0x01, 0x02, 0x98, 0x83, 0x21, 0x02, 0x9c, + 0x83, 0x41, 0x02, 0xa0, 0x83, 0x61, 0x02, 0xa4, 0x83, 0x81, 0x02, 0xa8, + 0x83, 0xa1, 0x02, 0xac, 0x83, 0xc1, 0x02, 0xb0, 0x83, 0xe1, 0x02, 0xb4, + 0x38, 0x21, 0x02, 0xb8, 0x4e, 0x80, 0x00, 0x20, 0x39, 0x20, 0x00, 0x64, + 0x9b, 0x41, 0x02, 0x6f, 0x99, 0x21, 0x02, 0x6c, 0x39, 0x20, 0x00, 0x69, + 0x99, 0x21, 0x02, 0x6d, 0x39, 0x20, 0x00, 0x72, 0x99, 0x21, 0x02, 0x6e, + 0x38, 0x81, 0x02, 0x6c, 0x38, 0xa0, 0x00, 0xfb, 0x81, 0x3c, 0x00, 0x00, + 0x7c, 0x69, 0xe8, 0x2e, 0x4b, 0xff, 0xf8, 0x0d, 0x81, 0x01, 0x02, 0x78, + 0x7f, 0xc3, 0xf3, 0x78, 0x7f, 0x04, 0xc3, 0x78, 0x7f, 0xe5, 0xfb, 0x78, + 0x38, 0xc1, 0x01, 0x6c, 0x7f, 0x67, 0xdb, 0x78, 0x4b, 0xff, 0xfc, 0xad, + 0x4b, 0xff, 0xfd, 0x48, 0x39, 0x80, 0x00, 0x01, 0x39, 0x20, 0x00, 0x00, + 0x4b, 0xff, 0xfd, 0x88, 0x38, 0x60, 0xff, 0xff, 0x4b, 0xff, 0xff, 0x60, + 0x94, 0x21, 0xfe, 0xa8, 0x7c, 0x08, 0x02, 0xa6, 0x39, 0x20, 0x00, 0x30, + 0x38, 0xe0, 0x00, 0x2f, 0x93, 0xe1, 0x01, 0x54, 0x3b, 0xe0, 0x00, 0x6c, + 0x90, 0x01, 0x01, 0x5c, 0x38, 0x00, 0x00, 0x76, 0x9b, 0xe1, 0x01, 0x0b, + 0x3b, 0xe0, 0x00, 0x73, 0x99, 0x21, 0x01, 0x15, 0x39, 0x00, 0x00, 0x6f, + 0x99, 0x21, 0x01, 0x16, 0x39, 0x40, 0x00, 0x00, 0x99, 0x21, 0x01, 0x17, + 0x39, 0x60, 0x00, 0x6d, 0x99, 0x21, 0x01, 0x18, 0x99, 0x21, 0x01, 0x19, + 0x99, 0x21, 0x01, 0x1a, 0x99, 0x21, 0x01, 0x1b, 0x39, 0x20, 0x00, 0x63, + 0x98, 0xe1, 0x01, 0x08, 0x98, 0x01, 0x01, 0x09, 0x98, 0xe1, 0x01, 0x0c, + 0x9b, 0xe1, 0x01, 0x0d, 0x3b, 0xe0, 0x00, 0x61, 0x98, 0x01, 0x01, 0x0f, + 0x38, 0x00, 0x00, 0x65, 0x98, 0xe1, 0x01, 0x11, 0x38, 0xe0, 0x00, 0x38, + 0x99, 0x21, 0x01, 0x20, 0x39, 0x20, 0x00, 0x6e, 0x93, 0x41, 0x01, 0x40, + 0x7c, 0x7a, 0x1b, 0x78, 0x93, 0x61, 0x01, 0x44, 0x7c, 0x9b, 0x23, 0x78, + 0x93, 0x81, 0x01, 0x48, 0x7c, 0xbc, 0x2b, 0x78, 0x93, 0xa1, 0x01, 0x4c, + 0x7c, 0xdd, 0x33, 0x78, 0x93, 0xc1, 0x01, 0x50, 0x3b, 0xc0, 0x00, 0x00, + 0x99, 0x01, 0x01, 0x0a, 0x99, 0x41, 0x01, 0x12, 0x98, 0xe1, 0x01, 0x14, + 0x38, 0xe1, 0x01, 0x09, 0x99, 0x41, 0x01, 0x1c, 0x99, 0x01, 0x01, 0x21, + 0x93, 0x21, 0x01, 0x3c, 0x9b, 0xe1, 0x01, 0x0e, 0x98, 0x01, 0x01, 0x10, + 0x99, 0x61, 0x01, 0x22, 0x99, 0x61, 0x01, 0x23, 0x99, 0x01, 0x01, 0x24, + 0x39, 0x01, 0x00, 0x07, 0x99, 0x21, 0x01, 0x25, 0x39, 0x20, 0x00, 0x76, + 0x99, 0x41, 0x01, 0x26, 0x39, 0x40, 0x00, 0x2f, 0x48, 0x00, 0x00, 0x08, + 0x8d, 0x27, 0x00, 0x01, 0x2f, 0x89, 0x00, 0x00, 0x9d, 0x48, 0x00, 0x01, + 0x3b, 0xde, 0x00, 0x01, 0x7d, 0x2a, 0x4b, 0x78, 0x40, 0x9e, 0xff, 0xec, + 0x39, 0x21, 0x00, 0x08, 0x3b, 0x3e, 0xff, 0xff, 0x7f, 0x29, 0xca, 0x14, + 0x38, 0xc1, 0x01, 0x15, 0x7f, 0x27, 0xcb, 0x78, 0x7f, 0xca, 0xf3, 0x78, + 0x39, 0x20, 0x00, 0x30, 0x39, 0x00, 0x00, 0x38, 0x48, 0x00, 0x00, 0x08, + 0x8d, 0x26, 0x00, 0x01, 0x2f, 0x89, 0x00, 0x00, 0x9d, 0x07, 0x00, 0x01, + 0x39, 0x4a, 0x00, 0x01, 0x7d, 0x28, 0x4b, 0x78, 0x40, 0x9e, 0xff, 0xec, + 0x7d, 0x41, 0x52, 0x14, 0x3b, 0xe0, 0x00, 0x01, 0x99, 0x2a, 0x00, 0x08, + 0x48, 0x00, 0x00, 0x28, 0x2f, 0x89, 0x00, 0x09, 0x39, 0x49, 0x00, 0x57, + 0x99, 0x01, 0x00, 0x18, 0x39, 0x29, 0x00, 0x30, 0x41, 0x9d, 0x00, 0x5c, + 0x2f, 0x9f, 0x01, 0x00, 0x99, 0x21, 0x00, 0x19, 0x3b, 0xff, 0x00, 0x01, + 0x41, 0x9e, 0x00, 0x5c, 0x39, 0x00, 0x00, 0x32, 0x7f, 0x43, 0xd3, 0x78, + 0x7f, 0xa4, 0xeb, 0x78, 0x7f, 0x65, 0xdb, 0x78, 0x38, 0xc1, 0x00, 0x08, + 0x7f, 0x87, 0xe3, 0x78, 0x4b, 0xff, 0xfb, 0x01, 0x7f, 0xe9, 0x26, 0x70, + 0x2f, 0x89, 0x00, 0x09, 0x39, 0x49, 0x00, 0x57, 0x39, 0x09, 0x00, 0x30, + 0x57, 0xe9, 0x07, 0x3e, 0x40, 0xbd, 0xff, 0xac, 0x2f, 0x89, 0x00, 0x09, + 0x99, 0x41, 0x00, 0x18, 0x39, 0x49, 0x00, 0x57, 0x39, 0x29, 0x00, 0x30, + 0x40, 0xbd, 0xff, 0xac, 0x2f, 0x9f, 0x01, 0x00, 0x99, 0x41, 0x00, 0x19, + 0x3b, 0xff, 0x00, 0x01, 0x40, 0x9e, 0xff, 0xac, 0x39, 0x01, 0x01, 0x21, + 0x7f, 0x2a, 0xcb, 0x78, 0x3b, 0xe0, 0x00, 0x6f, 0x39, 0x20, 0x00, 0x63, + 0x48, 0x00, 0x00, 0x08, 0x8f, 0xe8, 0x00, 0x01, 0x2f, 0x9f, 0x00, 0x00, + 0x9d, 0x2a, 0x00, 0x01, 0x3b, 0xde, 0x00, 0x01, 0x7f, 0xe9, 0xfb, 0x78, + 0x40, 0x9e, 0xff, 0xec, 0x7f, 0xc1, 0xf2, 0x14, 0x3f, 0x20, 0x10, 0x00, + 0x9b, 0xfe, 0x00, 0x08, 0x63, 0x39, 0x00, 0xe4, 0x57, 0xbe, 0x10, 0x3a, + 0x38, 0x81, 0x00, 0x08, 0x81, 0x39, 0x00, 0x00, 0x38, 0xa0, 0x00, 0xfb, + 0x7c, 0x69, 0xf0, 0x2e, 0x4b, 0xff, 0xf5, 0xb5, 0x7f, 0xa4, 0xeb, 0x78, + 0x7f, 0x65, 0xdb, 0x78, 0x38, 0xc1, 0x00, 0x08, 0x7f, 0x87, 0xe3, 0x78, + 0x7f, 0x43, 0xd3, 0x78, 0x39, 0x00, 0x00, 0x3c, 0x4b, 0xff, 0xfa, 0x55, + 0x39, 0x20, 0x00, 0x64, 0x99, 0x21, 0x01, 0x28, 0x39, 0x20, 0x00, 0x6f, + 0x99, 0x21, 0x01, 0x29, 0x39, 0x20, 0x00, 0x6e, 0x99, 0x21, 0x01, 0x2a, + 0x39, 0x20, 0x00, 0x65, 0x99, 0x21, 0x01, 0x2b, 0x39, 0x20, 0x00, 0x21, + 0x9b, 0xe1, 0x01, 0x2d, 0x38, 0x81, 0x01, 0x28, 0x99, 0x21, 0x01, 0x2c, + 0x38, 0xa0, 0x00, 0xfb, 0x81, 0x39, 0x00, 0x00, 0x7c, 0x69, 0xf0, 0x2e, + 0x4b, 0xff, 0xf5, 0x59, 0x80, 0x01, 0x01, 0x5c, 0x83, 0x21, 0x01, 0x3c, + 0x7c, 0x08, 0x03, 0xa6, 0x83, 0x41, 0x01, 0x40, 0x83, 0x61, 0x01, 0x44, + 0x83, 0x81, 0x01, 0x48, 0x83, 0xa1, 0x01, 0x4c, 0x83, 0xc1, 0x01, 0x50, + 0x83, 0xe1, 0x01, 0x54, 0x38, 0x21, 0x01, 0x58, 0x4e, 0x80, 0x00, 0x20, + 0x7c, 0x08, 0x02, 0xa6, 0x94, 0x21, 0xff, 0xe0, 0x3d, 0x20, 0x10, 0x00, + 0x61, 0x29, 0x00, 0xe4, 0x93, 0x81, 0x00, 0x10, 0x90, 0x01, 0x00, 0x24, + 0x7c, 0xbc, 0x2b, 0x78, 0x93, 0xa1, 0x00, 0x14, 0x7c, 0xdd, 0x33, 0x78, + 0x81, 0x69, 0x00, 0x00, 0x3d, 0x20, 0x0a, 0x00, 0x93, 0xc1, 0x00, 0x18, + 0x7c, 0x9e, 0x23, 0x78, 0x7f, 0x8b, 0x48, 0x00, 0x93, 0xe1, 0x00, 0x1c, + 0x7c, 0x7f, 0x1b, 0x78, 0x41, 0x9e, 0x00, 0x38, 0x39, 0x40, 0x00, 0x20, + 0x39, 0x2b, 0x00, 0x7c, 0x38, 0xc0, 0x00, 0x00, 0x7d, 0x49, 0x03, 0xa6, + 0x48, 0x00, 0x00, 0x0c, 0x38, 0xc6, 0x00, 0x01, 0x42, 0x40, 0x00, 0x1c, + 0x85, 0x09, 0x00, 0x04, 0x7f, 0x9f, 0x40, 0x00, 0x40, 0x9e, 0xff, 0xf0, + 0x81, 0x2b, 0x02, 0x80, 0x2f, 0x89, 0x00, 0x00, 0x41, 0x9e, 0x00, 0x40, + 0x3d, 0x20, 0x01, 0x1e, 0x80, 0x01, 0x00, 0x24, 0x81, 0x29, 0xf2, 0xb0, + 0x7f, 0xe3, 0xfb, 0x78, 0x7f, 0xc4, 0xf3, 0x78, 0x83, 0xe1, 0x00, 0x1c, + 0x83, 0xc1, 0x00, 0x18, 0x7f, 0x85, 0xe3, 0x78, 0x7f, 0xa6, 0xeb, 0x78, + 0x83, 0x81, 0x00, 0x10, 0x83, 0xa1, 0x00, 0x14, 0x7d, 0x29, 0x03, 0xa6, + 0x38, 0x21, 0x00, 0x20, 0x7c, 0x08, 0x03, 0xa6, 0x4e, 0x80, 0x04, 0x20, + 0x7f, 0xe3, 0xfb, 0x78, 0x7f, 0xc4, 0xf3, 0x78, 0x7c, 0xe5, 0x3b, 0x78, + 0x90, 0xe1, 0x00, 0x08, 0x4b, 0xff, 0xfc, 0x91, 0x3d, 0x20, 0x10, 0x00, + 0x61, 0x29, 0x00, 0xe4, 0x39, 0x40, 0x00, 0x02, 0x81, 0x29, 0x00, 0x00, + 0x7f, 0xe3, 0xfb, 0x78, 0x80, 0x01, 0x00, 0x24, 0x7f, 0xc4, 0xf3, 0x78, + 0x91, 0x49, 0x02, 0x80, 0x3d, 0x20, 0x01, 0x1e, 0x81, 0x29, 0xf2, 0xb0, + 0x7f, 0x85, 0xe3, 0x78, 0x80, 0xe1, 0x00, 0x08, 0x7f, 0xa6, 0xeb, 0x78, + 0x83, 0x81, 0x00, 0x10, 0x7d, 0x29, 0x03, 0xa6, 0x83, 0xa1, 0x00, 0x14, + 0x83, 0xc1, 0x00, 0x18, 0x7c, 0x08, 0x03, 0xa6, 0x83, 0xe1, 0x00, 0x1c, + 0x38, 0x21, 0x00, 0x20, 0x4e, 0x80, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const unsigned int cafiine_text_bin_len = 4788; diff --git a/server/saviine_server.exe b/server/saviine_server.exe new file mode 100644 index 0000000000000000000000000000000000000000..dab81314f89b8c3faf02f496b56e6d2101c32cbb GIT binary patch literal 26624 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 literal 0 HcmV?d00001 diff --git a/server/src/Program.cs b/server/src/Program.cs new file mode 100644 index 0000000..ae0477b --- /dev/null +++ b/server/src/Program.cs @@ -0,0 +1,335 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Collections.Generic; + +namespace saviine_server +{ + class Program + { + public const byte BYTE_NORMAL = 0xff; + public const byte BYTE_SPECIAL = 0xfe; + //public const byte BYTE_OPEN = 0x00; + // public const byte BYTE_READ = 0x01; + public const byte BYTE_CLOSE = 0x02; + //public const byte BYTE_OK = 0x03; + //public const byte BYTE_SETPOS = 0x04; + //public const byte BYTE_STATFILE = 0x05; + //public const byte BYTE_EOF = 0x06; + //public const byte BYTE_GETPOS = 0x07; + //public const byte BYTE_REQUEST = 0x08; + //public const byte BYTE_REQUEST_SLOW = 0x09; + public const byte BYTE_HANDLE = 0x0A; + public const byte BYTE_DUMP = 0x0B; + public const byte BYTE_PING = 0x0C; + + public const byte BYTE_LOG_STR = 0xFB; + + [Flags] + public enum FSStatFlag : uint + { + None = 0, + unk_14_present = 0x01000000, + mtime_present = 0x04000000, + ctime_present = 0x08000000, + entid_present = 0x10000000, + directory = 0x80000000, + } + + [StructLayout(LayoutKind.Sequential)] + public struct FSStat + { + public FSStatFlag flags; + public uint permission; + public uint owner; + public uint group; + public uint file_size; + public uint unk_14_nonzero; + public uint unk_18_zero; + public uint unk_1c_zero; + public uint ent_id; + public uint ctime_u; + public uint ctime_l; + public uint mtime_u; + public uint mtime_l; + public uint unk_34_zero; + public uint unk_38_zero; + public uint unk_3c_zero; + public uint unk_40_zero; + public uint unk_44_zero; + public uint unk_48_zero; + public uint unk_4c_zero; + public uint unk_50_zero; + public uint unk_54_zero; + public uint unk_58_zero; + public uint unk_5c_zero; + public uint unk_60_zero; + } + + public static string root = "saviine_root"; + public static string logs_root = "logs"; + + const uint BUFFER_SIZE = 64 * 1024; + + static void Main(string[] args) + { + if (args.Length > 1) + { + Console.Error.WriteLine("Usage: saviine_server [rootdir]"); + return; + } + if (args.Length == 1) + { + root = args[0]; + } + // Check for cafiine_root and logs folder + if (!Directory.Exists(root)) + { + Console.Error.WriteLine("Root directory `{0}' does not exist!", root); + Directory.CreateDirectory(root); + Console.WriteLine("Root directory `{0}' created!", root); + } + if (!Directory.Exists(logs_root)) + { + Console.Error.WriteLine("Logs directory `{0}' does not exist!", logs_root); + Directory.CreateDirectory(logs_root); + Console.WriteLine("Logs directory `{0}' created!", logs_root); + } + // Delete logs + System.IO.DirectoryInfo downloadedMessageInfo = new DirectoryInfo(logs_root); + foreach (FileInfo file in downloadedMessageInfo.GetFiles()) + { + file.Delete(); + } + + // Start server + string name = "[listener]"; + try + { + TcpListener listener = new TcpListener(IPAddress.Any, 7332); + listener.Start(); + Console.WriteLine(name + " Listening on 7332"); + + int index = 0; + while (true) + { + TcpClient client = listener.AcceptTcpClient(); + Console.WriteLine("connected"); + Thread thread = new Thread(Handle); + thread.Name = "[" + index.ToString() + "]"; + thread.Start(client); + index++; + } + } + catch (Exception e) + { + Console.WriteLine(name + " " + e.Message); + } + Console.WriteLine(name + " Exit"); + } + + static void Log(StreamWriter log, String str) + { + log.WriteLine(str); + log.Flush(); + Console.WriteLine(str); + } + + static void Handle(object client_obj) + { + string name = Thread.CurrentThread.Name; + FileStream[] files = new FileStream[256]; + Dictionary files_request = new Dictionary(); + StreamWriter log = null; + + try + { + TcpClient client = (TcpClient)client_obj; + using (NetworkStream stream = client.GetStream()) + { + EndianBinaryReader reader = new EndianBinaryReader(stream); + EndianBinaryWriter writer = new EndianBinaryWriter(stream); + + uint[] ids = reader.ReadUInt32s(4); + + // Log connection + Console.WriteLine(name + " Accepted connection from client " + client.Client.RemoteEndPoint.ToString()); + Console.WriteLine(name + " TitleID: " + ids[0].ToString("X8") + "-" + ids[1].ToString("X8")); + + + string LocalRoot = root + "\\" + ids[0].ToString("X8") + "-" + ids[1].ToString("X8") + "\\"; + if (!ids[0].ToString("X8").Equals("00050000")) + { + writer.Write(BYTE_NORMAL); + throw new Exception("Not interested."); + } + else + { + if (!Directory.Exists(LocalRoot)) + { + Directory.CreateDirectory(LocalRoot); + } + } + + // Create log file for current thread + log = new StreamWriter(logs_root + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + "-" + name + "-" + ids[0].ToString("X8") + "-" + ids[1].ToString("X8") + ".txt", true, Encoding.ASCII, 1024*64); + log.WriteLine(name + " Accepted connection from client " + client.Client.RemoteEndPoint.ToString()); + log.WriteLine(name + " TitleID: " + ids[0].ToString("X8") + "-" + ids[1].ToString("X8")); + + writer.Write(BYTE_SPECIAL); + + while (true) + { + byte cmd_byte = reader.ReadByte(); + switch (cmd_byte) + { + case BYTE_HANDLE: + { + // Read buffer params : fd, path length, path string + int fd = reader.ReadInt32(); + int len_path = reader.ReadInt32(); + string path = reader.ReadString(Encoding.ASCII, len_path - 1); + if (reader.ReadByte() != 0) throw new InvalidDataException(); + if (!Directory.Exists(LocalRoot + path)) + { + Directory.CreateDirectory(Path.GetDirectoryName(LocalRoot + path)); + } + + // Add new file for incoming data + files_request.Add(fd, new FileStream(LocalRoot + path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write)); + + // Send response + writer.Write(BYTE_SPECIAL); + break; + } + case BYTE_DUMP: + { + // Read buffer params : fd, size, file data + int fd = reader.ReadInt32(); + int sz = reader.ReadInt32(); + byte[] buffer = new byte[sz]; + buffer = reader.ReadBytes(sz); + + // Look for file descriptor + foreach (var item in files_request) + { + if (item.Key == fd) + { + FileStream dump_file = item.Value; + if (dump_file == null) + break; + + Log(log, name + " -> dump(\"" + Path.GetFileName(dump_file.Name) + "\") " + (sz / 1024).ToString() + "kB"); + + // Write to file + dump_file.Write(buffer, 0, sz); + + break; + } + } + + // Send response + writer.Write(BYTE_SPECIAL); + break; + } + case BYTE_CLOSE: + { + int fd = reader.ReadInt32(); + if ((fd & 0x0fff00ff) == 0x0fff00ff) + { + int handle = (fd >> 8) & 0xff; + if (files[handle] == null) + { + writer.Write(BYTE_SPECIAL); + writer.Write(-38); + break; + } + Log(log, name + " close(" + handle.ToString() + ")"); + FileStream f = files[handle]; + + writer.Write(BYTE_SPECIAL); + writer.Write(0); + f.Close(); + files[handle] = null; + } + else + { + // Check if it is a file to dump + foreach (var item in files_request) + { + if (item.Key == fd) + { + FileStream dump_file = item.Value; + if (dump_file == null) + break; + + Log(log, name + " -> dump complete(\"" + Path.GetFileName(dump_file.Name) + "\")"); + + // Close file and remove from request list + dump_file.Close(); + files_request.Remove(fd); + + break; + } + } + + // Send response + writer.Write(BYTE_NORMAL); + } + break; + } + case BYTE_PING: + { + int val1 = reader.ReadInt32(); + int val2 = reader.ReadInt32(); + + Log(log, name + " PING RECEIVED with values : " + val1.ToString() + " - " + val2.ToString()); + break; + } + + case BYTE_LOG_STR: + { + int len_str = reader.ReadInt32(); + string str = reader.ReadString(Encoding.ASCII, len_str - 1); + if (reader.ReadByte() != 0) throw new InvalidDataException(); + + Log(log, name + " LogString =>(\"" + str + "\")"); + break; + } + default: + throw new InvalidDataException(); + } + } + } + } + catch (Exception e) + { + if (log != null) + Log(log, name + " " + e.Message); + else + Console.WriteLine(name + " " + e.Message); + } + finally + { + foreach (var item in files) + { + if (item != null) + item.Close(); + } + foreach (var item in files_request) + { + if (item.Value != null) + item.Value.Close(); + } + + if (log != null) + log.Close(); + } + Console.WriteLine(name + " Exit"); + } + } +} diff --git a/server/src/Properties/AssemblyInfo.cs b/server/src/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..cec5cea --- /dev/null +++ b/server/src/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("cafiine_server")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("ChadSoft")] +[assembly: AssemblyProduct("cafiine_server")] +[assembly: AssemblyCopyright("Copyright © ChadSoft 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("fc022709-becf-498f-9a2a-dc3543457b0d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/server/src/System/IO/EndianBinaryReader.cs b/server/src/System/IO/EndianBinaryReader.cs new file mode 100644 index 0000000..74b9aad --- /dev/null +++ b/server/src/System/IO/EndianBinaryReader.cs @@ -0,0 +1,644 @@ +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace System.IO +{ + sealed class EndianBinaryReader : IDisposable + { + private bool disposed; + private byte[] buffer; + + private delegate void ArrayReverse(byte[] array, int count); + private static readonly ArrayReverse[] fastReverse = new ArrayReverse[] { null, null, ArrayReverse2, null, ArrayReverse4, null, null, null, ArrayReverse8 }; + + private static Dictionary>> parserCache = new Dictionary>>(); + + public Stream BaseStream { get; private set; } + public Endianness Endianness { get; set; } + public static Endianness SystemEndianness { get { return BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian; } } + + private bool Reverse { get { return SystemEndianness != Endianness; } } + + public EndianBinaryReader(Stream baseStream) + : this(baseStream, Endianness.BigEndian) + { } + + public EndianBinaryReader(Stream baseStream, Endianness endianness) + { + if (baseStream == null) throw new ArgumentNullException("baseStream"); + if (!baseStream.CanRead) throw new ArgumentException("base stream is not readable.", "baseStream"); + + BaseStream = baseStream; + Endianness = endianness; + } + + ~EndianBinaryReader() + { + Dispose(false); + } + + /// + /// Fills the buffer with bytes bytes, possibly reversing every stride. Bytes must be a multiple of stide. Stide must be 1 or 2 or 4 or 8. + /// + /// + /// + private void FillBuffer(int bytes, int stride) + { + if (buffer == null || buffer.Length < bytes) + buffer = new byte[bytes]; + + for (int i = 0, read = 0; i < bytes; i += read) + { + read = BaseStream.Read(buffer, i, bytes - i); + if (read <= 0) throw new EndOfStreamException(); + } + + if (Reverse) + { + if (fastReverse[stride] != null) + fastReverse[stride](buffer, bytes); + else + for (int i = 0; i < bytes; i += stride) + { + Array.Reverse(buffer, i, stride); + } + } + } + + private static void ArrayReverse2(byte[] array, int arrayLength) + { + byte temp; + + while (arrayLength > 0) + { + temp = array[arrayLength - 2]; + array[arrayLength - 2] = array[arrayLength - 1]; + array[arrayLength - 1] = temp; + arrayLength -= 2; + } + } + + private static void ArrayReverse4(byte[] array, int arrayLength) + { + byte temp; + + while (arrayLength > 0) + { + temp = array[arrayLength - 3]; + array[arrayLength - 3] = array[arrayLength - 2]; + array[arrayLength - 2] = temp; + temp = array[arrayLength - 4]; + array[arrayLength - 4] = array[arrayLength - 1]; + array[arrayLength - 1] = temp; + arrayLength -= 4; + } + } + + private static void ArrayReverse8(byte[] array, int arrayLength) + { + byte temp; + + while (arrayLength > 0) + { + temp = array[arrayLength - 5]; + array[arrayLength - 5] = array[arrayLength - 4]; + array[arrayLength - 4] = temp; + temp = array[arrayLength - 6]; + array[arrayLength - 6] = array[arrayLength - 3]; + array[arrayLength - 3] = temp; + temp = array[arrayLength - 7]; + array[arrayLength - 7] = array[arrayLength - 2]; + array[arrayLength - 2] = temp; + temp = array[arrayLength - 8]; + array[arrayLength - 8] = array[arrayLength - 1]; + array[arrayLength - 1] = temp; + arrayLength -= 8; + } + } + + public byte ReadByte() + { + FillBuffer(1, 1); + + return buffer[0]; + } + + public byte[] ReadBytes(int count) + { + byte[] temp; + + FillBuffer(count, 1); + temp = new byte[count]; + Array.Copy(buffer, 0, temp, 0, count); + return temp; + } + + public sbyte ReadSByte() + { + FillBuffer(1, 1); + + unchecked + { + return (sbyte)buffer[0]; + } + } + + public sbyte[] ReadSBytes(int count) + { + sbyte[] temp; + + temp = new sbyte[count]; + FillBuffer(count, 1); + + unchecked + { + for (int i = 0; i < count; i++) + { + temp[i] = (sbyte)buffer[i]; + } + } + + return temp; + } + + public char ReadChar(Encoding encoding) + { + int size; + + if (encoding == null) throw new ArgumentNullException("encoding"); + + size = GetEncodingSize(encoding); + FillBuffer(size, size); + return encoding.GetChars(buffer, 0, size)[0]; + } + + public char[] ReadChars(Encoding encoding, int count) + { + int size; + + if (encoding == null) throw new ArgumentNullException("encoding"); + + size = GetEncodingSize(encoding); + FillBuffer(size * count, size); + return encoding.GetChars(buffer, 0, size * count); + } + + private static int GetEncodingSize(Encoding encoding) + { + if (encoding == Encoding.UTF8 || encoding == Encoding.ASCII) + return 1; + else if (encoding == Encoding.Unicode || encoding == Encoding.BigEndianUnicode) + return 2; + + return 1; + } + + public string ReadStringNT(Encoding encoding) + { + string text; + + text = ""; + + do + { + text += ReadChar(encoding); + } while (!text.EndsWith("\0", StringComparison.Ordinal)); + + return text.Remove(text.Length - 1); + } + + public string ReadString(Encoding encoding, int count) + { + return new string(ReadChars(encoding, count)); + } + + public double ReadDouble() + { + const int size = sizeof(double); + FillBuffer(size, size); + return BitConverter.ToDouble(buffer, 0); + } + + public double[] ReadDoubles(int count) + { + const int size = sizeof(double); + double[] temp; + + temp = new double[count]; + FillBuffer(size * count, size); + + for (int i = 0; i < count; i++) + { + temp[i] = BitConverter.ToDouble(buffer, size * i); + } + return temp; + } + + public Single ReadSingle() + { + const int size = sizeof(Single); + FillBuffer(size, size); + return BitConverter.ToSingle(buffer, 0); + } + + public Single[] ReadSingles(int count) + { + const int size = sizeof(Single); + Single[] temp; + + temp = new Single[count]; + FillBuffer(size * count, size); + + for (int i = 0; i < count; i++) + { + temp[i] = BitConverter.ToSingle(buffer, size * i); + } + return temp; + } + + public Int32 ReadInt32() + { + const int size = sizeof(Int32); + FillBuffer(size, size); + return BitConverter.ToInt32(buffer, 0); + } + + public Int32[] ReadInt32s(int count) + { + const int size = sizeof(Int32); + Int32[] temp; + + temp = new Int32[count]; + FillBuffer(size * count, size); + + for (int i = 0; i < count; i++) + { + temp[i] = BitConverter.ToInt32(buffer, size * i); + } + return temp; + } + + public Int64 ReadInt64() + { + const int size = sizeof(Int64); + FillBuffer(size, size); + return BitConverter.ToInt64(buffer, 0); + } + + public Int64[] ReadInt64s(int count) + { + const int size = sizeof(Int64); + Int64[] temp; + + temp = new Int64[count]; + FillBuffer(size * count, size); + + for (int i = 0; i < count; i++) + { + temp[i] = BitConverter.ToInt64(buffer, size * i); + } + return temp; + } + + public Int16 ReadInt16() + { + const int size = sizeof(Int16); + FillBuffer(size, size); + return BitConverter.ToInt16(buffer, 0); + } + + public Int16[] ReadInt16s(int count) + { + const int size = sizeof(Int16); + Int16[] temp; + + temp = new Int16[count]; + FillBuffer(size * count, size); + + for (int i = 0; i < count; i++) + { + temp[i] = BitConverter.ToInt16(buffer, size * i); + } + return temp; + } + + public UInt16 ReadUInt16() + { + const int size = sizeof(UInt16); + FillBuffer(size, size); + return BitConverter.ToUInt16(buffer, 0); + } + + public UInt16[] ReadUInt16s(int count) + { + const int size = sizeof(UInt16); + UInt16[] temp; + + temp = new UInt16[count]; + FillBuffer(size * count, size); + + for (int i = 0; i < count; i++) + { + temp[i] = BitConverter.ToUInt16(buffer, size * i); + } + return temp; + } + + public UInt32 ReadUInt32() + { + const int size = sizeof(UInt32); + FillBuffer(size, size); + return BitConverter.ToUInt32(buffer, 0); + } + + public UInt32[] ReadUInt32s(int count) + { + const int size = sizeof(UInt32); + UInt32[] temp; + + temp = new UInt32[count]; + FillBuffer(size * count, size); + + for (int i = 0; i < count; i++) + { + temp[i] = BitConverter.ToUInt32(buffer, size * i); + } + return temp; + } + + public UInt64 ReadUInt64() + { + const int size = sizeof(UInt64); + FillBuffer(size, size); + return BitConverter.ToUInt64(buffer, 0); + } + + public UInt64[] ReadUInt64s(int count) + { + const int size = sizeof(UInt64); + UInt64[] temp; + + temp = new UInt64[count]; + FillBuffer(size * count, size); + + for (int i = 0; i < count; i++) + { + temp[i] = BitConverter.ToUInt64(buffer, size * i); + } + return temp; + } + + private List> GetParser(Type type) + { + List> parser; + + /* A parser describes how to read in a type in an Endian + * appropriate manner. Basically it describes as series of calls to + * the Read* methods above to parse the structure. + * The parser runs through each element in the list in order. If + * the TypeCode is not Empty then it reads an array of values + * according to the integer. Otherwise it skips a number of bytes. */ + + try + { + parser = parserCache[type]; + } + catch (KeyNotFoundException) + { + parser = new List>(); + + if (Endianness != SystemEndianness) + { + int pos, sz; + + pos = 0; + foreach (var item in type.GetFields()) + { + int off = Marshal.OffsetOf(type, item.Name).ToInt32(); + if (off != pos) + { + parser.Add(new Tuple(off - pos, TypeCode.Empty)); + pos = off; + } + switch (Type.GetTypeCode(item.FieldType)) + { + case TypeCode.Byte: + case TypeCode.SByte: + pos += 1; + parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); + break; + case TypeCode.Int16: + case TypeCode.UInt16: + pos += 2; + parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); + break; + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Single: + pos += 4; + parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Double: + pos += 8; + parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); + break; + case TypeCode.Object: + if (item.FieldType.IsArray) + { + /* array */ + Type elementType; + MarshalAsAttribute[] attrs; + MarshalAsAttribute attr; + + attrs = (MarshalAsAttribute[])item.GetCustomAttributes(typeof(MarshalAsAttribute), false); + if (attrs.Length != 1) + throw new ArgumentException(String.Format("Field `{0}' is an array without a MarshalAs attribute.", item.Name), "type"); + + attr = attrs[0]; + if (attr.Value != UnmanagedType.ByValArray) + throw new ArgumentException(String.Format("Field `{0}' is not a ByValArray.", item.Name), "type"); + + elementType = item.FieldType.GetElementType(); + switch (Type.GetTypeCode(elementType)) + { + case TypeCode.Byte: + case TypeCode.SByte: + pos += 1 * attr.SizeConst; + parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); + break; + case TypeCode.Int16: + case TypeCode.UInt16: + pos += 2 * attr.SizeConst; + parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); + break; + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Single: + pos += 4 * attr.SizeConst; + parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Double: + pos += 8 * attr.SizeConst; + parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); + break; + case TypeCode.Object: + /* nested structure */ + for (int i = 0; i < attr.SizeConst; i++) + { + pos += Marshal.SizeOf(elementType); + parser.AddRange(GetParser(elementType)); + } + break; + default: + break; + } + } + else + { + /* nested structure */ + pos += Marshal.SizeOf(item.FieldType); + parser.AddRange(GetParser(item.FieldType)); + } + break; + default: + throw new NotImplementedException(); + } + } + + sz = Marshal.SizeOf(type); + if (sz != pos) + { + parser.Add(new Tuple(sz - pos, TypeCode.Empty)); + } + } + else + { + int sz; + + sz = Marshal.SizeOf(type); + parser.Add(new Tuple(sz, TypeCode.Byte)); + } + parserCache.Add(type, parser); + } + + return parser; + } + + private void RunParser(List> parser, BinaryWriter wr) + { + foreach (var item in parser) + { + /* Assumption: Types of the same size can be interchanged. */ + switch (item.Item2) + { + case TypeCode.Byte: + case TypeCode.SByte: + wr.Write(ReadBytes(item.Item1), 0, item.Item1); + break; + case TypeCode.Int16: + case TypeCode.UInt16: + foreach (var val in ReadInt16s(item.Item1)) + wr.Write(val); + break; + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Single: + foreach (var val in ReadInt32s(item.Item1)) + wr.Write(val); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Double: + foreach (var val in ReadInt64s(item.Item1)) + wr.Write(val); + break; + case TypeCode.Empty: + BaseStream.Seek(item.Item1, SeekOrigin.Current); + wr.BaseStream.Seek(item.Item1, SeekOrigin.Current); + break; + default: + throw new NotImplementedException(); + } + } + } + + public object ReadStructure(Type type) + { + List> parser; + object result; + + parser = GetParser(type); + + using (var ms = new MemoryStream()) + { + using (var wr = new BinaryWriter(ms)) + { + RunParser(parser, wr); + } + result = Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(ms.ToArray(), 0), type); + } + return result; + } + + public Array ReadStructures(Type type, int count) + { + List> parser; + Array result; + + parser = GetParser(type); + + result = Array.CreateInstance(type, count); + + using (var ms = new MemoryStream()) + { + using (var wr = new BinaryWriter(ms)) + { + for (int i = 0; i < count; i++) + { + ms.Seek(0, SeekOrigin.Begin); + RunParser(parser, wr); + result.SetValue(Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(ms.ToArray(), 0), type), i); + } + } + } + return result; + } + + public void Close() + { + BaseStream.Close(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + } + + BaseStream = null; + buffer = null; + + disposed = true; + } + } + } + + enum Endianness + { + BigEndian, + LittleEndian, + } +} diff --git a/server/src/System/IO/EndianBinaryWriter.cs b/server/src/System/IO/EndianBinaryWriter.cs new file mode 100644 index 0000000..fbb9180 --- /dev/null +++ b/server/src/System/IO/EndianBinaryWriter.cs @@ -0,0 +1,643 @@ +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace System.IO +{ + sealed class EndianBinaryWriter : IDisposable + { + private bool disposed; + private byte[] buffer; + + private delegate void ArrayReverse(byte[] array, int count); + private static readonly ArrayReverse[] fastReverse = new ArrayReverse[] { null, null, ArrayReverse2, null, ArrayReverse4, null, null, null, ArrayReverse8 }; + + private static Dictionary>> parserCache = new Dictionary>>(); + + public Stream BaseStream { get; private set; } + public Endianness Endianness { get; set; } + public static Endianness SystemEndianness { get { return BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian; } } + + private bool Reverse { get { return SystemEndianness != Endianness; } } + + public EndianBinaryWriter(Stream baseStream) + : this(baseStream, Endianness.BigEndian) + { } + + public EndianBinaryWriter(Stream baseStream, Endianness endianness) + { + if (baseStream == null) throw new ArgumentNullException("baseStream"); + if (!baseStream.CanWrite) throw new ArgumentException("base stream is not writeable", "baseStream"); + + BaseStream = baseStream; + Endianness = endianness; + } + + ~EndianBinaryWriter() + { + Dispose(false); + } + + private void WriteBuffer(int bytes, int stride) + { + if (Reverse) + { + if (fastReverse[stride] != null) + fastReverse[stride](buffer, bytes); + else + for (int i = 0; i < bytes; i += stride) + { + Array.Reverse(buffer, i, stride); + } + } + + BaseStream.Write(buffer, 0, bytes); + } + + private static void ArrayReverse2(byte[] array, int arrayLength) + { + byte temp; + + while (arrayLength > 0) + { + temp = array[arrayLength - 2]; + array[arrayLength - 2] = array[arrayLength - 1]; + array[arrayLength - 1] = temp; + arrayLength -= 2; + } + } + + private static void ArrayReverse4(byte[] array, int arrayLength) + { + byte temp; + + while (arrayLength > 0) + { + temp = array[arrayLength - 3]; + array[arrayLength - 3] = array[arrayLength - 2]; + array[arrayLength - 2] = temp; + temp = array[arrayLength - 4]; + array[arrayLength - 4] = array[arrayLength - 1]; + array[arrayLength - 1] = temp; + arrayLength -= 4; + } + } + + private static void ArrayReverse8(byte[] array, int arrayLength) + { + byte temp; + + while (arrayLength > 0) + { + temp = array[arrayLength - 5]; + array[arrayLength - 5] = array[arrayLength - 4]; + array[arrayLength - 4] = temp; + temp = array[arrayLength - 6]; + array[arrayLength - 6] = array[arrayLength - 3]; + array[arrayLength - 3] = temp; + temp = array[arrayLength - 7]; + array[arrayLength - 7] = array[arrayLength - 2]; + array[arrayLength - 2] = temp; + temp = array[arrayLength - 8]; + array[arrayLength - 8] = array[arrayLength - 1]; + array[arrayLength - 1] = temp; + arrayLength -= 8; + } + } + + private void CreateBuffer(int size) + { + if (buffer == null || buffer.Length < size) + buffer = new byte[size]; + } + + public void Write(byte value) + { + CreateBuffer(1); + buffer[0] = value; + WriteBuffer(1, 1); + } + + public void Write(byte[] value, int offset, int count) + { + CreateBuffer(count); + Array.Copy(value, offset, buffer, 0, count); + WriteBuffer(count, 1); + } + + public void Write(sbyte value) + { + CreateBuffer(1); + unchecked + { + buffer[0] = (byte)value; + } + WriteBuffer(1, 1); + } + + public void Write(sbyte[] value, int offset, int count) + { + CreateBuffer(count); + + unchecked + { + for (int i = 0; i < count; i++) + { + buffer[i] = (byte)value[i + offset]; + } + } + + WriteBuffer(count, 1); + } + + public void Write(char value, Encoding encoding) + { + int size; + + if (encoding == null) throw new ArgumentNullException("encoding"); + + size = GetEncodingSize(encoding); + CreateBuffer(size); + Array.Copy(encoding.GetBytes(new string(value, 1)), 0, buffer, 0, size); + WriteBuffer(size, size); + } + + public void Write(char[] value, int offset, int count, Encoding encoding) + { + int size; + + if (encoding == null) throw new ArgumentNullException("encoding"); + + size = GetEncodingSize(encoding); + CreateBuffer(size * count); + Array.Copy(encoding.GetBytes(value, offset, count), 0, buffer, 0, count * size); + WriteBuffer(size * count, size); + } + + private static int GetEncodingSize(Encoding encoding) + { + if (encoding == Encoding.UTF8 || encoding == Encoding.ASCII) + return 1; + else if (encoding == Encoding.Unicode || encoding == Encoding.BigEndianUnicode) + return 2; + + return 1; + } + + public void Write(string value,Encoding encoding, bool nullTerminated) + { + Write(value.ToCharArray(), 0, value.Length, encoding); + if (nullTerminated) + Write('\0', encoding); + } + + public void Write(double value) + { + const int size = sizeof(double); + + CreateBuffer(size); + Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); + WriteBuffer(size, size); + } + + public void Write(double[] value, int offset, int count) + { + const int size = sizeof(double); + + CreateBuffer(size * count); + for (int i = 0; i < count; i++) + { + Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); + } + + WriteBuffer(size * count, size); + } + + public void Write(Single value) + { + const int size = sizeof(Single); + + CreateBuffer(size); + Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); + WriteBuffer(size, size); + } + + public void Write(Single[] value, int offset, int count) + { + const int size = sizeof(Single); + + CreateBuffer(size * count); + for (int i = 0; i < count; i++) + { + Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); + } + + WriteBuffer(size * count, size); + } + + public void Write(Int32 value) + { + const int size = sizeof(Int32); + + CreateBuffer(size); + Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); + WriteBuffer(size, size); + } + + public void Write(Int32[] value, int offset, int count) + { + const int size = sizeof(Int32); + + CreateBuffer(size * count); + for (int i = 0; i < count; i++) + { + Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); + } + + WriteBuffer(size * count, size); + } + + public void Write(Int64 value) + { + const int size = sizeof(Int64); + + CreateBuffer(size); + Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); + WriteBuffer(size, size); + } + + public void Write(Int64[] value, int offset, int count) + { + const int size = sizeof(Int64); + + CreateBuffer(size * count); + for (int i = 0; i < count; i++) + { + Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); + } + + WriteBuffer(size * count, size); + } + + public void Write(Int16 value) + { + const int size = sizeof(Int16); + + CreateBuffer(size); + Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); + WriteBuffer(size, size); + } + + public void Write(Int16[] value, int offset, int count) + { + const int size = sizeof(Int16); + + CreateBuffer(size * count); + for (int i = 0; i < count; i++) + { + Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); + } + + WriteBuffer(size * count, size); + } + + public void Write(UInt16 value) + { + const int size = sizeof(UInt16); + + CreateBuffer(size); + Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); + WriteBuffer(size, size); + } + + public void Write(UInt16[] value, int offset, int count) + { + const int size = sizeof(UInt16); + + CreateBuffer(size * count); + for (int i = 0; i < count; i++) + { + Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); + } + + WriteBuffer(size * count, size); + } + + public void Write(UInt32 value) + { + const int size = sizeof(UInt32); + + CreateBuffer(size); + Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); + WriteBuffer(size, size); + } + + public void Write(UInt32[] value, int offset, int count) + { + const int size = sizeof(UInt32); + + CreateBuffer(size * count); + for (int i = 0; i < count; i++) + { + Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); + } + + WriteBuffer(size * count, size); + } + + public void Write(UInt64 value) + { + const int size = sizeof(UInt64); + + CreateBuffer(size); + Array.Copy(BitConverter.GetBytes(value), 0, buffer, 0, size); + WriteBuffer(size, size); + } + + public void Write(UInt64[] value, int offset, int count) + { + const int size = sizeof(UInt64); + + CreateBuffer(size * count); + for (int i = 0; i < count; i++) + { + Array.Copy(BitConverter.GetBytes(value[i + offset]), 0, buffer, i * size, size); + } + + WriteBuffer(size * count, size); + } + + public void WritePadding(int multiple, byte padding) + { + int length = (int)(BaseStream.Position % multiple); + + if (length != 0) + while (length != multiple) + { + BaseStream.WriteByte(padding); + length++; + } + } + + public void WritePadding(int multiple, byte padding, long from, int offset) + { + int length = (int)((BaseStream.Position - from) % multiple); + length = (length + offset) % multiple; + + if (length != 0) + while (length != multiple) + { + BaseStream.WriteByte(padding); + length++; + } + } + + private List> GetParser(Type type) + { + List> parser; + + /* A parser describes how to read in a type in an Endian + * appropriate manner. Basically it describes as series of calls to + * the Read* methods above to parse the structure. + * The parser runs through each element in the list in order. If + * the TypeCode is not Empty then it reads an array of values + * according to the integer. Otherwise it skips a number of bytes. */ + + try + { + parser = parserCache[type]; + } + catch (KeyNotFoundException) + { + parser = new List>(); + + if (Endianness != SystemEndianness) + { + int pos, sz; + + pos = 0; + foreach (var item in type.GetFields()) + { + int off = Marshal.OffsetOf(type, item.Name).ToInt32(); + if (off != pos) + { + parser.Add(new Tuple(off - pos, TypeCode.Empty)); + pos = off; + } + switch (Type.GetTypeCode(item.FieldType)) + { + case TypeCode.Byte: + case TypeCode.SByte: + pos += 1; + parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); + break; + case TypeCode.Int16: + case TypeCode.UInt16: + pos += 2; + parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); + break; + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Single: + pos += 4; + parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Double: + pos += 8; + parser.Add(new Tuple(1, Type.GetTypeCode(item.FieldType))); + break; + case TypeCode.Object: + if (item.FieldType.IsArray) + { + /* array */ + Type elementType; + MarshalAsAttribute[] attrs; + MarshalAsAttribute attr; + + attrs = (MarshalAsAttribute[])item.GetCustomAttributes(typeof(MarshalAsAttribute), false); + if (attrs.Length != 1) + throw new ArgumentException(String.Format("Field `{0}' is an array without a MarshalAs attribute.", item.Name), "type"); + + attr = attrs[0]; + if (attr.Value != UnmanagedType.ByValArray) + throw new ArgumentException(String.Format("Field `{0}' is not a ByValArray.", item.Name), "type"); + + elementType = item.FieldType.GetElementType(); + switch (Type.GetTypeCode(elementType)) + { + case TypeCode.Byte: + case TypeCode.SByte: + pos += 1 * attr.SizeConst; + parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); + break; + case TypeCode.Int16: + case TypeCode.UInt16: + pos += 2 * attr.SizeConst; + parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); + break; + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Single: + pos += 4 * attr.SizeConst; + parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Double: + pos += 8 * attr.SizeConst; + parser.Add(new Tuple(attr.SizeConst, Type.GetTypeCode(elementType))); + break; + case TypeCode.Object: + /* nested structure */ + for (int i = 0; i < attr.SizeConst; i++) + { + pos += Marshal.SizeOf(elementType); + parser.AddRange(GetParser(elementType)); + } + break; + default: + break; + } + } + else + { + /* nested structure */ + pos += Marshal.SizeOf(item.FieldType); + parser.AddRange(GetParser(item.FieldType)); + } + break; + default: + throw new NotImplementedException(); + } + } + + sz = Marshal.SizeOf(type); + if (sz != pos) + { + parser.Add(new Tuple(sz - pos, TypeCode.Empty)); + } + } + else + { + int sz; + + sz = Marshal.SizeOf(type); + parser.Add(new Tuple(sz, TypeCode.Byte)); + } + parserCache.Add(type, parser); + } + + return parser; + } + + private void RunParser(List> parser, BinaryReader rd) + { + foreach (var item in parser) + { + /* Assumption: Types of the same size can be interchanged. */ + switch (item.Item2) + { + case TypeCode.Byte: + case TypeCode.SByte: + Write(rd.ReadBytes(item.Item1), 0, item.Item1); + break; + case TypeCode.Int16: + case TypeCode.UInt16: + for (int i = 0; i < item.Item1; i++) + Write(rd.ReadInt16()); + break; + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Single: + for (int i = 0; i < item.Item1; i++) + Write(rd.ReadInt32()); + break; + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Double: + for (int i = 0; i < item.Item1; i++) + Write(rd.ReadInt64()); + break; + case TypeCode.Empty: + rd.BaseStream.Seek(item.Item1, SeekOrigin.Current); + BaseStream.Seek(item.Item1, SeekOrigin.Current); + break; + default: + throw new NotImplementedException(); + } + } + } + + public void Write(object structure) + { + List> parser; + Type type; + byte[] data; + + type = structure.GetType(); + parser = GetParser(type); + data = new byte[Marshal.SizeOf(type)]; + + using (var ms = new MemoryStream(data)) + { + using (var rd = new BinaryReader(ms)) + { + Marshal.StructureToPtr(structure, Marshal.UnsafeAddrOfPinnedArrayElement(data, 0), true); + RunParser(parser, rd); + } + } + } + + public void Write(Array structures) + { + List> parser; + Type type; + byte[] data; + + type = structures.GetType().GetElementType(); + parser = GetParser(type); + data = new byte[Marshal.SizeOf(type)]; + + using (var ms = new MemoryStream(data)) + { + using (var rd = new BinaryReader(ms)) + { + foreach (var structure in structures) + { + ms.Seek(0, SeekOrigin.Begin); + Marshal.StructureToPtr(structure, Marshal.UnsafeAddrOfPinnedArrayElement(data, 0), true); + RunParser(parser, rd); + } + } + } + } + + public void Close() + { + BaseStream.Close(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + } + + BaseStream = null; + buffer = null; + + disposed = true; + } + } + } +} diff --git a/server/src/app.config b/server/src/app.config new file mode 100644 index 0000000..49cc43e --- /dev/null +++ b/server/src/app.config @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/server/src/cafiine_server.v12.suo b/server/src/cafiine_server.v12.suo new file mode 100644 index 0000000000000000000000000000000000000000..e2e067ce8a9d8f01634567c4c1370b9846d696be GIT binary patch literal 28672 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 literal 0 HcmV?d00001 diff --git a/server/src/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/server/src/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 0000000000000000000000000000000000000000..814ec97890e5f54f6b21fd912264977470d31651 GIT binary patch literal 6190 zcmeHLZBH9V5VpZ=FoYJ`FY~sud(TcHnMfr5#X@f?H;0gTZ-lD?9aN0H zEp*8Jq6s|_*!iGV3*_e7de+(or9~iDb5HF#`em(r=$lZ%s)W!e?q{v7uH)xiE z1CXobx%tXsWvPUtAMaRBPu%bVAPuKT=60=p-+18uf7aZ3d*XdS2* zsJ$>~6NX%S3V4N38c~Dxlx|KfFKIT!h8asFhLH{>lSzgT(!WNEO>LltL5Hj68VV!( z6-ZFRwO^wP-sN_7t{8%GzRmp*wzyF5p2kCqMhRKXS~cwk$`|F+owM-K8)s>WFS?7* zax3!-Ft@z2o4rbpYpQBL{IJ_ww_bC0$>bnJ`w>O)Zt4f~YaUZQ-avx@?MDm^0<>2j z&Rqhx>0u;e=@eB*h#(e7q;DX>*C>%Rkuf6UNXFPrq%7|XkcLfQ{pE3?4T_!@5hFLa z0w>0?j{SDM6vM;pA%5nw9cgxIG5I`9u;pj&_nF-FW}yWHJ}^p|AI zB$6lR_3YsJNMBTn@ZW0N<_r#hK*O!rE^EPRbz1GQYzhaZXg2!_Ys07;yM>i8YO?3& z=5s6Qukn9|yiX6CCSo^)ta*&ke9HEGpWTUSa0D4zAX*=G7s>i3xC-wAwi{gr*bK6g z`Pl1LbPuo|qq0w&*i}}0xezO#S=QL}^9$^deYA??FsJ1crvvsPi)K;g`~J&}p()*Z zLd6NdM)JQw`G0;!`3fSj_fV|=*~DV#q$pShU!cJksHbrY4ILL z_wY!kqy_f;lZtoo#XIQWf=NlF58j3Q5%;}b{2s&Yo!!yV^nE<_IweC;hSF;60g^)R zPu~o|q)32;mDC32!;%DZIUy;=Kqq`gYza%JMNDE2b<~#F8}}l literal 0 HcmV?d00001 diff --git a/server/src/obj/x86/Debug/saviine_server.csproj.FileListAbsolute.txt b/server/src/obj/x86/Debug/saviine_server.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..1e48085 --- /dev/null +++ b/server/src/obj/x86/Debug/saviine_server.csproj.FileListAbsolute.txt @@ -0,0 +1,6 @@ +G:\Programmieren\libwiiu-master\saviine\saviine\server\src\bin\saviine_server.exe.config +G:\Programmieren\libwiiu-master\saviine\saviine\server\src\bin\saviine_server.exe +G:\Programmieren\libwiiu-master\saviine\saviine\server\src\bin\saviine_server.pdb +G:\Programmieren\libwiiu-master\saviine\saviine\server\src\obj\x86\Debug\saviine_server.csprojResolveAssemblyReference.cache +G:\Programmieren\libwiiu-master\saviine\saviine\server\src\obj\x86\Debug\saviine_server.exe +G:\Programmieren\libwiiu-master\saviine\saviine\server\src\obj\x86\Debug\saviine_server.pdb diff --git a/server/src/obj/x86/Debug/saviine_server.csprojResolveAssemblyReference.cache b/server/src/obj/x86/Debug/saviine_server.csprojResolveAssemblyReference.cache new file mode 100644 index 0000000000000000000000000000000000000000..62c17994e4245b6fdf5a64037fc409153030e29b GIT binary patch literal 4634 zcmdTITZ|J`bf%q7JKgSfdFi4&x`GIy^ug}357?01N@W9!xFwKC*E-wY?hZ_6mOIlz z`H+p882!-zn!uNd@dpw#CIde(6eS}Ft4bY&>q=1XoL#UjiE>|u$NFn(Y1c0 z@D~ik@~WAqq+cU>lger^usc7dD(M}hxJN%owEnS3N46{5osC8_T@krMcA>WR#!3=J z!z8(cDVqejq^zP6Mmd+29))7Ms*cJZcBjZ--nh{LKFfwf0^HmysJkSw7@ zOOwEWVUQfGJTa1$#ZAV6pB&cd!DMLHwmns<&>4!LJY3*p3|`XAWW`!7tBmKVjEfw~ zsH%u|9hyh|lrXg(P^-?HT69zbjoQDjF=wQ8sw!jXWh-tq+jB1lOWY!QcyZL^`6qULGArFDs0#c*83$z)LM7E{ctDVTo~3JtHohjd5{_aY-Gc?>@M( zs}l5-RErzhcW1S&yIbx`cWsLHc65?RPp=EzysL%69cz(|+C}upM#7|uHtti5gp!+7 z3GTw`3E6;zO%ub==>SbnC??cFD7-^jJdaC`Op=nzGk86A8gUJ@-BCC!?ddk*o#W4~WfFjR7GF7DKY zD+rTT0^l)4n34b>Vmp8xB#%i9$?sR|;dF{wm653SY_^I)=2Y zE~T(t56TAdq^$bYlEN-KppNTQF(-0ub~3Khn68;rR~M!r(R6Jb)j@aPEh}_MSq5w3 zrZT3eidhs>aaA5S{7g{wectWncqcpD4u_}3iaN`Ru0}yvPC2N+WwdBBc5ZL2vUJtt z_AJ-7+^Id5{k9coprE#9vE^}yt6=vQH&P?5h9}a)0FJ>at^x2YkHbe_I2?}kg&T}E z;YOoPxKd2Qm0>1q8*ReaG*BK&50^%pa3hnj`$~_1A;K3h0?%#Pv97ucI1;8(oJW{~ zRJsPjP#`?vyuww${?WSIHLyB<1!UgIcGzD%g!Z_wc>PbSJdXCW#&u6jo|My&p)f;v zW=%>BCY17Qj{U-ejROxUSh{jNmUGrcHUQF82^&hJ5b}iqumFw#Qv46%w=^0{QBco+ zW<{DX1;nQo0S`DYe+eiIJq!W<6F@L#SOoCcR4@wgsQQh~h56kLh*Oq!X0L?39|BeJ_975fZ&w1tY;OZ_mw7t?h^O4?q(Pvh z7y#z)qkt6V&)>2YtSoH<-TViZlfo1b@7p|nuh!%9HjlB<&hprg<@a?s9UNf76vv$e zLc}BhdL%Ir#s45Y{M?`4+Bbdf`p<8KoxBNQw>apo>4!6GivHS4j>t;5#9RJaEW!ai0=sS#38a2y5>&I1Rs)*dd*^}DGQ zm_u!8_}+kD14f7`({TXs)d71R$Kx9Eeyk2S6C%ZqvRXfu4Cc@IqshHXJHLLB$K+*w jr^H!-e$2PVLNE#KLNF&93K{%UAsE?qAsA+jgf#sDsRx#3 literal 0 HcmV?d00001 diff --git a/server/src/obj/x86/Debug/saviine_server.exe b/server/src/obj/x86/Debug/saviine_server.exe new file mode 100644 index 0000000000000000000000000000000000000000..dab81314f89b8c3faf02f496b56e6d2101c32cbb GIT binary patch literal 26624 zcmeHv33yyrk#60!_m;a`mSkB=OLjuXisVf;vST}zWjRs2V#!V%J2-8rC3oEFt8}*< zD~TPMED%USoRD=K$i`v@-b>yi1co62CK=#CfSCaX9uUIvu?_2kd62-^^H-hQizLUG z4D)@D@6EMu-BWey)TvXa&Q|xfHeY@x8AN2_K6Q%dBY5()R^XYzB(lTJe-ow;`Ce%G zh_>m4mVy20e0(hDjO3D|@xf#^>lEVqQt{k)HlEJL*Kh5Qk2*uCj!?)y-xa;BmuQn_ zP}dLRJBziwM7kEx0z_{F#jB`aIE-f;_atuCE{=z;^hQeb=O=vvH|TsBwDxAAS@OU9 zbWz1Z1<1XW)01esxaiS|ba{1gKFwt!i2^;s7xGS!;Fi1x(m2tqxy3)hAS=KP_ifb*=x(Za1Pq1Ba zv2Hq9bpJ-8#}~2fh%{9jXP@;ocacfYq9Fc9NWUXpds44Csl~hzZ+i_TB1HZivc{Y! zQq*-n(!!(_nf#>JJUHp&4n4v!5d)(XU*Z<;Msdk*-Yz82E_WIJ_u#(Xs~f7|ScFXDeNKptm~)D-d8UKaC5{1M+B z>651D`>ot&2n*7YdT_)ahx6p;A{FvENczc}8$@=Z5$Qw|6KY~++fMR1%|K#S?pDr~ zjOvp%dc)4gk&$oVTsvat9zuT9Otb>eJ;J>6ka94r{1mYxmUE7zOsHj2ZOq&D?wAL4 zz8jnok8>_ErGTn|UWBvePOy}+J_|_0&>^z4Tkp`C7Nbk|k#4At)1B9`ctaIm0Is=< zY4WyVw5i1YJwb3vuzlIZ@ zh+WSD?3mZiJ;h3!&iO19CH+nZK!y5qZ%39EtB=&z*pEi)YjZE701|Z(Z_V46BP&;> zqiG9K3e-8d*}#yfk9ccmW+&kMZjUsS<%*Yy>^Z9NYmz-T=a!aAV!oOB zD?}waflV<9+iz~sKn*kXLv~{^yXmy{Yxd<*%0$x0+Qc%{5l1Ib9$f+VKU04y@ACcm zJ5&jVF;A9^+4WtX<07_$8OG+Z8*7X-)?M&v_-mxG*}tn-vcd8wZaUa%AC zu6MeH<6JlZDUt|AY-w*KSi8%?Xt!e_w*sEuxU1~^>mlu0OdlaN)?|L;oEmguk=O+u zw?n;=K#5*oSC?ObEN3MEyGBK8Vy9Io90}jPng>S}CWSE1cXq6byv_v>5Nqms584xL zN~~gR7DZbq2Bu?B`eTtuq`H*VuD4z1s~90_mm{7LKI1? z1rP_jS!L8m!f{@!cgLEe%`$n3!`)utF$t_(HlePn8fc8!-cM~2))H%%jEa?)oH}*t z#j0r2$r`ZEb$XcPo72o;B8xjWuKFi{QFN+mufS3hR=ycL6bUp9${5=li+Uo_2P>M` zI8|uG)BL}k?W|oF=23zt-wnN^Q8fuahIC>*bWsgpQL)&RK8kp1RPjj6>1CzCQ~fA) zy^qx>bxNCarXFeE_Nv{n9tyn|N>tiD_ro3WzDUPF>;y9{3{WwCC z=Rjtpc6vkG+<}I*Z*Eu3A5NW$Sm;<8f?lkS%4cGAaudyHV4o)sl!|`8r|ah+Q!96R6zQ6J9`>Fob7`Zqy_~JOdCd5dKSC zex)RIL#JV`_uK7(+!bg;elx1M)^N7q0phG&9z>P1m2-3>0n6B=n+a@pAs6ioxOzY| zI-@3m1s@qNdiBIM6v0#n9^RX=eS<-DLW(W2=xN|!#J>AFY>3SBXQ}5MY2%~`o!Yb- z3VPdvx*LrfARFiic4el?Gbhj*7gN#<2kXC0ZSfj^TeYj0Z6^A;A!M5+iQgA{T7*3x!5|T8 zYs7A|Zr3qCL*1y|b?i7p`#8{98X3?n;A#H`Oo4u|=)zLDr6@N?%FRJJNII9YmO-gW!+u}9ykhk43|gsrr%|?YmCGI8`loBkw?kJ z5rwT?A-5!W#C3htZxVJUJia1i+>Q~a!SZeu1Sg52|5Y8HV5|OLMV4wlbkYec9)+d-iJhpWBc#hFO$I6t3iy#(>u;N;j{p*{oxP+wi$RD155 zQ6Ku6>%)?P`s$3!-2zkWSqFP^$%oERAL0zBsc&#beT}LRKxi@6` zoHtD==Py^;wn&{JR12=Eb#4Ubxu$ay9#d(hu7^!;MlSo&%Ht;`q+_0}!3MY#(m{uD ze_`%mr=tVth;iI(c)oaE=E)33#yV$+gZlHgfEkCP{G0Jatl9P@a5};9=EbO-jL~!L z@XsC=`AwX@--1L<0&}O|NW2v=07fsLyoJ$QWb`09kGXe&Ci>J;Xg={#61r8Jd7xWL=d4v7ogfW>HPTmZUBT;Kwci*`{Wg4cN zZK;#KZWVcslmwI{{Nr(nCMX$hzo0VLA6$v#24`3cgn!J-?nE)W7Qq?oq^Y{OREH~L zQ&EPg&iUY~Jm;GUFz`8Fscagx`8(n1&M|-*SAE-6AF%+XUJUOL-%ykrKE2>@NwRgh zk{vb;({k>r5Nz3(7e!kL4^*`MKO+`kQ35)vxzKR#W<3l!FrhOuAfo&{fP<*c0JDhr>PsoiXtoPzV|;^_d60563(vflFhOC zDmme6vH87;3JGUt^SYwV@2{x4&b2ur0eNoBguf^ByNmNMq6Z`wGD(Zf1{aqp9Ec%<*f9wy6Yc4O4&T;5q zTy;g-?=HXbwwSwin$APWlL1Qzuk+gjOs3z(BFb{GPn{vh+NC6g&C84{{se*3;ux>T zZ2u~84Bp-FTVZT*{wt1AakT9EuQlc;=rP$iQ;pp6k!2#y<6GUPmNrO|l%D_zZZ?E9>V#-?LN_EqX7j zYqu@LcNp*Eq(#0`@FHTpY9qjYg4q$FYJcv<#)v&`LQ#(SaMPHIWVvZ>lNEhNlbKu& zb1cwcY@IHkwbxN2nuhdZI&i$$_5|Jbp>j+^7U5oKLMo;qXXjUsX>?+3oEg(lC-UmU z%aDth1~XO0G|&ee(|~)pF^zTT$NYW(71KZm7Ez9AU|U~ACznH%ifQnehoE)+m#oty zN~yl-J=oc?sAK7(rHi2hkIW3<69{0=L-4Snn&^5s;=KMsE}b1g^qc7Zc|?B;Q_tJc zPXn;iEYRj%w4)D|(h%^qUf}C8&OTV&9pnf+7<$A&*wm~>7N4Jx*rStO@Q^U3NpP-;AVkC zL6&?((oYNgTY*0j_=>=1a7z)M4H!vg{jhPbXb3;jcq{E>7(5HLd4QdOww(lJ=VV%21(+3hSm4_PJ|OVDfHplT@M-Yd z^eKU#5%>jxUlaI4fxi}bN}#PXe^_9Rz$Ss`3S1&^jlfF;?i9FBU{>I@0&fv`m%w)l zd_SN@AJ*Rp_^h6Uo&H`=VS%ePt^%dox=PdN3M;E|8z0klPJRXHos-`NoL=g$wV9>1 z#nBaXjMix9LfZMQ3frcgkD23Q7aP!)Vh(wfrx?1WZnxHjp1e)4V|5=7{LHZE11itE zz&MP&zZ2}JcZKmLV4rvME;il*3w_VUE;Vik_EW*`^$r_%$;S zeQyG`s)QZK9QO{vo})J#UGRXn&~zddzTpn z_GK4)zgefn=?^aUl-U4m{SqbRlV+>dLKzqPym>CL54qUa%=5KY`lgG0&s+p-{!%V? zlzw3@#|R8!O=ZlsHfZzdZ!y%_p0lh=;dh&4J-vnITUThWqgi}xN8VDa3p=G|!G55v z5Uf+MqqM?GY3I?HV9E!@k;oALyjd$^f3H1cy$!z1mqCB8J!{>iNe2n`IqM;92|cIk z6W?7*-%vG(?=Ge9yLs%po%DMbW8dv0^8%Kte0Lem7fku?GTI=R^4(=LP{NMWa{7Q^ z%6GfyB~^}oYCVn_?=qbCi@v*(?sPHs-Bt8W7h~UDLj!n=!1nx|^@O&TZWHVlvh1g{ zZaPuI{zh9zFO;xPX}$Ez6833r19h%pDO~Q0S|8n3!oI3)q@N0=Jb5dv<>_2FU(&Ww zp@jWN>!*iH*w3^9`eh0GSM5@2SX(Ujigp=gO4t+HZo0pO>G~e}Q3>oHtv*P1maqnWh+ZmT^Ysx5t}B*1Pv1{_O4wrk0KKJzE!Q*jwGy^QcgV!XP_?f| zAEN;m3)ma=JRL7#Z_vl-r-CWJo}e$|h^72`mwqi>+*g!xm3}?_sDwHCVfy|hDv$m8 z2>n_x<=02ZyHQDDzdl0i1yg=~180={x`-*iKI-NvzrL}Av0w8ZQKNHs@udZTI(0~T ziNMtrut(Aib=o57U6S4_>0wD{&X_(Vxe6YUG((-HQ#7iiSC%@P^qWQUZ6f(@Nh=D& zVqNzN{b8ZJSJFy;g3rcQqcis9O%T-QvxE>_Zrc`9@9 zV?2?mpvvX6LH|qWXOcNnnxg!_ME|<9_q)=Ta@wGufMSaEG^+H0UkE3EyROk61iseZ z$8X=Tr-;x4v|Ah39}##$;FAJBD)5s6KP&Jb1b$uMcK}n`k0t#pfiDZxwB6dZdIazZ zZ4O`;wPfxi+$tzVr`G_&62(i>8pKr=`El?psxVuIN)1-@6!9UM}1xN2JIQ& zd-PqP|25!uKuKyp_x(@d|0pP@e9!1tBll))0`NKgsJ6lXIh}dFs848H{o{b&@m;0g zs_g^)JT2?*q6w|w|262hfD(kZU6j_|hi{G3+DH9f;}Puzf7tL5%Xt#gstuMl2k?jf zX5&`T@T1yq{qv2FN^Y7?`42=;lMHDJdM*n(OJ{E zE+62pj5(5S(Hnwqr1Ov-$46Mud8Bu1N6Z%eJHfXA{wjDJPzyZ)=nFju zSR48p;5nh60xk=Y)uOKnRa^6EOK7EazG&V}$1upWVgSFo$tt!*^U zhs6f1TlG6aX{(!#hq8e03*ArM^!3maSs^|1d9MkgH#-vs!k@D0Y*VsnPII>RS4hJIM@zVI)t ztBH5?>!nA2EYhxqhHs)D>nFlFeWdBNkCA<_-OYWT{ z1*-0c-F9nN+i9w+x<=q(oK;$?Zn0bRx~jJcJSOlyfe#6MOyCKD|6Slyc9PnwOf5+( ztDe%15%1KG5$|V@5$}eN5$}7CiOxP+UzN64=dakuMCS+eO;z83hQonZ?3eW$@w?%d zb@nt1vkN*2Ge8w!5c7bAvq2278vA*R>adTuuI0=SB{ z0S3x;|z)lC{DFk>O)p!SKqy8T46SN!fC4r=IIxg@#7N<3vA$cS%FfOn~;97xK z3jC^mpYc26-wfSsFk8*nnaj=9X1CdI?lP}551BWaZ#N&)?lvDY-)lZ?e$4!w`9<^F z=J(8Bo4++ZR=}ETHCykserI*q+wBAP$L#Ogrl-}zQ2{UPMl@l`ug&Qijp!!pdcb?E zqkyNU>o^q}DC+^#MS=#KT1exkdVu)w7O)FX4e_u5(ksz>8eIUWqaUgOSECPK*7q9Y zM#Ad0c3U@F-?Pj~UMDKAbAYJGn4)TQha{%b?h_j8V&cY^1gM{5YqV;!Gc=w_t)YDK zU^<;m?ailh2UEF@)S(n@Q-6J7S{7~S?=K_^8#2ieH%YzOp>#64E}c#0Cbp-NL#Z6~ zPvi@!(T={Y)Sb&EL50wK%H_6MDeAVqEf=ZOrmYw4?H|}q>vn9|(7Szaf8XW3lymUm zZ^jwP@0HZ%WI9WTl%|rSZVNU!BXn^xI|S7_1`GJY_#msjcQ0*mvMCzR9@x9MbMII# zl}}|0G+IcHrb^i06b$+4p;F#ZItN2IIShN4Jx^n)+-N$VPvhHI=b9`mH}>iWflC(D)^UG@Gq<2U+(e`i36mDXy5qoFj_wh2Pk@&>v%m*CIgef zLivu1QZP|^kk+S#04Ke235hFhO6Ln#E~bI;u}sRP_293H&{z`An(Ijp?oYY>OCzbm z-gU`*N_98or=pyzMuGS?=T@MctMr;iEGoSLLnp)iwjrI#tW%Au95+j*jH1pdSQqh- zTQ>pUFDCMm>MzkGl`oFT0seC#z1cx$2oZka?Af2pl@fWnC{-wC_p?$gR=p3{GEjj? za=kOoCW?<^9Rcb15YmluhPmn-59lya#mSth$; znY?sKnd}N?GD}9wwn^8LnylPWyL~+ClHI0`4;IFADP?9+0?k*nJoRK8Ok`>fO3|va zPpmnx7alTr0PWq7PGyE@Rk^okQ5u(&JPVPW=0WI1$ zjGuu~XTg>5=hV^3}3O$CBBJ zndPuV51~0TGkTn{iClVQ|7o%Ya#*29la-Ar>evIT!ST!Dimh}FgJFc_WUL>1g!CZ# zx5pXXna-y%1C)zgG@c$RV@1iXNoQtY#!Jn}bgNyTPL5=qJXZKTtxxS69~t3Azbr%1 z&`sFHK$~gGLb)*r&sAy*f|z)FDw8}UseDhvMneoi})1 z@6F|~%nM-?_D`4?ic59xA*?P?yh!NF4m+d5# zecQT+hA;y1)SaCuHX0cn{m$Tl6lxq89OH$JS3Rs=$y|ZgFm`4%VK7*MbB4F+IpUfN2q)k50!rFr>&zqDqrYw>03m74W*Z1qG=05DVu03EOBhlueFc zb<2$79X3;X#&bDLBd!#tNHMkmL}5QgXb=xL7lU=&lxn0Bq8fqeZJU$Ebi!h`r$(Iu z&v=TY)&uIjdOVpyS72%wzyKG{EzULa?C$UB>qA4ZQ%etlBU#pcTM|)*p!@b#w!ND!Z)U~Lp*3@T*$VFC(H5(SeGuq zN3i6xCIw1#O2*lno0+!l`An=F)k%8Y;?PnnstgQtm=C;J7;!_W@T$ zAq->pG2j$k7*FgL6cj3U8o&St9JSjZ_n~m?I8*|q)c!*1?ZdGL?tt~bJgkA_X)_jw zR8EMS(c1mV3~iOAYwIxAvkhx2>n%lPa|*lgQ#PcW)0dYmA-ScyEBf4>xtQHOeYk}; zVAU&AddBkwXSCGaa9bxgiV0yyb~KsA039mU+nY&oR}0EpK%VeqocQEGT<{rw9i~fpOZ6w1a;I8Uja-t|02$?o#3|Erl-< z3ZUg^0`jgzjAaoW#X!@tjiD&BBPx&V-LRmT7^Wiq57!hYt(kj`eX1YQbJAY!wbjs`Yoa+kYAQ!cab%D$Bjxbf+U$Mc;Z`Ua?6ITx z^sGqb(Z;eBoo!kbL8=L5Zk0%bm0-ETE9;?vD~9K3y?8}`l$QN^%h4tOJ9dF zJ6*{|Js|5WZE+1WV_#)UDQgg}Sjc+tsZ)>iJo@{wAH2AYyt(%O5wR-FQZO#U9aa-S(Oq8y8lb z&!R-0<%t{2E{G|J2_r!UY~YKVTBG5G88`zL(79YlxI(H(P>`V4D^IT?`UUYT#1_O> zh({2QLVSYw6cP|5;Ds^KO;F1O^n0h9q>nXFEn3aVuniEIyxGDR>67=E_)B4777%W* zNsCTC1eX&ePL0vYM@&09c|zP_@&x)<4~NAIQ38dZVoA}-kD`0vq6YH94W0mGpkO#U z`9UvOqLUwwPCl=*ER;u~k9*l0k@Z=T@q)mA5cn0S7iO0aH-?dfUU10CZ<#2z%!}5A zbv&VPI2>XuGWp}kn(MA0q~uhfLNS zP13-#!_yjuvY#y~8J#1Icp+LHjW_zj==QL;(SmR#2Ao_6dJzqN9;wE#4waeVWvR!@ z!n{21iP?|(Ob-t_4~CoJ(Zm76VRR&f!XwDgqN~xN9=MQhdUQ(%ljX;sfd!d!@MoYJ ztwsw})9W!Y0vk=;8#fvaF3!aa-3DyLD;$KBff}Ex@#Zw=tkcW=vi%WDNZss2KM)d+X6& zl&_1nggx#o$BmRuj<&$JBkhGwyDUft?};xY-_ zUVkM+q#ef68(~AZWuql+SIP<2y>^PuFq*|0^?`|^M-OAtXlV5KDm5;0pm~S&XhhVWpVH{OjxD_drGVo3ZtQC{ zf0che!g<3u?XOnh8I1Qp zPa(X{#DQk0-x<2Nx|_^7=(9+V`afhub?> zbPcz!NG?gX4-GC|*15EE+3WW$8p5~wc-6TWKP$jLtlU7PHYDUFBQ^22)dPCXrMcu7 z-h`D7hlu|;*W`s4zyERnZo3XW8$i44dfaa$+TOpu|CUvOId>%*H$Ibmp!@lk&b^6? zjI7+_zM&jVrw~orgExoQq|@W=qj(*Z%I$IAXqKPyW^)hz2>Kp`mG>O#TE0gDh((6t ztIx5aeQc_~i0k;_vYO9uI$p*rQ^mi{UP;3*GJ2d`@1c~u#*}xjsZ_^MhJO!&zucbZgTY&S4WdqWDecb%TN#3xkn<+|&d=LuEEKxX5 zUF-4CWS5#!I{Si zhj;sYvMuX22sy*R`P?%-F2QmZVUU;Q>;w;=Wy<9jsRax#*|ghR?oF!=WUS3r$@YHhh~+1^T+JlDfXROe!7*X+qg^0@JW)b zH!3GLjtTv-a%|UMeC*j(7akhT#1RMP*I=t#F|)VEi!bX|w<1Q{-qjlCpxRI}gAW^4 zw@#$;trxBd`Bx>~mtb)S$mUnKj_0x~^LUMpIk|l_J(zRySb5q9oza!a{AkC)#jSCK z9@E3Ae4)4yN^KAokC%k?4I!q8moZZ`;a(O(%AxLjYH%E1rA)XuD7n*7$f@y`tD@11 z3Vm2=^a~A*63q$?3zG^9%U4=d_`(;O+Fk$*Y8#;oBa7u9KP!4$LP*W2> z{?CWk199!2cf9>SybT_y*@gnf{|osSB}l(%RLhRH^zJg{yJfeJeQbZPHC|LaQ8?_I zIXIdWW*`6i|A+_T#=owE;rU;2RVRx7N2++-%)@x#c>5p5IsXEzg9~Pk`QZt_XuEz^ zuygvCn%uc-!j#)z`oquLt{-FLEx&NnrEM4eqU|GngWzEE$&ZxYQ(g44^6RHQ^y;`h z*VTUccDG~bTV`bln1qg+C!&89iWa-C+IY&Gt>p@3Y`w+L*1b6 z&>2tVCPU{#Q=qBPH0T0IoqGl}6S@%6bMo2H9OxqGVrVXO z33MrR8B_wzgD!{WL#0p|v;bNNl|vQKB4`Oz34Ih=3N?cULyMsgD)g7*2_9|i_`e@^ z54`*5cmJHAlK;>@i$|8ssI6ICTefUjWkqd8bxBp_f~zVkSM*<2R##t9TT)kcWo2b` zMH(w=uT*mF!jf65kXTkSd3wo&>hj96>T#9TWwom=uB}9`U}2rfd-)OOU1#&ItSPB5 zc`q<|UzN)Hn45iJMOnEuo2-wm)wSWQkJQ;tPkucL16{?Gi)MEPbpk(AnFRMwHH_{2 zeG0e@($(R3_%L|jwC4sU_EM2=-u|^KKhpfEgnGdH2~82!dO>=+<|^nqr)#(Jo_O0u zHM$mx?|YW6b3UjydD$gh9(-A{>TQx0FD$Fxmy+$Y``41)4oOzLuq;W(LnAz(_1@^9 zuanRRM!=pS5aM2Te*;RUz&g6it75xWfcXJtGQs*ET6@drE+0K9r>kYW>>7N zCwtb9`Nm$#G^kbU_*p#)dkiGNZ$gh89pvOZKIY{ge)E!FP2SP7MU(H`yRB5Z(^%7p-f3U`?ERSJ*EXR&Xh-GhI;r=#0)f zNIG85+uiIib{D*t-Q<~*Xl?9N+sMuWXQ$tvp*r}$%$~d5+gY%%zNS{|=lAf9DZ~0A zTE;H?rl57P50sa?D^iAh&>PwHkYqhSiw}l`+pcl;-O*I_^!5#MKImr~)A-%n$308# zi8L71Vu8=%p{nv~HB{ruxpzfeIkVN_3E#3{{^hl%HREF-cIBfZ zc`qYh$eqle;eMR*g?w0!-iy!(4t6*(x`97?`qgF7`bSNyp2{ zSE?H;k62%+?#evP4+0jX%azvnI%M{FUx$6J4!Upjfq5f0hrwm~RJab@h^h{q(b)n? z$IJaQ>##`|X@c%2eZXP4fr}_$jTyJvk_jS;^ zecY=03esi&W^8YRj^w?J?3a4;%zl({?P%P*aA;=SypTi23TIj{|DJ-4k40_d!YLak zVc(OGu>U~V{#n+^vcEa<^^oMfjQ24^ zJl6ikkH^e8#oM@piYpAaVE*N~Q*JXF#EeJv17l;I@$pvdTZ@k5y^L(s+)K4$Z4_UZ zk_g|-&4r}wU5jXDf`dVE$$8kvl5v zlkrQ+xcnQR?8LSu=t$nn_?}ZK7_I(2=~C@%3XpaM=1q_zmip<{y3iM!I#&B1ND2z*YCWF`U%ww zYsxFD7teA&RX&-z-ba=~o4s>f7#2O;TrIp1Ea7qZqv{V~E%)x(d`G+$%X-a|CLj+tu$-yffJ zb!2W&1)g)~o=bXq4l;E#^&3d~Mo98r#qFXt(`m9d;+tla80mX1n?f?GS6b>ZW-=!f(*0()`SMe;0PF#*Q<{REV*9}`)U>=&9(7%Y#V`&)oYls^ zjI?oB=UBDz3>#ek+!j)e^%1r z6~b+tkKG*^BdIyk*lpKov$44t9m#u{Y<8=Cx&Fu6?b|oPZ_vJJerEfw#f~EEQ2X|C zw)!y~);U(~JCH%95N_XY_-RhWPs4`T_Fau_Z$pyzGHE|GoTphw#QN!I+jlOVT_N1Q z-LZRl1G{be4xyes(2=~C$!53Omo-DI-M)Pz{08lt=4ZC=670yw4z=$Mk@gMi9IN(i z;~pG_+n2I)w?_O_ILx+h3AXKnB=2R?ev)VGs#<;^qW9~uemdIr9YJST2)Az!?EX;$ zyKVdCQO_K7B=2Ri*{xbozgW9{`$qT;+BeP5Y~M-pH+HCf|0~kIVVz^uzOOnzk#{Qe z#7|AR)~4pUIZSjku&!gHklz7G-pge3lQQg_#ZO1tzJ8tFna-{dZr@(m-L;|Jb&`|q zZ(#R}OeoUva@lNG8?zQUY#T@T4ca)(&urs<*zi20HZFAb`Z+qRbFA8UkMmWyjd^C1 zduh~HT!iJPeB?JllJ_!cKQ&xuvG$4e)6urEF#sFFZQL8XS2VOc)y8|V`w1o$>3F$p zwyTX+MR0S9T@!pdeZT7*=$!EvmQBY8%OvJ z+BnV6Y~weuV=H#3jei?ybeD@-@ zZAM4(UPk@81)|*#;}boOi||uEk-i>hMfk(?4r~zV;`1mZ${#NNV8>_aALS1epCzKv z=du^&oAQT}?w^aK%V#|ns&i*1&?@SD33VRn{Nm@<44oQX_w94kaC+U<)^}}~)RvIX zN66;_mygPp>%;Qw5n4u)8B=r7DMu#dL0tZudKelR)a(K@i@K#k0evx4$3Q`yy3+E#C?LwlMgi7 z!}*2#tH!q{%^ULXCi37?8r*tHI*%riJmg2O*SL2Q@z2ZKV3@*9ri0tR`_bngb7cp- zd=K~eeb~0Io0a!6@~PV3jLbei)W5g&K9BG-`n-G|;b-)D`8~qV==1V%gm3cqeO_ZS z!Z-P=&&xiI$v8gCZqv4H@`GS6^`4EN_Bh}8v69x~U2CYnHb=e+(s(Q7z5-R#AB16@ zE6b`(TdMt~v&rdvKBS}eKb+1Ur}K-DPSY$p@2}qkX1>9H8PDNXVvNEP3+CVc$V}nh zMD+{YbKDgR>sQoPSRdxm*NV}Tj+gU&hkJ*t*AR`@2tU<#d>f?s>G?`L*%Ic}R@AMi zs<-w`#hxPUxfq$*&Tc{xO_|-Q1kROG--p5@Ql5ZL*<0`HX-p9hmjn8;L=W(;3 zaEAr+ZvlS(W~`s>z1C`M+)o9h+6mBar@NM3#p%gfJdmwIIPKojBrA|#&^k&l~qkECtJ!2?4$tNbgmZ$=1nYyF1D}W850;=`+Yru!u5I!VGR^B5cCrT}q8DEvOylbVpNHMk zl`wm5luI}d))m+3TF7l&QrA3T3%-tM!IvB@(NG>6A-g9lk=o0pHR*atfn|A8*JGBy z=FJaF7E`A`{rTM__0xTQlBE#W)gYe3LgF6Dt|MLY4>U+Qv1B9PjDOQ3d{qheA+*WpG^KNo%Bsgl6wk$vytR_X%Jb% zho^5K)CHD*%RE#-5W!%|T7SO}g?x)#ogF9$~wzXCi5TN%$3~^mYioh-4Snw;L63gy^TAOwR^!5W?z_ebzRw}KM1qPQ(ep3NZTi6<6KZS zUINO-OF`L4pD?zSfPIP21L=$U#wbbO(sxEl`j);)O427xzO-pjO5FE%>-Vtj;lJbS z(HtY~ez1hu8%CCa?0Q>&5a^p3^;k??^;iO`9+jZvKMJZIOTm1Qac$~BpEmWV24#N@ zSOn6?lbY-4<4M{ps0FE8PzU0JpdKs(SAYw^MIdFHdQ(<#CGp19JM4q--|_y?7`FS% z66T(cXrTIz(jSCXl#9!Pt3lO&HP{Zk7L+}$nMFmupOnJ0d%+TB9ZHlHChJ%IK}hny z^1A_4erv$C;EiB6@Fq}w_2Zz;Uv~F^216ZFD7oGa`-76#7}!L5A$S+4vG66(+S(Hz*mmST0FLIZlbKrv z8tljT?Pk6>wBtJLFI|u9Ua3T?ZJU!uzct|=%21t{J1NWB(#pkU`@N)h1-F7+n*!$6 znYotW+cr}K9Kq&6Fw@~O(VdzlhuUz23R<$s-gRQ@)TU%Gt7RsKVu%KsMF z2iy*-k3J0genK=sSd#n*f~?TTaCX>f7?giR?9+2v>chFlEaq;&x9@LTf)0 zn)0vup~4>Wk}bak)yIF8%KMyD9-2e!o|}Y!WFO>^ro0uNc6s~;)L7gH%E!-u)=u>m zyT+4$Xmhs@Cf!$ClePs?*zfXt9#kLtJt*5>05vzsAL=tN67K-M1oi=62K$1ND{wd% zl>9L871GtG4}jLLUf9*otP4msK5Wa%LB8zGlh^cGAw4h27j|z-yf6Mt-ty;PK>33D z8ehDgDzl+4?0%7WU;NGG_YY9Mcng#--T}kDc$c_*aR`(zf`s*jb?FmCLUUC|~e!#rT5nt1^6% zM_j&W2g(=iLG=sC$rr+~FHR*cU+|5Q^M&T&XulZup8dk^cZm1JY2+8Uas`oiu*i1$TzmtQYXzBm(9zvu&o`$b>k^2J%8e9;e-FC-^l2*bYUPh7qz0ByfG zGGExe0r9>lByag*FeqOP0p*LKsWKb-!oIVQ_r(a8-zZSNI2V*J#(-g86cLv%#)9(2 zI8eTjoO~e+`(iwC`Jxy+8eiD==<&WdkG$oJ$)J33J}6&INtM}fzOe7M<9%^~%kM%^ zzL*8d7jwX{FD@c3UtA2z7jr@RLUQtjFzky1ly z!oHV{_eC9f%NHv^`Ql1YzPKt?Wbza7*%>hs`PPHqsmk+|ycMQ|v%3Do*PI2u%bT1hEB6}%Ii2Hpjh zgI@xdf|9ENHxuXnFSr}L4!j3k1AZC25!?da0!n@_xRp3-q~JdA4e);OP4Md=*XiIJ zU^^=OO|TRAEs*O)upJx*J`A1%J_0gl1>Xf3$HAju3HUvbK5fdQ9|hkhUJvdD*MX0N z_|Wk2QSf8p+rTHlZ-f5{{>brv0{)cvAHXNU*TJX2w;g{R4f0FkQ^8+>i@;xlOTqsF zuL6Gq-VE*oKMU>$?*g9%cYx1<-vucn`D5?}@Okh>@DJcC;7g$V_X>D`_yO>b;NO%E zHe*rCVG+q(Q1Y!Db_Au@1$>qC)4|ukZr~eWcgODm{*8D~@E|xCd=s1q{sTM@d<&cm zz5`AN4}mj4<$n>V{!;4Vi@`*bAXyDI0qb4*O0X&MRbUSI39uRX?;tO@lAi>X->1Ol z#6JzT1aAdffnNe!gPXw$ARiGWzYU%Y+IcE{uRu3rJ=4dmY1;DK zs;xPt#@}yt3EJ_jtZ$%^t>>t{i0e7mbf~vU>&X327oO^L;VFRr?&$7xbbs27rzYJ< zZ`|MZE}|RvmpwVjy(hLP^a<|d@1f8P7yl%G&w{3ycz;g9=^tDN4Tj$P??LWvrGHSs z-O+6567&il zL2a+OP;uQGoeF9m?+E6D;x7Ta5WgPm3Vs~y2CjAZSx|DHckw&H?(o0j(!b`?A9nFa zz%$@~A3PKMfkW<3O+LSJam{snZ=K|R&ZNKW(lz&~yn|pL`2PU=gWTg7Y~kjZ6G5ft zfdyb^m)_l__jB=q-~jl8z(Q~$I0(EH91Lo1911P~hl2}2`Kt<)9cvtNO$$blZpTa> zEpPjLdq#r2-|m5hdUr7iIvvWV-;6hD-RTouv}=dZmwO0>V=FV6{HD4*&o%a(PS5ng z_aj|r>^ts6>K%C-(w=aF=TJYY8H_RYWK9gJ|BM4AHy%_!DhAb$CV(nsBG?_2zjVzM z4hPR8{d{l|s1d64`3_6L$)s0+=Y!U-y?8Eb$3X|ixg8^C8AQy4w&uM{{5UDbc>8WS zk$N|+_0b4biZG3G^yf@d9_wIG&o}0Ps@FxJ>UA-AIye{12QL9Nl4XBS@KWNvz{|ja zU`-fjGSWW>E&#osTJz2! z>Z{E655wpW1E3qsyfctF%)f{5a}Zf(opUq;+jqu^)U4E$G}Tw30$b$EMWCM3EKb>$ zpPIKOk#64!Cul<(asWHK^E}<on{;GnL;q$+XvFj0Cf^4dG`* z%Uym~fX!jc-z~vf;;q0sPrc9$qC5H#umZd=-;I5 z1I>%7({@D&GjM)BSPYdwE1~t!W@rcWB=jPLtIRnmL%^vJ_dTZJHiBB;v3}E(!XzD+aogNmUNs2*Am={$m6kj^tW2(`&Y4l0I9p_R}EXe+cE+7BIs^bLJ) zs0b>7>Y)wLR%j=*7kU-ax8V8E2xta$JpAwX0NsUm3E9v8VRf3j-^>vRHuC%*FaKda z|K}Y1@>?Kh!p?z}zL>oMn~rhxC@9 z^CXmy+?QSc?Ok6=>)Cw$p2WuO!Ls{>qjh`2)vXWZ`A|gNp7C`HpLa9J)~yvfw?WeJ za=va{8e;3#$<-mv$1g{CW>2&Zy51?!CaEx%I^?n-HDe)Mhat8OPh!8Wm(uZa|I9kf z=Gv>U)PnhU9Hvc(tHTIehYi@DkDheA+&{ApZ@W5Z-RHx2>hOTC!}!{YvigeDKv936 zi;mVG((!VtgY>ChY+q{p-1!r;2h;Z#zwfY=PN>k?g88=?JNJ1z&3M*c$i|n^*BwgpY3jMKPrGyVR3)g!))w_O=}uIs~jlrb_= zhT5_pvb!P4dcNA9=noL7R*E+`+pVwdj+G3WXA*M3TcsTRz?lc-ZoOr5xiFlYNn z*7M)1PJXUS*V%u?`+oM!+OtTaZC+GZ=|+ zp(HR4Qh~KaZmUkNzh*i2W+ioKg|76xte+#&KF&NB@^JMmB%j95A(?9r--q_Qy7W%f z#OyE3o$2f1#-7GSF|u1B$$GxG|LEtqEd4|i1|e)8eK>bjjD62zTM_b-_58T+0~_x% zyDp9P-4Z=;ATZaa!Zd8UGsY(Eed>+8WIaF5ro*+fp0~*VTga;idG#m0)s=N@<`;WT zOSGP^QkFv4_ZLvl-^bLm6xkh+WIaD!PZlZe`~Rc${ayx&LfH4yv8gqWW5U-8`Rfhr z*^InoJwMK-#`wMm`D}vvlUD~<)(7ePDm`>1gnd7Qdd`fg=RWKjfxKirKdzof>wA6M zs1Wu&b8_yg7@L-3Pd@UJ_53)S8sqy-R)takn&egC4XXV~`_QqMNywL zZIEO=KV8q}q}p>Yj@I{2wk0b<*!Q!r>Gv@<9l#!)rz2UGvfAxRB z_vI(6mYMy2HudboA~!m|_h8pR(*t7(DIv_7u z&yTaIF}}ZzyzrxAgs|@~ z#->MNYRWE{bvqj2XPmddEl`x7ao$4fhWul8 z#z>MnKgk+MXN;_Me$l+7bxGQ0_J6*|86)-FKRrP{I%8y`%SZiFt`FxEKC@v5I=j)y zLFlef**Os%f5ynG$nQd4XN)|~gEiSG|8k$6QO2=4Vw;CzR<()xM>s!!-o!TK^N^?h!F3^d^_Q@GZF%OHIvU^h?ndT> z-4@KhR3P`~F}|IRZExgTSZ{|2Uf-^x5U9L5gZ>!h%dla;vdj{>H;}L@iP5F1A z`ljYMki2W=a?>dL2&nYKC$)cRZd4mD5o zBhI5&-3ul=xpXe48b{_kb;%7TPQNkV%P6k(Um@`kAoI2PUIv-uc}{MM!x^CDXMscD z&jyEqOF;5q-=n@QXF*7?d#lNn@P5wOXpLol@8w2x?H-T#vo;tX#s{)V{dhF-9B>S% zdzKFCPwP`+L`;Ig^Lo z#}R+l#>M21h$&CLzKpo)Q39$S^FY<(a!~b{59WiVU|+BdR6Q1e^ciz@M-f;~TyrRk zG4p-aBJd*Oi$U@>XL+D&&hkLloJj)D*zr*iI!v2|>z(cI__H?fr`r>fa6A9abtiRZ z#WKp3KdQmb&K~ux8sdyebB2f=4;odvAf(T%P~LXGMd)vJ=lWu6`Lcb0Vs8UU+40^-sQapRNuG}%mZ%%xu%%&Lb`&i!%W-Rdf0P4H11R< zU!R@G+5H>|v$un&_OPYSviT%=s6OjVUiQoomARgHD^TM@@;4LLym$+!`S3qL*&sR9 zTX-fY`M%%=;%djwf=9JL6BdSgKY>j;UIR{FgcO_fwo{U6l zFGtvxd&pC^d$`-q? zBL1w8Z;+R4`6j4-DqA$(wh_+<9|SeRl&<;kA>w1e?I81f=**GH#MEBd&-y5ZZ}&(f zbgyPYF8|JUfA1hq+42b39{et-@u;%ZHjfflAKD4p`s@6Od{R_y`g;iFXZJd!%L$MF zAE2*terWRHjC)Y|?*^6s<0f6>Uu}Cnly2KC@b6>-);h76zZKMvHk!Z8H$x`xpOHsP z(w+p>wyJ~1$5X`HfIkP-|9=7Y10^@m;ZRWW^7}7Im!E$H+B)}UM5u-QHM9)-qGxcV z+h4x0dk^A$@ih6$7yC^9_8b!V;=hT@7r#xF8}AFdhalb;&$+yR56TyR0K>j`fw+9} zA}C+H1j-kZlP`o}U%WzGzBm9LsW0qafOud0nSAAo*G&H0^Mdll8=!pgSCgK-U)Xp0 z@xC~SuJV2hlrP=}!@hWjxP0+0C|?``gJEBsLR`M!8%E;`zV9}^keqxW z4Ey3#;_^iY@JM}O-<8Mv;xzJ=FS?ri?Kvd!MLu!)qFbum_%UMNdB^*rhs*m+Q2nAe z81_XU;_^jbQ2pX8Q2j!3@`W($i~hvrivsXSePQ2S$NQp?eB}$in=<`kC@5bH1Lce1 zCOvzHSzTkT@cT;QL|Y3%(zA<;IT@`;Io=7mHlp zm7sj_Q84U_rNre6zD+j1SO&@$l9MllVPEJ>68T~|cqG2qh^~FdnlR6yi3YN3z5XC@ zj)d_=J=m7`m7vx$x~6MAvyymca241Gyc+BeUIP|@SAoMo$)63bCT{I43bdA2uhVS5gTcE%^^Grqs+VvyD7lF)J{8zz2G6R6ZmbgEBGCdxi;7Vo&)X#nHSADCd>im{E`xI7wL=#Qy%>>c#Jsx zG586HFU|QSw>myPH0PL5cJMR!lx5B@`H|zJW6m-81NaO0uY-HQw;g{R4YHT`RPbq# zZ=cP1COX&T8RC3D9Q-$UGx%HZv*7Q*yTIqc9pLXloon(EsB=wT2A>CC0sjE%+>-FQ zCVwV=fb>_vzkz=Nb*{;4pw2asyuQa3hR-#@c5|-DU%_th{|B+_lk!9s^e>J90dPw;1KX5_(129Xf7NE=7S@^ zUf@XZOi=Y72&%r598Lyxj)xrs?|n{5SNDXX3)ir-c{|j{?BXDAo}C>j&z?o1Gf8v~ zO7`Zv##MwXV?lB4>a)U!(`9^F*{B z(s?3UXG{S{fy~$D8mIF=&Lb{=&2xA;cme72UA)d=Jvg0o>qk9r(z&36gFJJsKQTz3 zPg2d=B=d|CJ*V(N`mYhuwaUIHPo&D^|DnSlf$8%=Mjpm@op~p&m7n$}orNEDPLj?osfBbl zQv0A^=uL07b4$FB)zo?VEXKiG63CFnv+BEkT_{(hB4Zj-)iVL7SoN(36n99eW#UgTs46Ix~AVv;gC2l(LHnRLpjLFg9?&Srf1_i){o(vqW6|%kIH*Md;Zfhg_?tg-m1dHrl~q>r zxAuWZZzu9zR{W0yJ=)HhwW>~A=T1!jT`+mN-VLRA1s7D*lSf9pU{=k-r4{vcLHAHP zTL$YZmsJ$-%c&K$HOpsJ)LvP+u%eFV*lFF_6)SaV$;fZ^622*Sdz|0v43e(wU17MW zewr2tIl=f@JyPc{h|ds-^36WlLej-&SVZ||AFZwrI+#f5O5z%x~ZPKOU~#B!#ay9`LTC`--{@{wPgGW zuo;4qm)$JyGxVw|X}W6t@}bRFZ5+*$=S9Q^yF6uovxv^Zs!D!# z&*rC&6wa^Sv{QP&o${5fy06nSh4n7q_T+m)L{}B``yU4djrb-Xd0kiJU$wV%eZMb} zBM2QVn17MWRo~`+CYYMVf=2EcGtW!V<5n$QMxHteVY4dY0>$S3+Ltk@th$_LlMi$s zxSviZSoSUt4j- zii$eACO)@gA*^3TldCP((-tFWi+`u{{>`#3yts(H(J!PzM^0M>fNSRCQ6l45!N`$w_ z#O>a7<)_87u1Tlzx5|I7dbdG-H8ca+p-$&W>aF$bT=Li+DmRaK*mwMvW+6$&Cav8g zyg}lHec7A-JdHHj=t2x{5J?;y?yBQM(#w=*Cktj^mZQ2bxZdG3i7iLm0Y*3apiNS znE5@v#+6~u&A@JRZG4Rkb^f7(_C@%$r0%m{bm`OSimE65%G|%{zD@O=NnB&bW^Lb}?D=Naa?JkK+dN#Z=u zFwf^p!A}ua{x^eV#BTu?fL{g|g5Lm{=91qB$;+JCyBPc_@g?BT9R32K z5N{FK-wn}kCMno^@)FVup!1yGrC?rgCUJ%K?4R@Z?m~qgJd;vr%?PtRoh98JDuT4S z(c(+{%Em)Opb=0J)EDYvcx~N#tJZ^H%536xjhRTTJ)4m>NrHs6 z_y!kg-JWE)ncw45-~S|W%~9)8zEfEpp&qHcH#PTb$3$w)`4BoANLTob%X>YjYcYM< z)jTx9K08-0JnnGpyX@8`-sejlKuI27VuW3;Y4tx+%{L z!0`Ja#m^x9F;MS+RL*elhr~yKKLV$M_#!zC{0XS{JU<2Xeup{1yw~|Tcs21o;0@p} z!FAxTK)uKLHMj}UjbF#{Vx73@Hgny?l2A>9h3;xdW zUjX+Le+hgJEY9Km8aNYt0h|TC1kMIk{yb3mu5frIc!2a9z(0a(!B@dgfXrRVTfo=A zdqC;k2fj}He((+O+u&cpA3FYz!M_pz3HWz#FL)4q4tx`Q0elN=){N&(U`y~_umgAq z>;`gKN%jE~U;)?!9002PAz%*ip&)Z-a+t%>j$h>Blfe_(QAU7(>FM-;Nv=z()?+2N)lV1a6$97Qm{inm9f*nc!4cH0P z`Msxs&w^dS=fKm!SHOJm4X`^nJeU2Z;MpMWH{7OTm7m zYc1Iyyb0vJNOB!G2>cZIF#cT+O7AP+P}1)Ohk-gjcsTeC$A1VMLHuVf{uDTp_)8$~ zqLVMX^aBq6=E{@w9)wxH6Rp1}GG%mItQmZ0o93DjO6tzpN3TAM4q zD=0mkX{>lZa6Iw;4z+$&`fwL70*m2~1t);h9DgP_k@)2xb6RpfsC%LHn)ypu@lN`D+Q9_mL;&NF!w zP)Dmf%EbFnW`DlE@Mr%Q@;^;I zK7IH?qE9dpDu&L621BDw{x)CLXCNt4jQ$`jwzd_SxP3P~i|=)8J%)1KFJzTdNO^_q z1(w~CKSy7;xFB0(SK2R%i>?o)P2}$~sK{tpA7AYF<7lJyP&dO%uQ_bI9llg(M;*g! z5T)7v(}B{@Qj~ist#h=t>1E;_LH~Z}3`$ebnyeRoQAo@8Gx3hJq2#@+5_M*~g6s>I zA6^5gmF>Mm0y!JF{J5USxM7~rEy($x?gKSWTCnzNfkzd%ZkXqJS|cdrLse}MhHitl zLc5@S&>K*zwvsv?yax^uI<*+^mrc5Ma6P>bAIX(>L&8K8i;vSNiuXrO_$G6&kaxre zzt`MiM;c4%54W^f#==$cKeEUau41VzoZ#|%>?9kXXy))~ zm;WQqo|_#OI{r&8eygju{T(v=>+)OX%Dv8&x5~*s(_B4ocje7=@|CWf$6S85x^muhe*B)Z^IDg0YiH*(E}s@IpQoMN zQ_jzouG}v;KaX|te|6>bclCJ3$v^Jo`Zzf~)Kt9_&b~6ISLp1!+1WSG$#r!0t#bLb zb>&ZR`9ACF|E9xdUHLs-eOR}c@@{qIRJd{;bLHIX%9-rSx!aXD}t$ ztg{UNaTmYN#q~l`Jpnes2JTym^nkHVQBIK0!`dP}+$6L}^Yy?HegL0!F+3l-^9N$) zy2*aPBF+0nj+xHCjd>4|(x;v#WaQr_mA~T}pjeZ?B2vrOA2t+;;p`9Dhy7k6mhYL_ z(AK>Hm%L0RGfc34_Zsp=n;@A|&4r@VL4 zZh^KzJE6VM^U$l%+YtA)x);^`qwf98Z#;3&WA;;NuhkN$9!f&%pxdC$&;!tZNP7=+ zFq%Ds`W-U!TWUohd&RW(O8cR-$4C2X%)96f;3i1VV75cMpeLbyke=xs>9)u7+bQS0@CMnHPDQ35T2>Y*gG z4!RB63_SqtfObQBp#9JRs14&$@ArB`dVZj11XH28P&u?5S`Dp*w5HNMzV7FDL9a45 z=$^GXb9^3@59!`>1XK*ofOPMegl>a2Lk~c^p*_%k=m2yO%Av<;$m_iKB4{c!7ka;; zYjC1j5z~BFHh%iFv*u5zuCHA+e@0EE-rMtwfrd}jgC<#i0H%G=CnPg)*gyqtFFQHi z&SY+N(?62pbu7>6l<{1|>FhE(_6JM6tW9&W3(~UcZ%y2fn|N89=42P@d9SUI+V+9;pC-Y?%Brf=PZW@st*_-dor<*1T%6S;2>-yupp=f~Ih{pm9rdY9 zogpb5%d`}rXl%y? ze{ZO1r8#m8Nkeh6AJViVm{I?4dBV?Sc3yDv#h~O!+!_&+Ay8(;1pBV<&5LTgLb$WwmK}o9^U!^yO{Z#JxjW zK3xZ!?&ODSUy8j(Xv1)mpy3Zsc)Kj$>5WL&Wh#p@SNHl_j##~b#hBNzJf{=7Hfq0X zSSMV6%X2zh8~wb$C#6$dvtohW>*vd{Jg2iDt+S5%C0mZwk)yqi~VZ-N}cpm9tOIkk9BdmS=Sa zajo=atUe{AGc0{=wdqct>!h#ykh~1}j4~|G>4e6L&Ye!xqi|Ttzc$^;4^Een*FHl& zqYTS)I-&70>Qt{Y2WL@8s2wfO=`db=UEYZ3s370=mgjWH&FkcM43|Trgmf&==`e1* z&fbVlxE#xKI-xO>b6U6@8YWbZKXR1N-uBIoqTAlCleeSGs3t8o4*HX)b@-(97zcAu4OroGfkU z{i^$grrZ})msM3>P1|_C+H@yRx*r?5S7;iT9{@_-NO&E~b2_1UPQR(wG>E^Cuso|X zBy>-p->}QnNzZkb=X64IoqjVeQzv~NVR=r6S-{svztPq-$o%m(UmweJI)l^x4OmM! zAEf+c)0`}$-?v41jI2#_vdnK@R`_?`lQ!wP{Y4`OA-c?Wt>enDvF_J3acpFQb@sNwf@`=432eg`0%j^#O>&^)fQ_sv+0T<2}NlQ;9b zy*DUg9nmyqU0~Cle5haR%?sJYk#d; zZ)6*;0c^UHr*C`tgIVNlx|64Wd-;vsWlO^~fK7Mup}C1m6weXt$7H@uZJL!G7^>&n zO(bhS!11y+&B=!9S;{pw5x5U<eG5?&L%D>|ow0HMHBNJG<%YALgKEJXf_S)azEg ztNCqi@$|bwna*9Ir zh%d*i_iBT2!-owXKJM)CsQ3SN!C!pk{eE|wtNm9zr8AW^2;%8`FufOu2Jy6yhN-(j z5N|y^wX1^SKIree;Hg~|qCEXBD$Qm>l(!4s`B``;(&pLn>;dmvS@eDbuTK_U4r`h* zS$N~&EzQEaA0C$z6QXr^44&&g4eEXXp6%-m{n8AlaCwc*Q{Ov}%EHsSYIGLfL+~bK z;k^ZKTo&HBtg~1Om=LYs40zlw$MTlK%Vy`752N?}!|1(x7`=|H@v@aS2VOQimmfw? z-+E`$`_Wrq>l-HajmpjNXdF=xP5?w(_1hj9zmlMfG%ce6~Dwu6t#cJayi=pO>O}<}z9Pc`3@%+1-9# zit_Z^=KZpir*o(~XW{7_X?H2&*-NI+bPn|CS@d*X^Uy3j{f_n2EIge_JTnVVX9W8> zD{70*?CqUJPv`7rHOAgdOCkKo1V@(&8DZbO0((dT+nQK zI@dFsp3czBrl&J7v+3!)$!vN$FEX2+&SuP}r?VBa>FFH9ky+YE=Lc%7qY&*AI;+pm zXHniM+()o4K2}fX8m+Lb26^fnpxs$`I={xxXVLPu!1ME2l=m<^Kc7W;I>W`!XJH=O zgo4(_{zPgm+`p%NcPjJJ>*GU*nsUda8NCUl{~mJoUj{B7S>o2g%PK2sE2>MXDi>T; zS-GPBvN8^6s4a18<}{kMb4gw8!V{&)TkVk1}glTjlcZpWo$( zK6kB4T@6T(^K57pv`odE*N(%HdSF9AVPeD!`4`F6H;ro9@s{3QrhK>T_OXxc@3n?+ zYJve%E3229^GOEiD3ZEh0NrfB5_A8wpo&9VY=Sv%VE`R>xxImV-yOHk92-+oSyNJB z?$Z{S`!su^f%=6h_5&)#o~dx`N^w3=hPk+vI~oQ!(}_krK~s*ugKNg$e}W_=T^J-=4JJPuGhQCcymh?9*d0 z^(0mnA=`tRsgA24RiqZGgX$s8-B&_aK`S9SfLjGKH?#aT6;@^C1a7;`+K#2KahBGH zvXe)c=KfaeHvT>HbdZ!6IlZG!DX5 z=31(?60S2}2~7egL+3+NAg#ZqK^H*Np&5|YT^B;LpxMwI=pyK1NNsWnbSb3kMhP?z zx*VDhl|p6E0%##r4pl&lph{>7v>5s*q%~?4v<#|-YM|xN70~hU;q}1PRPCA%Z}pDX S?6?Pxd*HYSj(Z@B2mU|$E5bkk literal 0 HcmV?d00001 diff --git a/server/src/saviine_server.csproj b/server/src/saviine_server.csproj new file mode 100644 index 0000000..f5cb490 --- /dev/null +++ b/server/src/saviine_server.csproj @@ -0,0 +1,94 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {84FB17D5-D67B-4B9E-9041-00424036BF2E} + Exe + Properties + saviine_server + saviine_server + v4.0 + Client + 512 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + x86 + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + False + Microsoft .NET Framework 4 Client Profile %28x86 und x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 4.5 + true + + + + + \ No newline at end of file diff --git a/server/src/saviine_server.csproj.user b/server/src/saviine_server.csproj.user new file mode 100644 index 0000000..862620d --- /dev/null +++ b/server/src/saviine_server.csproj.user @@ -0,0 +1,13 @@ + + + + publish\ + + + + + + de-DE + false + + \ No newline at end of file diff --git a/server/src/saviine_server.sln b/server/src/saviine_server.sln new file mode 100644 index 0000000..a495a42 --- /dev/null +++ b/server/src/saviine_server.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Express 2013 for Windows Desktop +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "saviine_server", "saviine_server.csproj", "{84FB17D5-D67B-4B9E-9041-00424036BF2E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {84FB17D5-D67B-4B9E-9041-00424036BF2E}.Debug|x86.ActiveCfg = Debug|x86 + {84FB17D5-D67B-4B9E-9041-00424036BF2E}.Debug|x86.Build.0 = Debug|x86 + {84FB17D5-D67B-4B9E-9041-00424036BF2E}.Release|x86.ActiveCfg = Release|x86 + {84FB17D5-D67B-4B9E-9041-00424036BF2E}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/server/src/saviine_server.suo b/server/src/saviine_server.suo new file mode 100644 index 0000000000000000000000000000000000000000..26dbb3d22b9ea9f72fd77a1c9a50ff8f73ccbd16 GIT binary patch literal 2560 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 q%kr;!$N+1W8J6W=>0j%)e`lFix{}*