HLLib/HLExtract/Main.c

1568 lines
40 KiB
C

/*
* HLExtract
* Copyright (C) 2006-2010 Ryan Gregg
* This program 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 2
* of the License, or (at your option) any later version.
* This program 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.
*/
#ifdef _WIN32
# include "..\lib\HLLib.h"
# ifdef _MSC_VER
# ifdef _DEBUG
# ifdef _WIN64
# pragma comment(lib, "../HLLib/x64/Debug/HLLib.lib")
# else
# pragma comment(lib, "../HLLib/Win32/Debug/HLLib.lib")
# endif
# else
# ifdef _WIN64
# pragma comment(lib, "../HLLib/x64/Release/HLLib.lib")
# else
# pragma comment(lib, "../HLLib/Win32/Release/HLLib.lib")
# endif
# endif
# endif
#else
# include "../lib/HLLib.h"
#endif
#if _MSC_VER
# define _CRT_SECURE_NO_WARNINGS
# define _CRT_NONSTDC_NO_DEPRECATE
#endif
#include <assert.h>
#include <ctype.h>
#include <memory.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# define UNUSED
# include <windows.h>
#else
# include <unistd.h>
# include <linux/limits.h>
# define MAX_PATH PATH_MAX
# define UNUSED __attribute__((__unused__))
# define FOREGROUND_RED 0x0001
# define FOREGROUND_GREEN 0x0002
# define FOREGROUND_BLUE 0x0004
# define FOREGROUND_INTENSITY 0x0008
# define stricmp strcasecmp
# define strnicmp strncasecmp
#endif
hlUInt16 GetColor();
hlVoid SetColor(hlUInt16 uiColor);
hlVoid Print(hlUInt16 uiColor, const hlChar *lpFormat, ...);
hlVoid PrintUsage();
hlVoid List(FILE *pFile, HLDirectoryItem *pItem, hlBool bListFolders, hlBool bListFiles);
hlVoid ProgressStart();
hlVoid ProgressUpdate(hlULongLong uiBytesDone, hlULongLong uiBytesTotal);
hlVoid ExtractItemStartCallback(HLDirectoryItem *pItem);
hlVoid FileProgressCallback(HLDirectoryItem *pFile, hlUInt uiBytesExtracted, hlUInt uiBytesTotal, hlBool *pCancel);
hlVoid ExtractItemEndCallback(HLDirectoryItem *pItem, hlBool bSuccess);
hlVoid DefragmentProgressCallback(HLDirectoryItem *pFile, hlUInt uiFilesDefragmented, hlUInt uiFilesTotal, hlULongLong uiBytesDefragmented, hlULongLong uiBytesTotal, hlBool *pCancel);
HLValidation Validate(HLDirectoryItem *pItem);
hlVoid PrintAttribute(hlChar *lpPrefix, HLAttribute *pAttribute, hlChar *lpPostfix);
hlVoid PrintValidation(HLValidation eValidation);
hlVoid EnterConsole(hlUInt uiPackage, hlUInt uiConsoleCommands, hlChar *lpConsoleCommands[]);
#define MAX_ITEMS 1024
#define BUFFER_SIZE 1024
static hlChar lpDestination[MAX_PATH] = "";
static hlBool bSilent = hlFalse;
#ifndef _WIN32
static hlUInt uiProgressLast = 0;
static hlUInt16 uiCurrentColor = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
#endif
int main(hlInt argc, hlChar* argv[])
{
hlUInt i;
// Arguments.
hlUInt uiArgumentCount = (hlUInt)argc;
hlChar *lpPackage = 0;
hlUInt uiExtractItems = 0;
hlChar *lpExtractItems[MAX_ITEMS];
hlUInt uiValidateItems = 0;
hlChar *lpValidateItems[MAX_ITEMS];
hlChar *lpList = 0;
hlBool bDefragment = hlFalse;
hlChar *lpNCFRootPath = 0;
hlBool bList = hlFalse;
hlBool bListFolders = hlFalse;
hlBool bListFiles = hlFalse;
FILE *pFile = 0;
hlBool bConsoleMode = hlFalse;
hlUInt uiConsoleCommands = 0;
hlChar *lpConsoleCommands[MAX_ITEMS];
hlBool bFileMapping = hlFalse;
hlBool bQuickFileMapping = hlFalse;
hlBool bVolatileAccess = hlFalse;
hlBool bOverwriteFiles = hlTrue;
hlBool bForceDefragment = hlFalse;
// Package stuff.
HLPackageType ePackageType = HL_PACKAGE_NONE;
hlUInt uiPackage = HL_ID_INVALID, uiMode = HL_MODE_INVALID;
HLDirectoryItem *pItem = 0;
if(hlGetUnsignedInteger(HL_VERSION) < HL_VERSION_NUMBER)
{
printf("Wrong HLLib version: v%s.\n", hlGetString(HL_VERSION));
return 1;
}
// Process switches.
if(uiArgumentCount == 2)
{
// The user just specified a file, drop into console mode.
lpPackage = argv[1];
bConsoleMode = hlTrue;
bVolatileAccess = hlTrue;
}
else
{
for(i = 1; i < uiArgumentCount; i++)
{
if(stricmp(argv[i], "-p") == 0 || stricmp(argv[i], "--package") == 0)
{
if(lpPackage == 0 && i + 1 < uiArgumentCount)
{
lpPackage = argv[++i];
}
else
{
PrintUsage();
return 2;
}
}
else if(stricmp(argv[i], "-d") == 0 || stricmp(argv[i], "--dest") == 0)
{
if(*lpDestination == 0 && i + 1 < uiArgumentCount)
{
strcpy(lpDestination, argv[++i]);
}
else
{
PrintUsage();
return 2;
}
}
else if(stricmp(argv[i], "-e") == 0 || stricmp(argv[i], "--extract") == 0)
{
if(i + 1 < uiArgumentCount)
{
if(uiExtractItems == MAX_ITEMS)
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error loading package:\nMAX_ITEMS\n");
return 2;
}
lpExtractItems[uiExtractItems++] = argv[++i];
}
else
{
PrintUsage();
return 2;
}
}
else if(stricmp(argv[i], "-t") == 0 || stricmp(argv[i], "--validate") == 0)
{
if(i + 1 < uiArgumentCount)
{
if(uiValidateItems == MAX_ITEMS)
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error loading package:\nMAX_ITEMS\n");
return 2;
}
lpValidateItems[uiValidateItems++] = argv[++i];
}
else
{
PrintUsage();
return 2;
}
}
else if(strnicmp(argv[i], "-l", 2) == 0 || stricmp(argv[i], "--list") == 0)
{
if(bList)
{
PrintUsage();
return 2;
}
bList = hlTrue;
if(stricmp(argv[i], "-l") == 0 || stricmp(argv[i], "--list") == 0)
{
// By default list everything.
bListFolders = hlTrue;
bListFiles = hlTrue;
}
else
{
// List folders and files if specified.
bListFolders = strcspn(argv[i], "dD") != strlen(argv[i]);
bListFiles = strcspn(argv[i], "fF") != strlen(argv[i]);
}
// Check to see if we need to dump our list to a file.
if(i + 1 < uiArgumentCount && *argv[i + 1] != '-')
{
lpList = argv[++i];
}
}
else if(stricmp(argv[i], "-f") == 0 || stricmp(argv[i], "--defragment") == 0)
{
bDefragment = hlTrue;
}
else if(stricmp(argv[i], "-n") == 0 || stricmp(argv[i], "--ncfroot") == 0)
{
if(lpNCFRootPath == 0 && i + 1 < uiArgumentCount)
{
lpNCFRootPath = argv[++i];
}
else
{
PrintUsage();
return 2;
}
}
else if(stricmp(argv[i], "-s") == 0 || stricmp(argv[i], "--silent") == 0)
{
bSilent = hlTrue;
}
else if(stricmp(argv[i], "-c") == 0 || stricmp(argv[i], "--console") == 0)
{
bConsoleMode = hlTrue;
}
else if(stricmp(argv[i], "-x") == 0 || stricmp(argv[i], "--execute") == 0)
{
if(i + 1 < uiArgumentCount)
{
if(uiConsoleCommands == MAX_ITEMS)
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error loading package:\nMAX_ITEMS\n");
return 2;
}
lpConsoleCommands[uiConsoleCommands++] = argv[++i];
}
else
{
PrintUsage();
return 2;
}
}
else if(stricmp(argv[i], "-m") == 0 || stricmp(argv[i], "--filemapping") == 0)
{
bFileMapping = hlTrue;
}
else if(stricmp(argv[i], "-q") == 0 || stricmp(argv[i], "--quick-filemapping") == 0)
{
bFileMapping = hlTrue;
bQuickFileMapping = hlTrue;
}
else if(stricmp(argv[i], "-v") == 0 || stricmp(argv[i], "--volatile") == 0)
{
bVolatileAccess = hlTrue;
}
else if(stricmp(argv[i], "-o") == 0 || stricmp(argv[i], "--overwrite") == 0)
{
bOverwriteFiles = hlFalse;
}
else if(stricmp(argv[i], "-r") == 0 || stricmp(argv[i], "--force-defragment") == 0)
{
bDefragment = hlTrue;
bForceDefragment = hlTrue;
}
else
{
PrintUsage();
return 2;
}
}
}
// Make sure we have something to do.
if(lpPackage == 0 || (uiExtractItems == 0 && uiValidateItems == 0 && !bList && !bDefragment && !bConsoleMode))
{
PrintUsage();
return 2;
}
// If the destination directory is not specified, make it the input directory.
if(*lpDestination == 0)
{
const hlChar *pForward = strrchr(lpPackage, '\\');
const hlChar *pBackward = strrchr(lpPackage, '/');
const hlChar *pEnd = pForward > pBackward ? pForward : pBackward;
if(pEnd != 0)
{
strncpy(lpDestination, lpPackage, pEnd - lpPackage);
lpDestination[pEnd - lpPackage] = '\0';
}
}
hlInitialize();
hlSetBoolean(HL_OVERWRITE_FILES, bOverwriteFiles);
hlSetBoolean(HL_FORCE_DEFRAGMENT, bForceDefragment);
hlSetVoid(HL_PROC_EXTRACT_ITEM_START, ExtractItemStartCallback);
hlSetVoid(HL_PROC_EXTRACT_ITEM_END, ExtractItemEndCallback);
hlSetVoid(HL_PROC_EXTRACT_FILE_PROGRESS, FileProgressCallback);
hlSetVoid(HL_PROC_VALIDATE_FILE_PROGRESS, FileProgressCallback);
hlSetVoid(HL_PROC_DEFRAGMENT_PROGRESS_EX, DefragmentProgressCallback);
// Get the package type from the filename extension.
ePackageType = hlGetPackageTypeFromName(lpPackage);
// If the above fails, try getting the package type from the data at the start of the file.
if(ePackageType == HL_PACKAGE_NONE)
{
pFile = fopen(lpPackage, "rb");
if(pFile != 0)
{
hlByte lpBuffer[HL_DEFAULT_PACKAGE_TEST_BUFFER_SIZE];
hlUInt uiBufferSize = (hlUInt)fread(lpBuffer, 1, HL_DEFAULT_PACKAGE_TEST_BUFFER_SIZE, pFile);
ePackageType = hlGetPackageTypeFromMemory(lpBuffer, uiBufferSize);
fclose(pFile);
pFile = 0;
}
}
if(ePackageType == HL_PACKAGE_NONE)
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error loading %s:\nUnsupported package type.\n", lpPackage);
hlShutdown();
return 3;
}
// Create a package element, the element is allocated by the library and cleaned
// up by the library. An ID is generated which must be bound to apply operations
// to the package.
if(!hlCreatePackage(ePackageType, &uiPackage))
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error loading %s:\n%s\n", lpPackage, hlGetString(HL_ERROR_SHORT_FORMATED));
hlShutdown();
return 3;
}
hlBindPackage(uiPackage);
uiMode = HL_MODE_READ | (bDefragment ? HL_MODE_WRITE : 0);
uiMode |= !bFileMapping ? HL_MODE_NO_FILEMAPPING : 0;
uiMode |= bQuickFileMapping ? HL_MODE_QUICK_FILEMAPPING : 0;
uiMode |= bVolatileAccess ? HL_MODE_VOLATILE : 0;
// Open the package.
// Of the above modes, only HL_MODE_READ is required. HL_MODE_WRITE is present
// only for future use. File mapping is recommended as an efficient way to load
// packages. Quick file mapping maps the entire file (instead of bits as they are
// needed) and thus should only be used in Windows 2000 and up (older versions of
// Windows have poor virtual memory management which means large files won't be able
// to find a continues block and will fail to load). Volatile access allows HLLib
// to share files with other applications that have those file open for writing.
// This is useful for, say, loading .gcf files while Steam is running.
if(!hlPackageOpenFile(lpPackage, uiMode))
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error loading %s:\n%s\n", lpPackage, hlGetString(HL_ERROR_SHORT_FORMATED));
hlShutdown();
return 3;
}
// If we have a .ncf file, the package file data is stored externally. In order to
// validate the file data etc., HLLib needs to know where to look. Tell it where.
if(ePackageType == HL_PACKAGE_NCF)
{
hlNCFFileSetRootPath(lpNCFRootPath);
}
if(!bSilent)
Print(FOREGROUND_GREEN | FOREGROUND_INTENSITY, "%s opened.\n", lpPackage);
// Extract the requested items.
for(i = 0; i < uiExtractItems; i++)
{
// Find the item.
pItem = hlFolderGetItemByPath(hlPackageGetRoot(), lpExtractItems[i], HL_FIND_ALL);
if(pItem == 0)
{
printf("%s not found in package.\n", lpExtractItems[i]);
continue;
}
if(!bSilent)
{
Print(FOREGROUND_GREEN | FOREGROUND_INTENSITY, "Extracting %s...\n", hlItemGetName(pItem));
printf("\n");
}
// Extract the item.
// Item is extracted to cDestination\Item->GetName().
hlItemExtract(pItem, lpDestination);
if(!bSilent)
{
printf("\n");
printf("Done.\n");
}
}
// Validate the requested items.
for(i = 0; i < uiValidateItems; i++)
{
// Find the item.
pItem = hlFolderGetItemByPath(hlPackageGetRoot(), lpValidateItems[i], HL_FIND_ALL);
if(pItem == 0)
{
printf("%s not found in package.\n", lpValidateItems[i]);
continue;
}
if(!bSilent)
{
Print(FOREGROUND_GREEN | FOREGROUND_INTENSITY, "Validating %s...\n", hlItemGetName(pItem));
printf("\n");
}
// Validate the item.
Validate(pItem);
if(!bSilent)
{
printf("\n");
printf("Done.\n");
}
}
// List items in package.
if(bList)
{
if(!bSilent)
{
printf("Listing...\n");
printf("\n");
}
pFile = stdout;
if(lpList != 0)
{
pFile = fopen(lpList, "wt");
if(pFile == 0)
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error opening %s:\n%s\n", lpList, "fopen() failed.");
pFile = stdout;
}
}
List(pFile, hlPackageGetRoot(), bListFolders, bListFiles);
if(lpList != 0)
{
fclose(pFile);
pFile = 0;
}
if(!bSilent)
{
printf("\n");
printf("Done.\n");
}
}
if(bDefragment)
{
if(!bSilent)
{
printf("Defragmenting...\n");
printf("\n");
ProgressStart();
printf(" Progress: ");
}
if(!hlPackageDefragment())
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, " %s", hlGetString(HL_ERROR_SHORT_FORMATED));
}
if(!bSilent)
{
printf("\n");
printf("\n");
printf("Done.\n");
}
}
// Interactive console mode.
// Commands: dir, cd, root, info, extract, find, type, cls, help, exit.
if(bConsoleMode)
{
EnterConsole(uiPackage, uiConsoleCommands, lpConsoleCommands);
}
// Close the package.
hlPackageClose();
if(!bSilent)
Print(FOREGROUND_GREEN | FOREGROUND_INTENSITY, "%s closed.\n", lpPackage);
// Free up the allocated memory.
hlDeletePackage(uiPackage);
hlShutdown();
return 0;
}
hlUInt16 GetColor()
{
#ifdef _WIN32
HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (Handle != INVALID_HANDLE_VALUE)
{
CONSOLE_SCREEN_BUFFER_INFO Info;
if(GetConsoleScreenBufferInfo(Handle, &Info))
{
return Info.wAttributes;
}
}
return FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
#else
return uiCurrentColor;
#endif
}
hlVoid SetColor(hlUInt16 uiColor)
{
#ifdef _WIN32
HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (Handle == INVALID_HANDLE_VALUE)
{
return;
}
SetConsoleTextAttribute(Handle, uiColor);
#else
hlUInt16 uiColorPart;
if (!isatty(fileno(stdout)))
{
// cowardly refuse to write colors into things that aren't terminals
return;
}
uiColorPart = (uiColor & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE));
if (uiColorPart == 0)
{
// cowardly refuse to switch to black
return;
}
// start with "reset" attribute
printf("\033[0");
// handle white as the default case
if (uiColorPart != (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE))
{
printf(";3%d", uiColorPart);
}
// activate intensity if wanted
if ((uiColor & FOREGROUND_INTENSITY) != 0)
{
printf(";1");
}
// finish
printf("m");
fflush(stdout);
// store for next time
uiCurrentColor = uiColor;
#endif
}
hlVoid Print(hlUInt16 uiColor, const hlChar *lpFormat, ...)
{
hlUInt16 uiDefaultColor;
va_list List;
uiDefaultColor = GetColor();
if(uiDefaultColor != uiColor)
{
SetColor(uiColor);
}
va_start(List, lpFormat);
vprintf(lpFormat, List);
va_end(List);
if(uiDefaultColor != uiColor)
{
SetColor(uiDefaultColor);
}
}
hlVoid PrintUsage()
{
printf("HLExtract using HLLib v%s\n", hlGetString(HL_VERSION));
printf("\n");
printf("Correct HLExtract usage:\n");
printf(" -p <filepath> (Package to load.)\n");
printf(" -d <path> (Destination extraction directory.)\n");
printf(" -e <itempath> (Item in package to extract.)\n");
printf(" -t <itempath> (Item in package to validate.)\n");
printf(" -l[d][f] [filepath] (List the contents of the package.)\n");
printf(" -f (Defragment package.)\n");
printf(" -c (Console mode.)\n");
printf(" -x <command> (Execute console command.)\n");
printf(" -s (Silent mode.)\n");
printf(" -m (Use file mapping.)\n");
printf(" -q (Use quick file mapping.)\n");
printf(" -v (Allow volatile access.)\n");
printf(" -o (Don't overwrite files.)\n");
printf(" -r (Force defragmenting on all files.)\n");
printf(" -n <path> (NCF file's root path.)\n");
printf("\n");
printf("Example HLExtract usage:\n");
#ifdef _WIN32
printf("HLExtract.exe -p \"C:\\half-life.gcf\" -d \"C:\\backup\" -e \"root\\valve\\models\" -e \"root\\valve\\config.cfg\"\n");
printf("HLExtract.exe -p \"C:\\half-life.gcf\" -c -m -v\n");
printf("HLExtract.exe -p \"C:\\half-life.gcf\" -lf \"C:\\half-life.txt\" -m -s\n");
printf("HLExtract.exe -p \"C:\\half-life.gcf\" -m -f\n");
#else
printf("hlextract -p ~/half-life.gcf -d ~/backup -e \"root/valve/models\" -e \"root/valve/config.cfg\"\n");
printf("hlextract -p ~/half-life.gcf -c -m -v\n");
printf("hlextract -p ~/half-life.gcf -lf ~/half-life.txt -m -s\n");
printf("hlextract -p ~/half-life.gcf -m -f\n");
#endif
}
hlVoid List(FILE *pFile, HLDirectoryItem *pItem, hlBool bListFolders, hlBool bListFiles)
{
hlUInt i, uiItemCount;
hlChar lpPath[512] = "";
switch(hlItemGetType(pItem))
{
case HL_ITEM_FOLDER:
if(bListFolders)
{
hlItemGetPath(pItem, lpPath, sizeof(lpPath));
fprintf(pFile, "%s\n", lpPath);
}
uiItemCount = hlFolderGetCount(pItem);
for(i = 0; i < uiItemCount; i++)
{
List(pFile, hlFolderGetItem(pItem, i), bListFolders, bListFiles);
}
break;
case HL_ITEM_FILE:
if(bListFiles)
{
hlItemGetPath(pItem, lpPath, sizeof(lpPath));
fprintf(pFile, "%s\n", lpPath);
}
break;
default:
break;
}
}
hlVoid ProgressStart()
{
#ifndef _WIN32
uiProgressLast = 0;
printf("0%%");
#endif
}
hlVoid ProgressUpdate(hlULongLong uiBytesDone, hlULongLong uiBytesTotal)
{
if(!bSilent)
{
#ifdef _WIN32
HANDLE Handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (Handle != INVALID_HANDLE_VALUE)
{
CONSOLE_SCREEN_BUFFER_INFO Info;
if(GetConsoleScreenBufferInfo(Handle, &Info))
{
if(uiBytesTotal == 0)
{
printf("100.0%%");
}
else
{
printf("%0.0f%%", (hlSingle)((hlDouble)uiBytesDone / (hlDouble)uiBytesTotal * 100.0));
}
SetConsoleCursorPosition(Handle, Info.dwCursorPosition);
}
}
#else
hlUInt uiProgress = uiBytesTotal == 0 ? 100 : (hlUInt)((hlUInt64)uiBytesDone * 100 / (hlUInt64)uiBytesTotal);
while(uiProgress >= uiProgressLast + 10)
{
uiProgressLast += 10;
if(uiProgressLast == 100)
{
printf("100%% ");
}
else if(uiProgressLast == 50)
{
printf("50%%");
}
else
{
printf(".");
}
}
fflush(stdout);
#endif
}
}
hlVoid ExtractItemStartCallback(HLDirectoryItem *pItem)
{
if(!bSilent)
{
if(hlItemGetType(pItem) == HL_ITEM_FILE)
{
printf(" Extracting %s: ", hlItemGetName(pItem));
ProgressStart();
}
else
{
Print(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, " Extracting %s:\n", hlItemGetName(pItem));
}
}
}
hlVoid FileProgressCallback(HLDirectoryItem *pFile UNUSED, hlUInt uiBytesExtracted, hlUInt uiBytesTotal, hlBool *pCancel UNUSED)
{
ProgressUpdate((hlULongLong)uiBytesExtracted, (hlULongLong)uiBytesTotal);
}
hlVoid ExtractItemEndCallback(HLDirectoryItem *pItem, hlBool bSuccess)
{
hlUInt uiSize = 0;
hlChar lpPath[512] = "";
if(bSuccess)
{
if(!bSilent)
{
hlItemGetSize(pItem, &uiSize);
if(hlItemGetType(pItem) == HL_ITEM_FILE)
{
Print(FOREGROUND_GREEN | FOREGROUND_INTENSITY, "OK");
printf(" (%u B)\n", uiSize);
}
else
{
Print(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, " Done %s: ", hlItemGetName(pItem));
Print(FOREGROUND_GREEN | FOREGROUND_INTENSITY, "OK");
Print(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, " (%u B)\n", uiSize);
}
}
}
else
{
if(!bSilent)
{
if(hlItemGetType(pItem) == HL_ITEM_FILE)
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Errored\n");
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, " %s\n", hlGetString(HL_ERROR_SHORT_FORMATED));
}
else
{
Print(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, " Done %s: ", hlItemGetName(pItem));
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Errored\n");
}
}
else
{
hlItemGetPath(pItem, lpPath, sizeof(lpPath));
if(hlItemGetType(pItem) == HL_ITEM_FILE)
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, " Error extracting %s:\n", lpPath);
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, " %s\n", hlGetString(HL_ERROR_SHORT_FORMATED));
}
else
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, " Error extracting %s.\n", lpPath);
}
}
}
}
hlVoid DefragmentProgressCallback(HLDirectoryItem *pFile UNUSED, hlUInt uiFilesDefragmented UNUSED, hlUInt uiFilesTotal UNUSED, hlULongLong uiBytesDefragmented, hlULongLong uiBytesTotal, hlBool *pCancel UNUSED)
{
ProgressUpdate(uiBytesDefragmented, uiBytesTotal);
}
HLValidation Validate(HLDirectoryItem *pItem)
{
hlUInt i, uiItemCount;
hlChar lpPath[512] = "";
HLValidation eValidation = HL_VALIDATES_OK, eTest;
switch(hlItemGetType(pItem))
{
case HL_ITEM_FOLDER:
if(!bSilent)
{
Print(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, " Validating %s:\n", hlItemGetName(pItem));
}
uiItemCount = hlFolderGetCount(pItem);
for(i = 0; i < uiItemCount; i++)
{
eTest = Validate(hlFolderGetItem(pItem, i));
if(eTest > eValidation)
{
eValidation = eTest;
}
}
if(!bSilent)
{
Print(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, " Done %s: ", hlItemGetName(pItem));
PrintValidation(eValidation);
printf("\n");
}
break;
case HL_ITEM_FILE:
if(!bSilent)
{
printf(" Validating %s: ", hlItemGetName(pItem));
ProgressStart();
}
eValidation = hlFileGetValidation(pItem);
if(bSilent)
{
switch(eValidation)
{
case HL_VALIDATES_INCOMPLETE:
case HL_VALIDATES_CORRUPT:
hlItemGetPath(pItem, lpPath, sizeof(lpPath));
printf(" Validating %s: ", lpPath);
PrintValidation(eValidation);
printf("\n");
break;
default:
break;
}
}
else
{
PrintValidation(eValidation);
printf(" \n");
}
break;
default:
break;
}
return eValidation;
}
hlVoid PrintAttribute(hlChar *lpPrefix, HLAttribute *pAttribute, hlChar *lpPostfix)
{
switch(pAttribute->eAttributeType)
{
case HL_ATTRIBUTE_BOOLEAN:
printf("%s%s: %s%s\n", lpPrefix, pAttribute->lpName, pAttribute->Value.Boolean.bValue ? "True" : "False", lpPostfix);
break;
case HL_ATTRIBUTE_INTEGER:
printf("%s%s: %i%s\n", lpPrefix, pAttribute->lpName, pAttribute->Value.Integer.iValue, lpPostfix);
break;
case HL_ATTRIBUTE_UNSIGNED_INTEGER:
if(pAttribute->Value.UnsignedInteger.bHexadecimal)
{
printf("%s%s: %#.8x%s\n", lpPrefix, pAttribute->lpName, pAttribute->Value.UnsignedInteger.uiValue, lpPostfix);
}
else
{
printf("%s%s: %u%s\n", lpPrefix, pAttribute->lpName, pAttribute->Value.UnsignedInteger.uiValue, lpPostfix);
}
break;
case HL_ATTRIBUTE_FLOAT:
printf("%s%s: %f%s\n", lpPrefix, pAttribute->lpName, pAttribute->Value.Float.fValue, lpPostfix);
break;
case HL_ATTRIBUTE_STRING:
printf("%s%s: %s%s\n", lpPrefix, pAttribute->lpName, pAttribute->Value.String.lpValue, lpPostfix);
break;
default:
break;
}
}
hlVoid PrintValidation(HLValidation eValidation)
{
switch(eValidation)
{
case HL_VALIDATES_ASSUMED_OK:
Print(FOREGROUND_GREEN | FOREGROUND_INTENSITY, "Assumed OK");
break;
case HL_VALIDATES_OK:
Print(FOREGROUND_GREEN | FOREGROUND_INTENSITY, "OK");
break;
case HL_VALIDATES_INCOMPLETE:
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Incomplete");
break;
case HL_VALIDATES_CORRUPT:
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Corrupt");
break;
case HL_VALIDATES_CANCELED:
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Canceled");
break;
case HL_VALIDATES_ERROR:
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error");
break;
default:
printf("Unknown");
break;
}
}
hlVoid EnterConsole(hlUInt uiPackage, hlUInt uiConsoleCommands, hlChar *lpConsoleCommands[])
{
hlUInt i;
hlChar lpBuffer[BUFFER_SIZE]; // Input string.
hlChar lpCommand[BUFFER_SIZE]; // Input command (i.e. first word in input string).
hlChar lpArgument[BUFFER_SIZE]; // Input argument (i.e. rest of input string).
hlChar *lpTemp;
hlChar lpTempBuffer[BUFFER_SIZE];
hlUInt16 uiColor;
HLDirectoryItem *pItem = 0, *pSubItem = 0;
hlBool bFound;
hlUInt uiItemCount, uiFolderCount, uiFileCount;
hlChar iChar;
HLStream *pStream = 0;
HLAttribute Attribute;
HLPackageType ePackageType = HL_PACKAGE_NONE;
hlUInt uiSubPackage = HL_ID_INVALID;
pItem = hlPackageGetRoot();
while(hlTrue)
{
uiColor = GetColor();
SetColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
if(uiConsoleCommands > 0)
{
printf("%s>%s\n", hlItemGetName(pItem), *lpConsoleCommands);
strncpy(lpBuffer, *lpConsoleCommands, sizeof(lpBuffer));
lpBuffer[sizeof(lpBuffer) - 1] = '\0';
uiConsoleCommands--;
lpConsoleCommands++;
}
else
{
// Command prompt.
printf("%s>", hlItemGetName(pItem));
// Get and parse line.
fgets(lpBuffer, sizeof(lpBuffer), stdin);
}
SetColor(uiColor);
i = (hlUInt)strlen(lpBuffer);
while(i > 0 && (lpBuffer[i - 1] == '\r' || lpBuffer[i - 1] == '\n'))
{
i--;
lpBuffer[i] = '\0';
}
*lpCommand = *lpArgument = 0;
strcpy(lpCommand, lpBuffer);
lpTemp = strchr(lpCommand, ' ');
if(lpTemp != 0)
{
strcpy(lpArgument, lpTemp + 1);
*lpTemp = 0;
}
// Cycle through commands.
//
// Directory listing.
// Good example of CDirectoryItem::GetType().
//
#ifdef _WIN32
if(stricmp(lpCommand, "dir") == 0)
#else
if(stricmp(lpCommand, "ls") == 0)
#endif
{
uiItemCount = hlFolderGetCount(pItem);
uiFolderCount = 0, uiFileCount = 0;
*lpTempBuffer = 0;
hlItemGetPath(pItem, lpTempBuffer, sizeof(lpTempBuffer));
printf("Directory of %s:\n", lpTempBuffer);
printf("\n");
if(*lpArgument == 0)
{
// List all items in the current folder.
for(i = 0; i < uiItemCount; i++)
{
pSubItem = hlFolderGetItem(pItem, i);
if(hlItemGetType(pSubItem) == HL_ITEM_FOLDER)
{
uiFolderCount++;
printf(" <%s>\n", hlItemGetName(pSubItem));
}
else if(hlItemGetType(pSubItem) == HL_ITEM_FILE)
{
uiFileCount++;
printf(" %s\n", hlItemGetName(pSubItem));
}
}
}
else
{
pSubItem = hlFolderFindFirst(pItem, lpArgument, HL_FIND_ALL | HL_FIND_NO_RECURSE);
while(pSubItem)
{
if(hlItemGetType(pSubItem) == HL_ITEM_FOLDER)
{
uiFolderCount++;
printf(" <%s>\n", hlItemGetName(pSubItem));
}
else if(hlItemGetType(pSubItem) == HL_ITEM_FILE)
{
uiFileCount++;
printf(" %s\n", hlItemGetName(pSubItem));
}
pSubItem = hlFolderFindNext(pItem, pSubItem, lpArgument, HL_FIND_ALL | HL_FIND_NO_RECURSE);
}
}
printf("\n");
// Could also have used hlFolderGetFolderCount() and
// hlFolderGetFileCount().
printf("Summary:\n");
printf("\n");
printf(" %u Folder%s.\n", uiFolderCount, uiFolderCount != 1 ? "s" : "");
printf(" %u File%s.\n", uiFileCount, uiFileCount != 1 ? "s" : "");
printf("\n");
}
//
// Change directory.
// Good example of CDirectoryFolder::GetParent() and item casting.
//
else if(stricmp(lpCommand, "cd") == 0)
{
if(*lpArgument == 0)
{
printf("No argument for command cd supplied.\n");
}
else
{
if(stricmp(lpArgument, ".") == 0)
{
}
else if(stricmp(lpArgument, "..") == 0)
{
if(hlItemGetParent(pItem) != 0)
{
pItem = hlItemGetParent(pItem);
}
else
{
printf("Folder does not have a parent.\n");
}
}
else
{
bFound = hlFalse;
uiItemCount = hlFolderGetCount(pItem);
for(i = 0; i < uiItemCount; i++)
{
pSubItem = hlFolderGetItem(pItem, i);
if(hlItemGetType(pSubItem) == HL_ITEM_FOLDER && stricmp(lpArgument, hlItemGetName(pSubItem)) == 0)
{
bFound = hlTrue;
pItem = pSubItem;
break;
}
}
if(!bFound)
{
printf("%s not found.\n", lpArgument);
}
}
}
}
//
// Go to the root folder.
//
else if(stricmp(lpCommand, "root") == 0)
{
pItem = hlPackageGetRoot();
}
//
// Item information.
// Good example of CPackageUtility helper functions.
//
else if(stricmp(lpCommand, "info") == 0)
{
if(*lpArgument == 0)
{
printf("No argument for command info supplied.\n");
}
else
{
pSubItem = hlFolderGetItemByPath(pItem, lpArgument, HL_FIND_ALL);
if(pSubItem != 0)
{
*lpTempBuffer = 0;
hlItemGetPath(pSubItem, lpTempBuffer, sizeof(lpTempBuffer));
printf("Information for %s:\n", lpTempBuffer);
printf("\n");
switch(hlItemGetType(pSubItem))
{
case HL_ITEM_FOLDER:
printf(" Type: Folder\n");
#ifdef _WIN32
printf(" Size: %I64u B\n", hlFolderGetSizeEx(pSubItem, hlTrue));
printf(" Size On Disk: %I64u B\n", hlFolderGetSizeOnDiskEx(pSubItem, hlTrue));
#else
printf(" Size: %llu B\n", hlFolderGetSizeEx(pSubItem, hlTrue));
printf(" Size On Disk: %llu B\n", hlFolderGetSizeOnDiskEx(pSubItem, hlTrue));
#endif
printf(" Folders: %u\n", hlFolderGetFolderCount(pSubItem, hlTrue));
printf(" Files: %u\n", hlFolderGetFileCount(pSubItem, hlTrue));
break;
case HL_ITEM_FILE:
printf(" Type: File\n");
printf(" Extractable: %s\n", hlFileGetExtractable(pSubItem) ? "True" : "False");
//printf(" Validates: %s\n", hlFileGetValidates(pSubItem) ? "True" : "False");
printf(" Size: %u B\n", hlFileGetSize(pSubItem));
printf(" Size On Disk: %u B\n", hlFileGetSizeOnDisk(pSubItem));
break;
default:
break;
}
uiItemCount = hlPackageGetItemAttributeCount();
for(i = 0; i < uiItemCount; i++)
{
if(hlPackageGetItemAttribute(pSubItem, i, &Attribute))
{
PrintAttribute(" ", &Attribute, "");
}
}
printf("\n");
}
else
{
printf("%s not found.\n", lpArgument);
}
}
}
//
// Extract item.
// Good example of CPackageUtility extract functions.
//
else if(stricmp(lpCommand, "extract") == 0)
{
if(*lpArgument == 0)
{
printf("No argument for command extract supplied.\n");
}
else
{
if(stricmp(lpArgument, ".") == 0)
{
pSubItem = pItem;
}
else
{
pSubItem = hlFolderGetItemByName(pItem, lpArgument, HL_FIND_ALL);
}
if(pSubItem)
{
// Extract the item.
// Item is extracted to cDestination\Item->GetName().
if(!bSilent)
{
printf("Extracting %s...\n", hlItemGetName(pSubItem));
printf("\n");
}
hlItemExtract(pSubItem, lpDestination);
if(!bSilent)
{
printf("\n");
printf("Done.\n");
}
}
else
{
printf("%s not found.\n", lpArgument);
}
}
}
//
// Validate item.
// Validates the checksums of each item.
//
else if(stricmp(lpCommand, "validate") == 0)
{
if(*lpArgument == 0)
{
printf("No argument for command extract supplied.\n");
}
else
{
if(stricmp(lpArgument, ".") == 0)
{
pSubItem = pItem;
}
else
{
pSubItem = hlFolderGetItemByName(pItem, lpArgument, HL_FIND_ALL);
}
if(pSubItem)
{
if(!bSilent)
{
printf("Validating %s...\n", hlItemGetName(pSubItem));
printf("\n");
}
Validate(pSubItem);
if(!bSilent)
{
printf("\n");
printf("Done.\n");
}
}
else
{
printf("%s not found.\n", lpArgument);
}
}
}
//
// Find items.
// Good example of recursive directory navigation (Search() function).
//
else if(stricmp(lpCommand, "find") == 0)
{
if(*lpArgument == 0)
{
printf("No argument for command find supplied.\n");
}
else
{
// Search for the requested items.
if(!bSilent)
{
printf("Searching for %s...\n", lpArgument);
printf("\n");
}
uiItemCount = 0;
pSubItem = hlFolderFindFirst(pItem, lpArgument, HL_FIND_ALL);
while(pSubItem)
{
hlItemGetPath(pSubItem, lpTempBuffer, sizeof(lpTempBuffer));
// Print the path.
uiItemCount++;
Print(hlItemGetType(pSubItem) == HL_ITEM_FILE ? FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY : GetColor(), " Found %s: %s\n", hlItemGetType(pSubItem) == HL_ITEM_FOLDER ? "folder" : "file", lpTempBuffer);
pSubItem = hlFolderFindNext(pItem, pSubItem, lpArgument, HL_FIND_ALL);
}
if(!bSilent)
{
if(uiItemCount != 0)
{
printf("\n");
}
printf(" %u item%s found.\n", uiItemCount, uiItemCount != 1 ? "s" : "");
printf("\n");
}
}
}
//
// Type files.
// Good example of reading files into memory.
//
else if(stricmp(lpCommand, "type") == 0)
{
if(*lpArgument == 0)
{
printf("No argument for command type supplied.\n");
}
else
{
pSubItem = hlFolderGetItemByName(pItem, lpArgument, HL_FIND_FILES);
if(pSubItem)
{
*lpTempBuffer = 0;
hlItemGetPath(pSubItem, lpTempBuffer, sizeof(lpTempBuffer));
if(!bSilent)
{
printf("Type for %s:\n", lpTempBuffer);
printf("\n");
}
if(hlFileCreateStream(pSubItem, &pStream))
{
if(hlStreamOpen(pStream, HL_MODE_READ))
{
uiColor = GetColor();
SetColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
while(hlStreamReadChar(pStream, &iChar))
{
if((iChar >= ' ' && iChar <= '~') || iChar == '\n' || iChar == '\t')
{
putc(iChar, stdout);
}
}
SetColor(uiColor);
hlStreamClose(pStream);
}
else
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error typing %s:\n%s\n", hlItemGetName(pSubItem), hlGetString(HL_ERROR_SHORT_FORMATED));
}
hlFileReleaseStream(pSubItem, pStream);
pStream = 0;
}
else
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error typing %s:\n%s\n", hlItemGetName(pSubItem), hlGetString(HL_ERROR_SHORT_FORMATED));
}
if(!bSilent)
{
printf("\n");
printf("Done.\n");
}
}
else
{
printf("%s not found.\n", lpArgument);
}
}
}
//
// Open item.
// Good example of opening packages inside packages.
//
else if(stricmp(lpCommand, "open") == 0)
{
if(*lpArgument == 0)
{
printf("No argument for command open supplied.\n");
}
else
{
pSubItem = hlFolderGetItemByName(pItem, lpArgument, HL_FIND_FILES);
if(pSubItem)
{
if(hlFileCreateStream(pSubItem, &pStream))
{
if(hlStreamOpen(pStream, HL_MODE_READ))
{
ePackageType = hlGetPackageTypeFromStream(pStream);
if(hlCreatePackage(ePackageType, &uiSubPackage))
{
hlBindPackage(uiSubPackage);
if(hlPackageOpenStream(pStream, HL_MODE_READ))
{
if(!bSilent)
Print(FOREGROUND_GREEN | FOREGROUND_INTENSITY, "%s opened.\n", hlItemGetName(pSubItem));
EnterConsole(uiSubPackage, uiConsoleCommands, lpConsoleCommands);
hlPackageClose();
if(!bSilent)
Print(FOREGROUND_GREEN | FOREGROUND_INTENSITY, "%s closed.\n", hlItemGetName(pSubItem));
}
else
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error opening %s:\n%s\n", hlItemGetName(pSubItem), hlGetString(HL_ERROR_SHORT_FORMATED));
}
hlDeletePackage(uiSubPackage);
hlBindPackage(uiPackage);
}
else
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error opening %s:\n%s\n", hlItemGetName(pSubItem), hlGetString(HL_ERROR_SHORT_FORMATED));
}
hlStreamClose(pStream);
}
else
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error opening %s:\n%s\n", hlItemGetName(pSubItem), hlGetString(HL_ERROR_SHORT_FORMATED));
}
hlFileReleaseStream(pSubItem, pStream);
pStream = 0;
}
else
{
Print(FOREGROUND_RED | FOREGROUND_INTENSITY, "Error opening %s:\n%s\n", hlItemGetName(pSubItem), hlGetString(HL_ERROR_SHORT_FORMATED));
}
}
else
{
printf("%s not found.\n", lpArgument);
}
}
}
//
// Clear screen.
//
else if(stricmp(lpCommand, "status") == 0)
{
#ifdef _WIN32
printf("Total size: %I64u B\n", hlGetUnsignedLongLong(HL_PACKAGE_SIZE));
#else
printf("Total size: %llu B\n", hlGetUnsignedLongLong(HL_PACKAGE_SIZE));
#endif
printf("Total mapping allocations: %u\n", hlGetUnsignedInteger(HL_PACKAGE_TOTAL_ALLOCATIONS));
#ifdef _WIN32
printf("Total mapping memory allocated: %I64u B\n", hlGetUnsignedLongLong(HL_PACKAGE_TOTAL_MEMORY_ALLOCATED));
printf("Total mapping memory used: %I64u B\n", hlGetUnsignedLongLong(HL_PACKAGE_TOTAL_MEMORY_USED));
#else
printf("Total mapping memory allocated: %llu B\n", hlGetUnsignedLongLong(HL_PACKAGE_TOTAL_MEMORY_ALLOCATED));
printf("Total mapping memory used: %llu B\n", hlGetUnsignedLongLong(HL_PACKAGE_TOTAL_MEMORY_USED));
#endif
uiItemCount = hlPackageGetAttributeCount();
for(i = 0; i < uiItemCount; i++)
{
if(hlPackageGetAttribute(i, &Attribute))
{
PrintAttribute("", &Attribute, "");
}
}
}
#ifdef _WIN32
else if(stricmp(lpCommand, "cls") == 0)
{
system("cls");
}
#endif
else if(stricmp(lpCommand, "help") == 0)
{
printf("Valid commands:\n");
printf("\n");
#ifdef _WIN32
printf("dir <filter> (Directory list.)\n");
#else
printf("ls <filter> (Directory list.)\n");
#endif
printf("cd <folder> (Change directroy.)\n");
printf("info <item> (Item information.)\n");
printf("extract <item> (Extract item.)\n");
printf("validate <item> (Validate item.)\n");
printf("find <filter> (Find item.)\n");
printf("type <file> (Type a file.)\n");
printf("open <file> (Open a nested package.)\n");
printf("root (Go to the root folder.)\n");
printf("status (Package information.)\n");
#ifdef _WIN32
printf("cls (Clear the screen.)\n");
#endif
printf("help (Program help.)\n");
printf("exit (Quit program.)\n");
printf("\n");
}
else if(stricmp(lpCommand, "exit") == 0)
{
break;
}
else
{
printf("Unkown command: %s\n", lpCommand);
}
}
}