728 lines
21 KiB
C
728 lines
21 KiB
C
/* This file is part of REWise.
|
|
*
|
|
* REWise is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* REWise is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <linux/limits.h> // PATH_MAX, NAME_MAX
|
|
#include <stdlib.h>
|
|
#include <getopt.h>
|
|
#include <time.h>
|
|
#include <libgen.h> // dirname
|
|
#include <errno.h>
|
|
#include <sys/stat.h> // mkdir
|
|
#include <utime.h>
|
|
#include <sys/statvfs.h>
|
|
|
|
#include "print.h"
|
|
#include "reader.h"
|
|
#include "inflate.h"
|
|
#include "pefile.h"
|
|
#include "errors.h"
|
|
#include "wiseoverlay.h"
|
|
#include "wisescript.h"
|
|
#include "version.h"
|
|
|
|
|
|
#ifndef REWISE_DEFAULT_TMP_PATH
|
|
#define REWISE_DEFAULT_TMP_PATH "/tmp/"
|
|
#endif
|
|
|
|
|
|
#define SIZE_KiB 1024
|
|
#define SIZE_MiB 1048576 // 1024^2
|
|
#define SIZE_GiB 1073741824 // 1024^3
|
|
|
|
|
|
enum Operation {
|
|
OP_NONE = 0,
|
|
OP_EXTRACT = 1,
|
|
OP_EXTRACT_RAW = 2,
|
|
OP_LIST = 3,
|
|
OP_VERIFY = 4,
|
|
OP_HELP = 5,
|
|
OP_SCRIPT_DEBUG = 6
|
|
};
|
|
|
|
|
|
void printPrettySize(size_t size) {
|
|
if (size > SIZE_GiB) {
|
|
printf("%.2f GiB", (float)size / SIZE_GiB);
|
|
}
|
|
else
|
|
if (size > SIZE_MiB) {
|
|
printf("%.2f MiB", (float)size / SIZE_MiB);
|
|
}
|
|
else
|
|
if (size > SIZE_KiB) {
|
|
printf("%.2f KiB", (float)size / SIZE_KiB);
|
|
}
|
|
else {
|
|
printf("%zu bytes", size);
|
|
}
|
|
}
|
|
|
|
|
|
unsigned long getFreeDiskSpace(char * path) {
|
|
struct statvfs fsStats;
|
|
if (statvfs((const char *)path, &fsStats) != 0) {
|
|
printError("Failed to determine free disk space for '%s'. Errno: %s\n",
|
|
strerror(errno));
|
|
return 0;
|
|
}
|
|
return fsStats.f_bsize * fsStats.f_bavail;
|
|
}
|
|
|
|
|
|
void convertMsDosTime(struct tm * destTime, uint16_t date, uint16_t time) {
|
|
destTime->tm_year = (int)((date >> 9) + 80);
|
|
destTime->tm_mon = (int)((date >> 5) & 0b0000000000001111);
|
|
destTime->tm_mday = (int)(date & 0b0000000000011111);
|
|
destTime->tm_hour = (int)(time >> 11);
|
|
destTime->tm_min = (int)((time >> 5) & 0b0000000000111111);
|
|
destTime->tm_sec = (int)(time & 0b0000000000011111) * 2;
|
|
}
|
|
|
|
static InflateObject * InflateObjPtr;
|
|
static long ScriptDeflateOffset;
|
|
|
|
#define MAX_OUTPUT_PATH (PATH_MAX - WIN_PATH_MAX) - 2
|
|
static char OutputPath[MAX_OUTPUT_PATH]; // should be absolute and end with a '/'
|
|
static char TempPath[MAX_OUTPUT_PATH] = REWISE_DEFAULT_TMP_PATH;
|
|
static char PreserveTmp = 0;
|
|
static char NoExtract = 0;
|
|
|
|
|
|
void printHelp(void) {
|
|
printf("==============================================================\n");
|
|
printf(" Welcome to REWise version %s\n", REWISE_VERSION_STR);
|
|
printf("==============================================================\n\n");
|
|
printf(" Usage: rewise [OPERATION] [OPTIONS] INPUT_FILE\n\n");
|
|
printf(" OPERATIONS\n");
|
|
printf(" -x --extract OUTPUT_PATH Extract files.\n");
|
|
printf(" -r --raw OUTPUT_PATH Extract all files in the overlay "
|
|
"data. This does not move/rename "
|
|
"files!\n");
|
|
printf(" -l --list List files.\n");
|
|
printf(" -V --verify Run extract without actually "
|
|
"outputting files, crc32s will be "
|
|
"checked.\n");
|
|
printf(" -z --script-debug Print parsed WiseScript.bin\n");
|
|
printf(" -v --version Print version and exit.\n");
|
|
printf(" -h --help Display this HELP.\n");
|
|
printf("\n");
|
|
printf(" OPTIONS\n");
|
|
printf(" -p --preserve Don't delete TMP files.\n");
|
|
printf(" -t --tmp-path TMP_PATH Set temporary path, default: %s\n",
|
|
REWISE_DEFAULT_TMP_PATH);
|
|
printf(" -d --debug Print debug info.\n");
|
|
printf(" -s --silent Be silent, don't print anything.\n");
|
|
printf(" -n --no-extract Don't extract anything. This will "
|
|
"be ignored with -x or -r. It also "
|
|
"will not try to remove TMP files, "
|
|
"so -p won't do anything.\n");
|
|
printf("\n");
|
|
printf(" NOTES\n");
|
|
printf(" - Path to directory OUTPUT_PATH and TMP_PATH should exist and "
|
|
"be writable.\n");
|
|
}
|
|
|
|
|
|
void printFile(WiseScriptFileHeader * data) {
|
|
struct tm fileDatetime;
|
|
convertMsDosTime(&fileDatetime, data->date, data->time);
|
|
printf("% 12u %02d-%02d-%04d %02d:%02d:%02d '%s'\n", data->inflatedSize,
|
|
fileDatetime.tm_mday, fileDatetime.tm_mon, fileDatetime.tm_year + 1900,
|
|
fileDatetime.tm_hour, fileDatetime.tm_min, fileDatetime.tm_sec,
|
|
data->destFile);
|
|
}
|
|
|
|
|
|
/* preparePath() - Joins the two given paths to dest and tries to create the
|
|
* directories that don't exist yet.
|
|
* param subPath: Rest of the filepath (including file) from WiseScript.bin
|
|
* Should not be larger then (WIN_PATH_MAX + 1)
|
|
* param dest : A pre-allocated char buffer with size PATH_MAX */
|
|
bool preparePath(char * basePath, char * subPath, char * dest) {
|
|
// Join paths
|
|
if ((strlen(basePath) + strlen(subPath) + 1) > PATH_MAX) {
|
|
printError("Overflow of final path > PATH_MAX\n");
|
|
return false;
|
|
}
|
|
strcpy(dest, basePath);
|
|
strcat(dest, subPath);
|
|
|
|
// Try to create directories as needed
|
|
char * outputFilePath;
|
|
char * currentSubPath;
|
|
char * separator;
|
|
|
|
// make a copy which strchr may manipulate.
|
|
outputFilePath = strdup(dest);
|
|
|
|
if (outputFilePath == NULL) {
|
|
printError("Errno: %s\n", strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
// get the path without filename
|
|
currentSubPath = dirname(outputFilePath);
|
|
|
|
// get the path its root (string until first '/')
|
|
separator = strchr(currentSubPath, '/');
|
|
|
|
// This should not happen because the given path by the user should exist.
|
|
if (separator == NULL) {
|
|
printError("This should not happen, please report if it does! (1)\n");
|
|
return false;
|
|
}
|
|
|
|
// iterate through all sub-directories from root
|
|
while (separator != NULL) {
|
|
// terminate the dirName string on next occurance of '/'
|
|
separator[0] = 0x00;
|
|
|
|
// do not create root
|
|
if (currentSubPath[0] != 0x00) {
|
|
// stat currentSubPath
|
|
if (access(currentSubPath, F_OK) != 0) {
|
|
// currentSubPath exists but is not a directory
|
|
if (errno == ENOTDIR) {
|
|
printError("Extract subpath '%s' exists but is not a directory!\n",
|
|
currentSubPath);
|
|
free(outputFilePath);
|
|
return false;
|
|
}
|
|
|
|
// currentSubPath does not exist, try to create a new directory
|
|
if (errno == ENOENT) {
|
|
errno = 0;
|
|
if (mkdir(currentSubPath, 0777) != 0) {
|
|
printError("Failed to create subpath (1): '%s'\n", currentSubPath);
|
|
printError("Errno: %s\n", strerror(errno));
|
|
free(outputFilePath);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// reset the previous set terminator
|
|
separator[0] = '/';
|
|
|
|
// set separator to next occurrence of '/' (will be set to NULL when
|
|
// there are no more occurrences of '/'.
|
|
separator = strchr(separator + 1, '/');
|
|
}
|
|
|
|
// last subdir
|
|
if (access(currentSubPath, F_OK) != 0) {
|
|
// currentSubPath exists but is not a directory
|
|
if (errno == ENOTDIR) {
|
|
printError("Extract path '%s' exists but is not a directory!\n",
|
|
currentSubPath);
|
|
free(outputFilePath);
|
|
return false;
|
|
}
|
|
|
|
// currentSubPath does not exist, try to create a new directory
|
|
if (errno == ENOENT) {
|
|
if (mkdir(currentSubPath, 0777) != 0) {
|
|
printError("Failed to create subpath (2): '%s'\n", currentSubPath);
|
|
printError("Errno: %s\n", strerror(errno));
|
|
free(outputFilePath);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// cleanup
|
|
free(outputFilePath);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void extractFile(WiseScriptFileHeader * data) {
|
|
bool result;
|
|
char outputFilePath[PATH_MAX];
|
|
|
|
// Create the final absolute filepath and make sure the path exists (will be
|
|
// created when it doesn't exist).
|
|
if (preparePath(OutputPath, data->destFile, outputFilePath) == false) {
|
|
printError("preparePath failed.\n");
|
|
stopWiseScriptParse();
|
|
return;
|
|
}
|
|
|
|
// Seek to deflated file start
|
|
if (fseek(InflateObjPtr->inputFile, ((long)data->deflateStart) + ScriptDeflateOffset, SEEK_SET) != 0) {
|
|
printError("Failed seek to file offset 0x%08X\n", data->deflateStart);
|
|
printError("Errno: %s\n", strerror(errno));
|
|
stopWiseScriptParse();
|
|
return;
|
|
}
|
|
|
|
// Inflate/extract the file
|
|
result = inflateExtractNextFile(InflateObjPtr, outputFilePath);
|
|
if (result == false) {
|
|
printError("Failed to extract '%s'\n", outputFilePath);
|
|
stopWiseScriptParse();
|
|
return;
|
|
}
|
|
|
|
// Set file access/modification datetime
|
|
struct tm fileCreation;
|
|
time_t creationSeconds;
|
|
convertMsDosTime(&fileCreation, data->date, data->time);
|
|
creationSeconds = mktime(&fileCreation);
|
|
const struct utimbuf times = {
|
|
.actime = creationSeconds,
|
|
.modtime = creationSeconds
|
|
};
|
|
if (utime(outputFilePath, ×) != 0) {
|
|
printWarning("Failed to set access and modification datetime for file "
|
|
"'%s'\n", outputFilePath);
|
|
}
|
|
|
|
printInfo("Extracted %s\n", data->destFile);
|
|
}
|
|
|
|
|
|
void noExtractFile(WiseScriptFileHeader * data) {
|
|
// Inflate/extract the file
|
|
bool result = inflateExtractNextFile(InflateObjPtr, NULL);
|
|
if (result == false) {
|
|
printError("Failed to no-extract '%s'\n", data->destFile);
|
|
stopWiseScriptParse();
|
|
return;
|
|
}
|
|
printInfo("CRC32 success for '%s'\n", data->destFile);
|
|
}
|
|
|
|
|
|
bool setPath(const char * optarg, char * dest) {
|
|
// Resolve absolute path
|
|
char * outputPath = realpath(optarg, dest);
|
|
if (outputPath == NULL) {
|
|
printError("Invalid PATH given, could not resolve absolute path for "
|
|
"'%s'. Errno: %s\n", optarg, strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
size_t outputPathLen = strlen(outputPath);
|
|
// -2 for the potential '/' we may add
|
|
if (outputPathLen >= (MAX_OUTPUT_PATH - 1)) {
|
|
printError("Absolute path of PATH is to large.\n");
|
|
return false;
|
|
}
|
|
|
|
// Make sure the path ends with a '/'
|
|
if (dest[outputPathLen - 1] != '/') {
|
|
strcat(dest, "/");
|
|
}
|
|
|
|
// Make sure the path exists
|
|
if (access(dest, F_OK) != 0) {
|
|
// dest exists but is not a directory
|
|
if (errno == ENOTDIR) {
|
|
printError("'%s' is not a directory.\n", dest);
|
|
return false;
|
|
}
|
|
// NOTE: realpath would have failed when the directory does not exist.
|
|
// dest does not exist
|
|
/*if (errno == ENOENT) {
|
|
printError("'%s' does not exist.\n", dest);
|
|
return false;
|
|
}*/
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
char inputFile[PATH_MAX];
|
|
long overlayOffset;
|
|
FILE * fp;
|
|
REWError status;
|
|
enum Operation operation = OP_NONE;
|
|
inputFile[0] = 0x00;
|
|
|
|
// https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Options.html
|
|
// https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html
|
|
struct option long_options[] = {
|
|
// OPERATIONS
|
|
{"extract" , required_argument, NULL, 'x'},
|
|
{"raw" , required_argument, NULL, 'r'},
|
|
{"list" , no_argument , NULL, 'l'},
|
|
{"verify" , no_argument , NULL, 'V'},
|
|
{"script-debug", no_argument , NULL, 'z'},
|
|
{"version" , no_argument , NULL, 'v'},
|
|
{"help" , no_argument , NULL, 'h'},
|
|
// OPTIONS
|
|
{"temp" , required_argument, NULL, 't'},
|
|
{"debug" , no_argument , NULL, 'd'},
|
|
{"preserve" , no_argument , NULL, 'p'},
|
|
{"silent" , no_argument , NULL, 's'},
|
|
{"no-extract" , no_argument , NULL, 'n'},
|
|
{NULL , 0 , NULL, 0}
|
|
};
|
|
|
|
int option_index = 0;
|
|
for (;;) {
|
|
int opt = getopt_long(argc, argv, "x:r:t:lhdspznVv",
|
|
long_options, &option_index);
|
|
|
|
if (opt == -1) {
|
|
break;
|
|
}
|
|
|
|
switch (opt) {
|
|
// OPERATIONS
|
|
case 'x':
|
|
{
|
|
if (operation != OP_NONE) {
|
|
printError("More then one operation is set! Do set only one.\n");
|
|
return 1;
|
|
}
|
|
operation = OP_EXTRACT;
|
|
if (setPath(optarg, OutputPath) == false) {
|
|
return 1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'r':
|
|
if (operation != OP_NONE) {
|
|
printError("More then one operation is set! Do set only one.\n");
|
|
return 1;
|
|
}
|
|
operation = OP_EXTRACT_RAW;
|
|
if (setPath(optarg, OutputPath) == false) {
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case 'l':
|
|
if (operation != OP_NONE) {
|
|
printError("More then one operation is set! Do set only one.\n");
|
|
return 1;
|
|
}
|
|
operation = OP_LIST;
|
|
break;
|
|
|
|
case 'V':
|
|
if (operation != OP_NONE) {
|
|
printError("More then one operation is set! Do set only one.\n");
|
|
return 1;
|
|
}
|
|
operation = OP_VERIFY;
|
|
break;
|
|
|
|
case 'z':
|
|
if (operation != OP_NONE) {
|
|
printError("More then one operation is set! Do set only one.\n");
|
|
return 1;
|
|
}
|
|
operation = OP_SCRIPT_DEBUG;
|
|
break;
|
|
|
|
case 'v':
|
|
printf("REWise v%s\n", REWISE_VERSION_STR);
|
|
return 0;
|
|
|
|
case 'h':
|
|
printHelp();
|
|
return 0;
|
|
|
|
// OPTIONS
|
|
case 'd':
|
|
setPrintFlag(PRINT_DEBUG);
|
|
break;
|
|
|
|
case 's':
|
|
setPrintFlags(PRINT_SILENT);
|
|
break;
|
|
|
|
case 't':
|
|
if (setPath(optarg, TempPath) == false) {
|
|
printError("Invalid TMP_PATH given.\n");
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case 'p':
|
|
PreserveTmp = 1;
|
|
break;
|
|
|
|
case 'n':
|
|
NoExtract = 1;
|
|
break;
|
|
|
|
case '?':
|
|
// invalid option
|
|
printError("Invalid operation or option\n");
|
|
return 1;
|
|
|
|
default:
|
|
printError("default\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((argc - 1 ) < optind) {
|
|
printError("Please supply a input file\n");
|
|
return 1;
|
|
}
|
|
if ((argc - 1 ) > optind) {
|
|
printError("Please supply only one input file\n");
|
|
return 1;
|
|
}
|
|
|
|
if (strlen(argv[optind]) > (PATH_MAX - 1)) {
|
|
printError("What are you trying to do? INPUT_FILE is larger then PATH_MAX\n");
|
|
return 1;
|
|
}
|
|
strcpy(inputFile, argv[optind]);
|
|
|
|
if (operation == OP_NONE) {
|
|
printError("Please specify a operation.\n");
|
|
return 1;
|
|
}
|
|
|
|
/* Check if input file exists */
|
|
if (access(inputFile, F_OK) != 0) {
|
|
printError("InputFile '%s' not found. Errno: %s\n", inputFile,
|
|
strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
// Get offset to overlay data
|
|
overlayOffset = pefileGetOverlayOffset(inputFile);
|
|
if (overlayOffset == -1) {
|
|
printError("Failed to find overlay offset.\n", inputFile);
|
|
return 1;
|
|
}
|
|
|
|
printDebug("InputFile: %s\n", inputFile);
|
|
printDebug("OverlayOffset: %ld\n", overlayOffset);
|
|
|
|
/* Open inputFile */
|
|
fp = fopen(inputFile, "rb");
|
|
|
|
if (fp == NULL) {
|
|
printError("Failed to open inputFile '%s'\n", inputFile);
|
|
printError("Errno: %s\n", strerror(errno));
|
|
return 1;
|
|
};
|
|
|
|
// Seek to overlayData
|
|
if (fseek(fp, overlayOffset, SEEK_SET) != 0) {
|
|
printError("Failed to seek to overlayData. Offset: 0x%08X\n", overlayOffset);
|
|
printError("Errno: %s\n", strerror(errno));
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
|
|
// Read Wise overlay header
|
|
WiseOverlayHeader overlayHeader;
|
|
if ((status = readWiseOverlayHeader(fp, &overlayHeader)) != REWERR_OK) {
|
|
printError("Failed to read WiseOverlayHeader.\n");
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
freeWiseOverlayHeader(&overlayHeader);
|
|
|
|
// Here we arrived at the delated data, each entry followed by a CRC32
|
|
// https://en.wikipedia.org/wiki/DEFLATE
|
|
if (huffmanInitFixedTrees() == false) {
|
|
printError("Failed to huffmanInitFixedTrees, out of mem?\n");
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
|
|
// Initial check on free disk space (TMP_PATH)
|
|
unsigned long tmpFreeDiskSpace = getFreeDiskSpace(TempPath);
|
|
if (tmpFreeDiskSpace == 0) { // failed to determine free disk space
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
// make sure at-least 1 MiB is available at the TMP path
|
|
if (tmpFreeDiskSpace < SIZE_MiB) {
|
|
printError("At-least 1 MiB of free space is required in the TMP_PATH.\n");
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
|
|
bool result;
|
|
InflateObject inflateObj;
|
|
inflateInit(&inflateObj, fp);
|
|
InflateObjPtr = &inflateObj;
|
|
|
|
// Raw extract
|
|
if (operation == OP_EXTRACT_RAW) {
|
|
uint32_t extractCount = 0;
|
|
char extractFilePath[PATH_MAX];
|
|
|
|
// Start inflating and outputting files
|
|
while (ftell(fp) < inflateObj.inputFileSize) {
|
|
char fileName[21];
|
|
if (snprintf(fileName, 20, "EXTRACTED_%09u", extractCount) > 20) {
|
|
// truncated
|
|
printError("Failed to format filename, it truncated.\n");
|
|
fclose(fp);
|
|
huffmanFreeFixedTrees();
|
|
return 1;
|
|
}
|
|
if (preparePath(OutputPath, fileName, extractFilePath) == false) {
|
|
printError("Failed to create directories for '%s'.\n", fileName);
|
|
fclose(fp);
|
|
huffmanFreeFixedTrees();
|
|
return 1;
|
|
}
|
|
|
|
result = inflateExtractNextFile(&inflateObj, (const char *)extractFilePath);
|
|
if (result == false) {
|
|
printError("Failed to extract '%s'.\n", extractFilePath);
|
|
fclose(fp);
|
|
huffmanFreeFixedTrees();
|
|
return 1;
|
|
}
|
|
|
|
printInfo("Extracted '%s'\n", extractFilePath);
|
|
extractCount++;
|
|
}
|
|
|
|
printInfo("Extracted %d files.\n", extractCount);
|
|
}
|
|
|
|
else {
|
|
char tmpFileScript[PATH_MAX];
|
|
|
|
// Skip WiseColors.dib
|
|
if (NoExtract == 0) {
|
|
result = inflateExtractNextFile(&inflateObj, NULL);
|
|
if (result == false) {
|
|
printError("Failed to extract 'WiseColors.dib'.\n");
|
|
fclose(fp);
|
|
huffmanFreeFixedTrees();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Create filepath for WiseScript.bin
|
|
if (preparePath(TempPath, "WiseScript.bin", tmpFileScript) == false) {
|
|
fclose(fp);
|
|
huffmanFreeFixedTrees();
|
|
printf("Failed to create filepath for WiseScript.bin.\n");
|
|
return 1;
|
|
}
|
|
// Extract WiseScript.bin
|
|
if (NoExtract == 0) {
|
|
result = inflateExtractNextFile(&inflateObj, tmpFileScript);
|
|
if (result == false) {
|
|
printError("Failed to extract '%s'.\n", tmpFileScript);
|
|
fclose(fp);
|
|
huffmanFreeFixedTrees();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Determine the inflate data offset inside WiseScript.bin (this needs to
|
|
// be added to the inflateStart we got for files from WiseScript to get to
|
|
// the real inflateStart offset in the PE file.)
|
|
WiseScriptParsedInfo * parsedInfo = wiseScriptGetParsedInfo(tmpFileScript);
|
|
ScriptDeflateOffset = inflateObj.inputFileSize - parsedInfo->inflateStartOffset;
|
|
printDebug("scriptDeflateOffset: %ld (0x%08X).\n", parsedInfo->inflateStartOffset);
|
|
|
|
WiseScriptCallbacks callbacks;
|
|
initWiseScriptCallbacks(&callbacks);
|
|
|
|
// LIST
|
|
if (operation == OP_LIST) {
|
|
callbacks.cb_0x00 = &printFile;
|
|
printf(" FILESIZE FILEDATE FILETIME FILEPATH\n");
|
|
printf("------------ ---------- -------- ----------------------------\n");
|
|
status = parseWiseScript(tmpFileScript, &callbacks);
|
|
if (status != REWERR_OK) {
|
|
printError("Parsing WiseScript failed.\n");
|
|
}
|
|
printf("------------ ---------- -------- ----------------------------\n");
|
|
printf("Total size: ");
|
|
printPrettySize(parsedInfo->inflatedSize0x00);
|
|
printf(" (%zu bytes)\n", parsedInfo->inflatedSize0x00);
|
|
}
|
|
// EXTRACT
|
|
else
|
|
if (operation == OP_EXTRACT) {
|
|
// Check if there is enough free disk space
|
|
unsigned long outputFreeDiskSpace = getFreeDiskSpace(OutputPath);
|
|
if (outputFreeDiskSpace == 0) { // failed to determine free disk space
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
if (outputFreeDiskSpace <= parsedInfo->inflatedSize0x00) {
|
|
printError("Not enough free disk space at '%s'. Required: %ld Left: "
|
|
"%ld\n", OutputPath, parsedInfo->inflatedSize0x00,
|
|
outputFreeDiskSpace);
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
|
|
// Start inflating and outputting files
|
|
callbacks.cb_0x00 = &extractFile;
|
|
status = parseWiseScript(tmpFileScript, &callbacks);
|
|
|
|
// Something went wrong
|
|
if (status != REWERR_OK) {
|
|
printError("Parsing WiseScript failed.\n");
|
|
}
|
|
}
|
|
// SCRIPT_DEBUG
|
|
else
|
|
if (operation == OP_SCRIPT_DEBUG) {
|
|
status = wiseScriptDebugPrint(tmpFileScript);
|
|
if (status != REWERR_OK) {
|
|
printError("Debug print WiseScript failed.\n");
|
|
}
|
|
}
|
|
else
|
|
if (operation == OP_VERIFY) {
|
|
callbacks.cb_0x00 = &noExtractFile;
|
|
status = parseWiseScript(tmpFileScript, &callbacks);
|
|
if (status != REWERR_OK) {
|
|
printError("Parsing WiseScript failed.\n");
|
|
}
|
|
printInfo("All looks good!\n");
|
|
}
|
|
|
|
// remove tmp files
|
|
if (PreserveTmp == 0 && NoExtract == 0) {
|
|
if (remove(tmpFileScript) != 0) {
|
|
printError("Failed to remove '%s'. Errno: %s\n", tmpFileScript,
|
|
strerror(errno));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cleanup
|
|
huffmanFreeFixedTrees();
|
|
fclose(fp);
|
|
|
|
return status;
|
|
}
|