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