--- /dev/null
+/** @file\r
+ BrotliCompress Compress/Decompress tool (BrotliCompress)\r
+\r
+ Copyright (c) 2020, ByoSoft Corporation. All rights reserved.<BR>\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+/* Command line interface for Brotli library. */\r
+\r
+#include <errno.h>\r
+#include <fcntl.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <sys/stat.h>\r
+#include <sys/types.h>\r
+#include <time.h>\r
+\r
+#include "./brotli/c/common/constants.h"\r
+#include "./brotli/c/common/version.h"\r
+#include <brotli/decode.h>\r
+#include <brotli/encode.h>\r
+\r
+#if !defined(_WIN32)\r
+#include <unistd.h>\r
+#include <utime.h>\r
+#else\r
+#include <io.h>\r
+#include <share.h>\r
+#include <sys/utime.h>\r
+\r
+#if !defined(__MINGW32__)\r
+#define STDIN_FILENO _fileno(stdin)\r
+#define STDOUT_FILENO _fileno(stdout)\r
+#define S_IRUSR S_IREAD\r
+#define S_IWUSR S_IWRITE\r
+#endif\r
+\r
+#define fopen ms_fopen\r
+#define open ms_open\r
+\r
+#if defined(_MSC_VER) && (_MSC_VER >= 1400)\r
+#define fseek _fseeki64\r
+#define ftell _ftelli64\r
+#endif\r
+\r
+static FILE* ms_fopen(const char* FileName, const char* Mode) {\r
+ FILE* Result;\r
+ Result = NULL;\r
+ fopen_s(&Result, FileName, Mode);\r
+ return Result;\r
+}\r
+\r
+static int ms_open(const char* FileName, int Oflag, int Pmode) {\r
+ int Result;\r
+ Result = -1;\r
+ _sopen_s(&Result, FileName, Oflag | O_BINARY, _SH_DENYNO, Pmode);\r
+ return Result;\r
+}\r
+#endif /* WIN32 */\r
+\r
+\r
+#ifndef _MAX_PATH\r
+#define _MAX_PATH 500\r
+#endif\r
+\r
+#define DEFAULT_LGWIN 22\r
+#define DECODE_HEADER_SIZE 0x10\r
+#define GAP_MEM_BLOCK 0x1000\r
+size_t ScratchBufferSize = 0;\r
+static const size_t kFileBufferSize = 1 << 19;\r
+\r
+static void Version(void) {\r
+ int Major;\r
+ int Minor;\r
+ int Patch;\r
+ Major = BROTLI_VERSION >> 24;\r
+ Minor = (BROTLI_VERSION >> 12) & 0xFFF;\r
+ Patch = BROTLI_VERSION & 0xFFF;\r
+ printf("BrotliCompress %d.%d.%d\n", Major, Minor, Patch);\r
+}\r
+\r
+static void Usage() {\r
+ printf("Usage: %s [OPTION]... [FILE]...\n", __FILE__);\r
+ printf(\r
+"Options:\n"\r
+" -e, --compress compress\n"\r
+" -d, --decompress decompress\n"\r
+" -h, --help display this help and exit\n");\r
+ printf(\r
+" -o FILE, --output=FILE output file (only if 1 input file)\n");\r
+ printf(\r
+" -g NUM, --gap=NUM scratch memory gap level (1-16)\n");\r
+ printf(\r
+" -q NUM, --quality=NUM compression level (%d-%d)\n",\r
+ BROTLI_MIN_QUALITY, BROTLI_MAX_QUALITY);\r
+ printf(\r
+" -v, --version display version and exit\n");\r
+}\r
+\r
+static int64_t FileSize(const char* Path) {\r
+ FILE *FileHandle;\r
+ int64_t RetVal;\r
+ FileHandle = fopen(Path, "rb");\r
+\r
+ if (FileHandle == NULL) {\r
+ printf ("Failed to open file [%s]\n", Path);\r
+ return -1;\r
+ }\r
+ if (fseek(FileHandle, 0L, SEEK_END) != 0) {\r
+ printf ("Failed to seek file [%s]\n", Path);\r
+ fclose(FileHandle);\r
+ return -1;\r
+ }\r
+ RetVal = ftell(FileHandle);\r
+ if (fclose(FileHandle) != 0) {\r
+ printf ("Failed to close file [%s]\n", Path);\r
+ return -1;\r
+ }\r
+ return RetVal;\r
+}\r
+\r
+static BROTLI_BOOL HasMoreInput(FILE *FileHandle) {\r
+ return feof(FileHandle) ? BROTLI_FALSE : BROTLI_TRUE;\r
+}\r
+\r
+int OpenFiles(char *InputFile, FILE **InHandle, char *OutputFile, FILE **OutHandle) {\r
+ *InHandle = NULL;\r
+ *OutHandle = NULL;\r
+ *InHandle = fopen(InputFile, "rb");\r
+ if (*InHandle == NULL) {\r
+ printf("Failed to open input file [%s]\n", InputFile);\r
+ return BROTLI_FALSE;\r
+ }\r
+\r
+ *OutHandle = fopen(OutputFile, "wb+");\r
+ if (*OutHandle == NULL) {\r
+ printf("Failed to open output file [%s]\n", OutputFile);\r
+ fclose(*InHandle);\r
+ return BROTLI_FALSE;\r
+ }\r
+ return BROTLI_TRUE;\r
+}\r
+\r
+int CompressFile(char *InputFile, uint8_t *InputBuffer, char *OutputFile, uint8_t *OutputBuffer, int Quality, int Gap) {\r
+ int64_t InputFileSize;\r
+ FILE *InputFileHandle;\r
+ FILE *OutputFileHandle;\r
+ BrotliEncoderState *EncodeState;\r
+ uint32_t LgWin;\r
+ BROTLI_BOOL IsEof;\r
+ size_t AvailableIn;\r
+ const uint8_t *NextIn;\r
+ size_t AvailableOut;\r
+ uint8_t *NextOut;\r
+ uint8_t *Input;\r
+ uint8_t *Output;\r
+ size_t OutSize;\r
+ uint32_t SizeHint;\r
+ BROTLI_BOOL IsOk;\r
+ AvailableIn = 0;\r
+ IsEof = BROTLI_FALSE;\r
+ Input = InputBuffer;\r
+ Output = OutputBuffer;\r
+ IsOk = BROTLI_TRUE;\r
+ LgWin = DEFAULT_LGWIN;\r
+\r
+ InputFileSize = FileSize(InputFile);\r
+\r
+ IsOk = OpenFiles(InputFile, &InputFileHandle, OutputFile, &OutputFileHandle);\r
+ if (!IsOk) {\r
+ return IsOk;\r
+ }\r
+\r
+ fseek (OutputFileHandle, DECODE_HEADER_SIZE, SEEK_SET);\r
+\r
+ EncodeState = BrotliEncoderCreateInstance(NULL, NULL, NULL);\r
+ if (!EncodeState) {\r
+ printf("Out of memory\n");\r
+ IsOk = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ BrotliEncoderSetParameter(EncodeState, BROTLI_PARAM_QUALITY, (uint32_t)Quality);\r
+\r
+ if (InputFileSize >= 0) {\r
+ LgWin = BROTLI_MIN_WINDOW_BITS;\r
+ while (BROTLI_MAX_BACKWARD_LIMIT(LgWin) < InputFileSize) {\r
+ LgWin++;\r
+ if (LgWin == BROTLI_MAX_WINDOW_BITS) {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ BrotliEncoderSetParameter(EncodeState, BROTLI_PARAM_LGWIN, LgWin);\r
+ if (InputFileSize > 0) {\r
+ SizeHint = InputFileSize < (1 << 30)? (uint32_t)InputFileSize : (1u << 30);\r
+ BrotliEncoderSetParameter(EncodeState, BROTLI_PARAM_SIZE_HINT, SizeHint);\r
+ }\r
+\r
+ AvailableIn = 0;\r
+ NextIn = NULL;\r
+ AvailableOut = kFileBufferSize;\r
+ NextOut = Output;\r
+ for (;;) {\r
+ if (AvailableIn == 0 && !IsEof) {\r
+ AvailableIn = fread(Input, 1, kFileBufferSize, InputFileHandle);\r
+ NextIn = Input;\r
+ if (ferror(InputFileHandle)) {\r
+ printf("Failed to read input [%s]\n", InputFile);\r
+ IsOk = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ IsEof = !HasMoreInput(InputFileHandle);\r
+ }\r
+\r
+ if (!BrotliEncoderCompressStream(EncodeState,\r
+ IsEof ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS,\r
+ &AvailableIn, &NextIn, &AvailableOut, &NextOut, NULL)) {\r
+ printf("Failed to compress data [%s]\n", InputFile);\r
+ IsOk = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ if (AvailableOut == 0) {\r
+ OutSize = (size_t)(NextOut - Output);\r
+ if (OutSize > 0) {\r
+ fwrite(Output, 1, OutSize, OutputFileHandle);\r
+ if (ferror(OutputFileHandle)) {\r
+ printf("Failed to write output [%s]\n", OutputFile);\r
+ IsOk = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ }\r
+ AvailableOut = kFileBufferSize;\r
+ NextOut = Output;\r
+ }\r
+ if (BrotliEncoderIsFinished(EncodeState)) {\r
+ OutSize = (size_t)(NextOut - Output);\r
+ if (OutSize > 0) {\r
+ fwrite(Output, 1, OutSize, OutputFileHandle);\r
+ if (ferror(OutputFileHandle)) {\r
+ printf("Failed to write output [%s]\n", OutputFile);\r
+ IsOk = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ AvailableOut = 0;\r
+ }\r
+ }\r
+ if (IsEof) {\r
+ break;\r
+ }\r
+ }\r
+\r
+Finish:\r
+ if (EncodeState) {\r
+ BrotliEncoderDestroyInstance(EncodeState);\r
+ }\r
+ if (InputFileHandle) {\r
+ fclose(InputFileHandle);\r
+ }\r
+ if (OutputFileHandle) {\r
+ fclose(OutputFileHandle);\r
+ }\r
+ return IsOk;\r
+}\r
+\r
+/* Default BrotliAllocFunc */\r
+void* BrotliAllocFunc(void* Opaque, size_t Size) {\r
+ *(size_t *)Opaque = *(size_t *) Opaque + Size;\r
+ return malloc(Size);\r
+}\r
+\r
+/* Default BrotliFreeFunc */\r
+void BrotliFreeFunc(void* Opaque, void* Address) {\r
+ free(Address);\r
+}\r
+\r
+int DecompressFile(char *InputFile, uint8_t *InputBuffer, char *OutputFile, uint8_t *OutputBuffer, int Quality, int Gap) {\r
+ FILE *InputFileHandle;\r
+ FILE *OutputFileHandle;\r
+ BrotliDecoderState *DecoderState;\r
+ BrotliDecoderResult Result;\r
+ size_t AvailableIn;\r
+ const uint8_t *NextIn;\r
+ size_t AvailableOut;\r
+ uint8_t *NextOut;\r
+ uint8_t *Input;\r
+ uint8_t *Output;\r
+ size_t OutSize;\r
+ BROTLI_BOOL IsOk;\r
+ AvailableIn = 0;\r
+ Input = InputBuffer;\r
+ Output = OutputBuffer;\r
+ IsOk = BROTLI_TRUE;\r
+\r
+ IsOk = OpenFiles(InputFile, &InputFileHandle, OutputFile, &OutputFileHandle);\r
+ if (!IsOk) {\r
+ return IsOk;\r
+ }\r
+ fseek(InputFileHandle, DECODE_HEADER_SIZE, SEEK_SET);\r
+\r
+ DecoderState = BrotliDecoderCreateInstance(BrotliAllocFunc, BrotliFreeFunc, &ScratchBufferSize);\r
+ if (!DecoderState) {\r
+ printf("Out of memory\n");\r
+ IsOk = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ /* This allows decoding "large-window" streams. Though it creates\r
+ fragmentation (new builds decode streams that old builds don't),\r
+ it is better from used experience perspective. */\r
+ BrotliDecoderSetParameter(DecoderState, BROTLI_DECODER_PARAM_LARGE_WINDOW, 1u);\r
+\r
+ AvailableIn = 0;\r
+ NextIn = NULL;\r
+ AvailableOut = kFileBufferSize;\r
+ NextOut = Output;\r
+ Result = BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;\r
+ for (;;) {\r
+ if (Result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {\r
+ if (!HasMoreInput(InputFileHandle)) {\r
+ printf("Corrupt input [%s]\n", InputFile);\r
+ IsOk = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ AvailableIn = fread(Input, 1, kFileBufferSize, InputFileHandle);\r
+ NextIn = Input;\r
+ if (ferror(InputFileHandle)) {\r
+ printf("Failed to read input [%s]\n", InputFile);\r
+ IsOk = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ } else if (Result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {\r
+ OutSize = (size_t) (NextOut - Output);\r
+ if (OutSize > 0) {\r
+ fwrite(Output, 1, OutSize, OutputFileHandle);\r
+ if (ferror(OutputFileHandle)) {\r
+ printf("Failed to write output [%s]\n", OutputFile);\r
+ IsOk = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ }\r
+ AvailableOut = kFileBufferSize;\r
+ NextOut = Output;\r
+ } else if (Result == BROTLI_DECODER_RESULT_SUCCESS) {\r
+ OutSize = (size_t) (NextOut - Output);\r
+ if (OutSize > 0) {\r
+ fwrite(Output, 1, OutSize, OutputFileHandle);\r
+ if (ferror(OutputFileHandle)) {\r
+ printf("Failed to write output [%s]\n", OutputFile);\r
+ IsOk = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ }\r
+ AvailableOut = 0;\r
+ if (AvailableIn != 0 || HasMoreInput(InputFileHandle)) {\r
+ printf("Corrupt input [%s]\n", InputFile);\r
+ IsOk = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ } else {\r
+ printf("Corrupt input [%s]\n", InputFile);\r
+ IsOk = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ if (!HasMoreInput(InputFileHandle) && Result == BROTLI_DECODER_RESULT_SUCCESS ) {\r
+ break;\r
+ }\r
+ Result = BrotliDecoderDecompressStream(DecoderState, &AvailableIn, &NextIn, &AvailableOut, &NextOut, 0);\r
+ }\r
+Finish:\r
+ if (DecoderState) {\r
+ BrotliDecoderDestroyInstance(DecoderState);\r
+ }\r
+ if (InputFileHandle) {\r
+ fclose(InputFileHandle);\r
+ }\r
+ if (OutputFileHandle) {\r
+ fclose(OutputFileHandle);\r
+ }\r
+ return IsOk;\r
+}\r
+\r
+int main(int argc, char** argv) {\r
+ BROTLI_BOOL CompressBool;\r
+ BROTLI_BOOL DecompressBool;\r
+ char *OutputFile;\r
+ char *InputFile;\r
+ char OutputTmpFile[_MAX_PATH];\r
+ FILE *OutputHandle;\r
+ int Quality;\r
+ int Gap;\r
+ int OutputFileLength;\r
+ int InputFileLength;\r
+ int Ret;\r
+ size_t InputFileSize;\r
+ uint8_t *Buffer;\r
+ uint8_t *InputBuffer;\r
+ uint8_t *OutputBuffer;\r
+ int64_t Size;\r
+\r
+ InputFile = NULL;\r
+ OutputFile = NULL;\r
+ CompressBool = BROTLI_FALSE;\r
+ DecompressBool = BROTLI_FALSE;\r
+ //\r
+ //Set default Quality and Gap\r
+ //\r
+ Quality = 9;\r
+ Gap = 1;\r
+ InputFileSize = 0;\r
+ Ret = 0;\r
+\r
+ if (argc < 2) {\r
+ Usage();\r
+ return 1;\r
+ }\r
+ if (strcmp(argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0 ) {\r
+ Usage();\r
+ return 0;\r
+ }\r
+ if (strcmp(argv[1], "-v") == 0 || strcmp (argv[1], "--version") == 0 ) {\r
+ Version();\r
+ return 0;\r
+ }\r
+ while (argc > 1) {\r
+ if (strcmp(argv[1], "-e") == 0 || strcmp(argv[1], "--compress") == 0 ) {\r
+ CompressBool = BROTLI_TRUE;\r
+ if (DecompressBool) {\r
+ printf("Can't use -e/--compress with -d/--decompess on the same time\n");\r
+ return 1;\r
+ }\r
+ argc--;\r
+ argv++;\r
+ continue;\r
+ }\r
+ if (strcmp(argv[1], "-d") == 0 || strcmp(argv[1], "--decompress") == 0 ) {\r
+ DecompressBool = BROTLI_TRUE;\r
+ if (CompressBool) {\r
+ printf("Can't use -e/--compress with -d/--decompess on the same time\n");\r
+ return 1;\r
+ }\r
+ argc--;\r
+ argv++;\r
+ continue;\r
+ }\r
+ if (strcmp(argv[1], "-o") == 0 || strncmp(argv[1], "--output", 8) == 0) {\r
+ if (strcmp(argv[1], "-o") == 0) {\r
+ OutputFileLength = strlen(argv[2]);\r
+ if (OutputFileLength > _MAX_PATH) {\r
+ printf ("The file path %s is too long\n", argv[2]);\r
+ return 1;\r
+ }\r
+ OutputFile = argv[2];\r
+ if (OutputFile == NULL) {\r
+ fprintf(stderr, "Input file can't be null\n");\r
+ return 1;\r
+ }\r
+ argc--;\r
+ argv++;\r
+ } else {\r
+ OutputFileLength = strlen(argv[1] - 9);\r
+ OutputFile = (char *)argv[1] + 9;\r
+ }\r
+ argc--;\r
+ argv++;\r
+ continue;\r
+ }\r
+ if (strcmp(argv[1], "-q") == 0 || strncmp(argv[1], "--quality", 9) == 0) {\r
+ if (strcmp(argv[1], "-q") == 0) {\r
+ Quality = strtol(argv[2], NULL, 16);\r
+ argc--;\r
+ argv++;\r
+ } else {\r
+ Quality = strtol((char *)argv[1] + 10, NULL, 16);\r
+ }\r
+ argc--;\r
+ argv++;\r
+ continue;\r
+ }\r
+ if (strcmp(argv[1], "-g") == 0 || strncmp(argv[1], "--gap", 5) == 0) {\r
+ if (strcmp(argv[1], "-g") == 0) {\r
+ Gap = strtol(argv[2], NULL, 16);\r
+ argc--;\r
+ argv++;\r
+ } else {\r
+ Gap = strtol((char *)argv[1] + 6, NULL, 16);\r
+ }\r
+ argc--;\r
+ argv++;\r
+ continue;\r
+ }\r
+ if (argc > 1) {\r
+ InputFileLength = strlen(argv[1]);\r
+ if (InputFileLength > _MAX_PATH - 1) {\r
+ printf ("The file path %s is too long\n", argv[2]);\r
+ return 1;\r
+ }\r
+ InputFile = argv[1];\r
+ if (InputFile == NULL) {\r
+ printf("Input file can't be null\n");\r
+ return 1;\r
+ }\r
+ argc--;\r
+ argv++;\r
+ }\r
+ }\r
+\r
+ Buffer = (uint8_t*)malloc(kFileBufferSize * 2);\r
+ if (!Buffer) {\r
+ printf("Out of memory\n");\r
+ goto Finish;\r
+ }\r
+ memset(Buffer, 0, kFileBufferSize*2);\r
+ InputBuffer = Buffer;\r
+ OutputBuffer = Buffer + kFileBufferSize;\r
+ if (CompressBool) {\r
+ //\r
+ // Compress file\r
+ //\r
+ Ret = CompressFile(InputFile, InputBuffer, OutputFile, OutputBuffer, Quality, Gap);\r
+ if (!Ret) {\r
+ printf ("Failed to compress file [%s]\n", InputFile);\r
+ goto Finish;\r
+ }\r
+ //\r
+ // Decompress file for get Outputfile size\r
+ //\r
+ strcpy (OutputTmpFile, OutputFile);\r
+ if (strlen(InputFile) + strlen(".tmp") < _MAX_PATH) {\r
+ strcat(OutputTmpFile, ".tmp");\r
+ } else {\r
+ printf ("Output file path is too long[%s]\n", OutputFile);\r
+ Ret = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ memset(Buffer, 0, kFileBufferSize*2);\r
+ Ret = DecompressFile(OutputFile, InputBuffer, OutputTmpFile, OutputBuffer, Quality, Gap);\r
+ if (!Ret) {\r
+ printf ("Failed to decompress file [%s]\n", OutputFile);\r
+ goto Finish;\r
+ }\r
+ remove (OutputTmpFile);\r
+\r
+ //\r
+ // fill decoder header\r
+ //\r
+ InputFileSize = FileSize(InputFile);\r
+ Size = (int64_t)InputFileSize;\r
+ OutputHandle = fopen(OutputFile, "rb+"); /* open output_path file and add in head info */\r
+ fwrite(&Size, 1, sizeof(int64_t), OutputHandle);\r
+ ScratchBufferSize += Gap * GAP_MEM_BLOCK; /* there is a memory gap between IA32 and X64 environment*/\r
+ ScratchBufferSize += kFileBufferSize * 2;\r
+ Size = (int64_t) ScratchBufferSize;\r
+ fwrite(&Size, 1, sizeof(int64_t), OutputHandle);\r
+ if (fclose(OutputHandle) != 0) {\r
+ printf("Failed to close output file [%s]\n", OutputFile);\r
+ Ret = BROTLI_FALSE;\r
+ goto Finish;\r
+ }\r
+ } else {\r
+ Ret = DecompressFile(InputFile, InputBuffer, OutputFile, OutputBuffer, Quality, Gap);\r
+ if (!Ret) {\r
+ printf ("Failed to decompress file [%s]\n", InputFile);\r
+ goto Finish;\r
+ }\r
+ }\r
+ Finish:\r
+ if (Buffer != NULL) {\r
+ free (Buffer);\r
+ }\r
+ return !Ret;\r
+}\r