From 40a05f665d74b5745b867ea25e91bf9f1bc9d425 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Wed, 25 Feb 2026 21:14:19 +0100 Subject: [PATCH] Migrate from libmpq to mpqfs --- .gitmodules | 0 3rdParty/PKWare/CMakeLists.txt | 4 - 3rdParty/PKWare/Makefile | 59 -- 3rdParty/PKWare/PKWare.vcxproj | 100 ---- 3rdParty/PKWare/PKWare.vcxproj.filters | 30 - 3rdParty/PKWare/Pkware.dsp | 100 ---- 3rdParty/PKWare/explode.cpp | 522 ----------------- 3rdParty/PKWare/implode.cpp | 775 ------------------------- 3rdParty/PKWare/pkware.h | 142 ----- 3rdParty/libmpq/CMakeLists.txt | 38 -- 3rdParty/libmpq/config.h | 1 - 3rdParty/mpqfs/CMakeLists.txt | 9 + CMake/Dependencies.cmake | 16 +- CMake/platforms/n3ds.cmake | 1 - CMake/platforms/xbox_nxdk.cmake | 6 + Source/CMakeLists.txt | 4 +- Source/encrypt.cpp | 104 +--- Source/engine/assets.cpp | 14 +- Source/engine/assets.hpp | 11 +- Source/init.cpp | 6 +- Source/mpq/mpq_common.cpp | 6 +- Source/mpq/mpq_reader.cpp | 188 +++--- Source/mpq/mpq_reader.hpp | 70 +-- Source/mpq/mpq_sdl_rwops.cpp | 295 +--------- Source/mpq/mpq_sdl_rwops.hpp | 19 +- Source/mpq/mpq_writer.cpp | 14 +- test/writehero_test.cpp | 2 +- uwp-project/devilutionx.vcxproj | 8 +- 28 files changed, 228 insertions(+), 2316 deletions(-) create mode 100644 .gitmodules delete mode 100644 3rdParty/PKWare/CMakeLists.txt delete mode 100644 3rdParty/PKWare/Makefile delete mode 100644 3rdParty/PKWare/PKWare.vcxproj delete mode 100644 3rdParty/PKWare/PKWare.vcxproj.filters delete mode 100644 3rdParty/PKWare/Pkware.dsp delete mode 100644 3rdParty/PKWare/explode.cpp delete mode 100644 3rdParty/PKWare/implode.cpp delete mode 100644 3rdParty/PKWare/pkware.h delete mode 100644 3rdParty/libmpq/CMakeLists.txt delete mode 100644 3rdParty/libmpq/config.h create mode 100644 3rdParty/mpqfs/CMakeLists.txt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e69de29bb diff --git a/3rdParty/PKWare/CMakeLists.txt b/3rdParty/PKWare/CMakeLists.txt deleted file mode 100644 index 99f9783b2..000000000 --- a/3rdParty/PKWare/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_library(PKWare STATIC - explode.cpp - implode.cpp) -target_include_directories(PKWare PUBLIC .) diff --git a/3rdParty/PKWare/Makefile b/3rdParty/PKWare/Makefile deleted file mode 100644 index 377b29a23..000000000 --- a/3rdParty/PKWare/Makefile +++ /dev/null @@ -1,59 +0,0 @@ -VC5_DIR ?= $(HOME)/DevStudio_5.10/VC - -# The $(VS6_DIR) directory is a copy of the "Microsoft Visual Studio" directory. -# -# To get a working setup on Linux or other "portable" copies of VS, -# the following DLLs have to be copied to the -# $(VS6_DIR)/VC98/Bin directory. -# -# - $(VS6_DIR)/Common/MSDev98/Bin/MSPDB60.DLL -# -# And to the $(VC5_DIR)/bin directory. -# -# - $(VC5_DIR)/SharedIDE/bin/MSDIS100.DLL -# - $(VC5_DIR)/SharedIDE/bin/MSPDB50.DLL -VS6_DIR ?= $(HOME)/VS6 - -VC6_DIR = $(VS6_DIR)/VC98 - -VC6_BIN_DIR = $(VC6_DIR)/Bin -VC6_INC_DIR = $(VC6_DIR)/Include -VC6_LIB_DIR = $(VC6_DIR)/Lib - -VC5_LIB_DIR = $(VC5_DIR)/lib - -IDE_DIR ?= $(VS6_DIR)/Common/MSDev98 -IDE_BIN_DIR = $(IDE_DIR)/bin -ifeq ($(OS),Windows_NT) - CL = $(VC6_BIN_DIR)/CL.EXE - RC = $(IDE_BIN_DIR)/RC.EXE - VC5_LINK = $(VC5_DIR)/bin/link.exe - VC6_LINK = $(VC6_BIN_DIR)/link.exe -else - CL = wine $(VC6_BIN_DIR)/CL.EXE - RC = wine $(IDE_BIN_DIR)/RC.EXE - VC5_LINK = wine $(VC5_DIR)/bin/link.exe - VC6_LINK = wine $(VC6_BIN_DIR)/link.exe -endif - -CFLAGS=/nologo /c /GX /W3 /O1 /I $(VC6_INC_DIR) /FD /MT /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /YX /Gm /Zi -LINKFLAGS=/nologo /subsystem:windows /machine:I386 /incremental:no - -VC_LINK=$(VC5_LINK) -LINKFLAGS+= /LIBPATH:$(VC5_LIB_DIR) - -all: pkware.lib - -PKWARE_SRC=$(sort $(wildcard *.cpp)) -PKWARE_OBJS=$(PKWARE_SRC:.cpp=.obj) - -pkware.lib: $(PKWARE_OBJS) - $(VC_LINK) -lib /OUT:$@ $^ /nologo - -%.obj: %.cpp - $(CL) $(CFLAGS) /Fo$@ $< - -clean: - @$(RM) -v $(PKWARE_OBJS) pkware.lib vc60.{idb,pch,pdb} - -.PHONY: clean all diff --git a/3rdParty/PKWare/PKWare.vcxproj b/3rdParty/PKWare/PKWare.vcxproj deleted file mode 100644 index 5d7772459..000000000 --- a/3rdParty/PKWare/PKWare.vcxproj +++ /dev/null @@ -1,100 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - - - - - - - - 15.0 - {C7F9F3B4-2F7C-4672-9586-94D8BA0950B6} - Win32Proj - PKWare - 10.0.17763.0 - - - - StaticLibrary - true - v141 - Unicode - - - StaticLibrary - false - v141 - true - Unicode - - - - - - - - - - - - - - - true - .\WinDebug\ - .\WinDebug\ - - - false - .\WinRel - .\WinRel - - - - NotUsing - Level3 - Disabled - true - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - true - MultiThreadedDebug - - - Windows - true - - - - - NotUsing - Level3 - MaxSpeed - true - true - true - WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) - true - MultiThreaded - - - Windows - true - true - true - - - - - - \ No newline at end of file diff --git a/3rdParty/PKWare/PKWare.vcxproj.filters b/3rdParty/PKWare/PKWare.vcxproj.filters deleted file mode 100644 index 5bcd9e7ec..000000000 --- a/3rdParty/PKWare/PKWare.vcxproj.filters +++ /dev/null @@ -1,30 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - - - Header Files - - - \ No newline at end of file diff --git a/3rdParty/PKWare/Pkware.dsp b/3rdParty/PKWare/Pkware.dsp deleted file mode 100644 index ebdaeba03..000000000 --- a/3rdParty/PKWare/Pkware.dsp +++ /dev/null @@ -1,100 +0,0 @@ -# Microsoft Developer Studio Project File - Name="Pkware" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 6.00 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Static Library" 0x0104 - -CFG=Pkware - Win32 Debug -!MESSAGE This is not a valid makefile. To build this project using NMAKE, -!MESSAGE use the Export Makefile command and run -!MESSAGE -!MESSAGE NMAKE /f "Pkware.mak". -!MESSAGE -!MESSAGE You can specify a configuration when running NMAKE -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "Pkware.mak" CFG="Pkware - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "Pkware - Win32 Release" (based on "Win32 (x86) Static Library") -!MESSAGE "Pkware - Win32 Debug" (based on "Win32 (x86) Static Library") -!MESSAGE - -# Begin Project -# PROP AllowPerConfigDependencies 0 -# PROP Scc_ProjName "" -# PROP Scc_LocalPath "" -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "Pkware - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "WinRel" -# PROP BASE Intermediate_Dir "WinRel" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "WinRel" -# PROP Intermediate_Dir "WinRel" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD BASE RSC /l 0x409 /d "NDEBUG" -# ADD RSC /l 0x409 /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ELSEIF "$(CFG)" == "Pkware - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "WinDebug" -# PROP BASE Intermediate_Dir "WinDebug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "WinDebug" -# PROP Intermediate_Dir "WinDebug" -# PROP Target_Dir "" -# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD BASE RSC /l 0x409 /d "_DEBUG" -# ADD RSC /l 0x409 /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -LIB32=link.exe -lib -# ADD BASE LIB32 /nologo -# ADD LIB32 /nologo - -!ENDIF - -# Begin Target - -# Name "Pkware - Win32 Release" -# Name "Pkware - Win32 Debug" -# Begin Group "Source Files" - -# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" -# Begin Source File - -SOURCE=.\explode.cpp -# End Source File -# Begin Source File - -SOURCE=.\implode.cpp -# End Source File -# End Group -# Begin Group "Header Files" - -# PROP Default_Filter "h;hpp;hxx;hm;inl" -# End Group -# End Target -# End Project diff --git a/3rdParty/PKWare/explode.cpp b/3rdParty/PKWare/explode.cpp deleted file mode 100644 index 2702c4bfe..000000000 --- a/3rdParty/PKWare/explode.cpp +++ /dev/null @@ -1,522 +0,0 @@ -/*****************************************************************************/ -/* explode.cpp Copyright (c) Ladislav Zezula 2003 */ -/*---------------------------------------------------------------------------*/ -/* Implode function of PKWARE Data Compression library */ -/*---------------------------------------------------------------------------*/ -/* Date Ver Who Comment */ -/* -------- ---- --- ------- */ -/* 11.03.03 1.00 Lad Splitted from Pkware.cpp */ -/* 08.04.03 1.01 Lad Renamed to explode.cpp to be compatible with pkware */ -/* 02.05.03 1.01 Lad Stress test done */ -/* 22.04.10 1.01 Lad Documented */ -/*****************************************************************************/ - -#include -#include - -#include "pkware.h" - -#define PKDCL_OK 0 -#define PKDCL_STREAM_END 1 // All data from the input stream is read -#define PKDCL_NEED_DICT 2 // Need more data (dictionary) -#define PKDCL_CONTINUE 10 // Internal flag, not returned to user -#define PKDCL_GET_INPUT 11 // Internal flag, not returned to user - -static char CopyrightPkware[] = "PKWARE Data Compression Library for Win32\r\n" - "Copyright 1989-1995 PKWARE Inc. All Rights Reserved\r\n" - "Patent No. 5,051,745\r\n" - "PKWARE Data Compression Library Reg. U.S. Pat. and Tm. Off.\r\n" - "Version 1.11\r\n"; - -//----------------------------------------------------------------------------- -// Tables - -static unsigned char DistBits[] = -{ - 0x02, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 -}; - -static unsigned char DistCode[] = -{ - 0x03, 0x0D, 0x05, 0x19, 0x09, 0x11, 0x01, 0x3E, 0x1E, 0x2E, 0x0E, 0x36, 0x16, 0x26, 0x06, 0x3A, - 0x1A, 0x2A, 0x0A, 0x32, 0x12, 0x22, 0x42, 0x02, 0x7C, 0x3C, 0x5C, 0x1C, 0x6C, 0x2C, 0x4C, 0x0C, - 0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08, - 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00 -}; - -static unsigned char ExLenBits[] = -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 -}; - -static unsigned short LenBase[] = -{ - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x000A, 0x000E, 0x0016, 0x0026, 0x0046, 0x0086, 0x0106 -}; - -static unsigned char LenBits[] = -{ - 0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07 -}; - -static unsigned char LenCode[] = -{ - 0x05, 0x03, 0x01, 0x06, 0x0A, 0x02, 0x0C, 0x14, 0x04, 0x18, 0x08, 0x30, 0x10, 0x20, 0x40, 0x00 -}; - -static unsigned char ChBitsAsc[] = -{ - 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x07, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x04, 0x0A, 0x08, 0x0C, 0x0A, 0x0C, 0x0A, 0x08, 0x07, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x08, - 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x07, 0x07, 0x08, 0x08, 0x0C, 0x0B, 0x07, 0x09, 0x0B, - 0x0C, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x08, 0x08, 0x06, 0x0B, 0x09, 0x06, 0x07, 0x06, 0x06, - 0x07, 0x0B, 0x06, 0x06, 0x06, 0x07, 0x09, 0x08, 0x09, 0x09, 0x0B, 0x08, 0x0B, 0x09, 0x0C, 0x08, - 0x0C, 0x05, 0x06, 0x06, 0x06, 0x05, 0x06, 0x06, 0x06, 0x05, 0x0B, 0x07, 0x05, 0x06, 0x05, 0x05, - 0x06, 0x0A, 0x05, 0x05, 0x05, 0x05, 0x08, 0x07, 0x08, 0x08, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, - 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, - 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, - 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, - 0x0D, 0x0D, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D -}; - -static unsigned short ChCodeAsc[] = -{ - 0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0, - 0x01E0, 0x00B8, 0x0062, 0x0EE0, 0x06E0, 0x0022, 0x0AE0, 0x02E0, - 0x0CE0, 0x04E0, 0x08E0, 0x00E0, 0x0F60, 0x0760, 0x0B60, 0x0360, - 0x0D60, 0x0560, 0x1240, 0x0960, 0x0160, 0x0E60, 0x0660, 0x0A60, - 0x000F, 0x0250, 0x0038, 0x0260, 0x0050, 0x0C60, 0x0390, 0x00D8, - 0x0042, 0x0002, 0x0058, 0x01B0, 0x007C, 0x0029, 0x003C, 0x0098, - 0x005C, 0x0009, 0x001C, 0x006C, 0x002C, 0x004C, 0x0018, 0x000C, - 0x0074, 0x00E8, 0x0068, 0x0460, 0x0090, 0x0034, 0x00B0, 0x0710, - 0x0860, 0x0031, 0x0054, 0x0011, 0x0021, 0x0017, 0x0014, 0x00A8, - 0x0028, 0x0001, 0x0310, 0x0130, 0x003E, 0x0064, 0x001E, 0x002E, - 0x0024, 0x0510, 0x000E, 0x0036, 0x0016, 0x0044, 0x0030, 0x00C8, - 0x01D0, 0x00D0, 0x0110, 0x0048, 0x0610, 0x0150, 0x0060, 0x0088, - 0x0FA0, 0x0007, 0x0026, 0x0006, 0x003A, 0x001B, 0x001A, 0x002A, - 0x000A, 0x000B, 0x0210, 0x0004, 0x0013, 0x0032, 0x0003, 0x001D, - 0x0012, 0x0190, 0x000D, 0x0015, 0x0005, 0x0019, 0x0008, 0x0078, - 0x00F0, 0x0070, 0x0290, 0x0410, 0x0010, 0x07A0, 0x0BA0, 0x03A0, - 0x0240, 0x1C40, 0x0C40, 0x1440, 0x0440, 0x1840, 0x0840, 0x1040, - 0x0040, 0x1F80, 0x0F80, 0x1780, 0x0780, 0x1B80, 0x0B80, 0x1380, - 0x0380, 0x1D80, 0x0D80, 0x1580, 0x0580, 0x1980, 0x0980, 0x1180, - 0x0180, 0x1E80, 0x0E80, 0x1680, 0x0680, 0x1A80, 0x0A80, 0x1280, - 0x0280, 0x1C80, 0x0C80, 0x1480, 0x0480, 0x1880, 0x0880, 0x1080, - 0x0080, 0x1F00, 0x0F00, 0x1700, 0x0700, 0x1B00, 0x0B00, 0x1300, - 0x0DA0, 0x05A0, 0x09A0, 0x01A0, 0x0EA0, 0x06A0, 0x0AA0, 0x02A0, - 0x0CA0, 0x04A0, 0x08A0, 0x00A0, 0x0F20, 0x0720, 0x0B20, 0x0320, - 0x0D20, 0x0520, 0x0920, 0x0120, 0x0E20, 0x0620, 0x0A20, 0x0220, - 0x0C20, 0x0420, 0x0820, 0x0020, 0x0FC0, 0x07C0, 0x0BC0, 0x03C0, - 0x0DC0, 0x05C0, 0x09C0, 0x01C0, 0x0EC0, 0x06C0, 0x0AC0, 0x02C0, - 0x0CC0, 0x04C0, 0x08C0, 0x00C0, 0x0F40, 0x0740, 0x0B40, 0x0340, - 0x0300, 0x0D40, 0x1D00, 0x0D00, 0x1500, 0x0540, 0x0500, 0x1900, - 0x0900, 0x0940, 0x1100, 0x0100, 0x1E00, 0x0E00, 0x0140, 0x1600, - 0x0600, 0x1A00, 0x0E40, 0x0640, 0x0A40, 0x0A00, 0x1200, 0x0200, - 0x1C00, 0x0C00, 0x1400, 0x0400, 0x1800, 0x0800, 0x1000, 0x0000 -}; - -//----------------------------------------------------------------------------- -// Local functions - -static void PKWAREAPI GenDecodeTabs( - unsigned char * positions, // [out] Table of positions - unsigned char * start_indexes, // [in] Table of start indexes - unsigned char * length_bits, // [in] Table of lengths. Each length is stored as number of bits - size_t elements) // [in] Number of elements in start_indexes and length_bits -{ - unsigned int index; - unsigned int length; - size_t i; - - for(i = 0; i < elements; i++) - { - length = 1 << length_bits[i]; // Get the length in bytes - - for(index = start_indexes[i]; index < 0x100; index += length) - { - positions[index] = (unsigned char)i; - } - } -} - -static void PKWAREAPI GenAscTabs(TDcmpStruct * pWork) -{ - unsigned short * pChCodeAsc = &ChCodeAsc[0xFF]; - unsigned int acc, add; - unsigned short count; - - for(count = 0x00FF; pChCodeAsc >= ChCodeAsc; pChCodeAsc--, count--) - { - unsigned char * pChBitsAsc = pWork->ChBitsAsc + count; - unsigned char bits_asc = *pChBitsAsc; - - if(bits_asc <= 8) - { - add = (1 << bits_asc); - acc = *pChCodeAsc; - - do - { - pWork->offs2C34[acc] = (unsigned char)count; - acc += add; - } - while(acc < 0x100); - } - else if((acc = (*pChCodeAsc & 0xFF)) != 0) - { - pWork->offs2C34[acc] = 0xFF; - - if(*pChCodeAsc & 0x3F) - { - bits_asc -= 4; - *pChBitsAsc = bits_asc; - - add = (1 << bits_asc); - acc = *pChCodeAsc >> 4; - do - { - pWork->offs2D34[acc] = (unsigned char)count; - acc += add; - } - while(acc < 0x100); - } - else - { - bits_asc -= 6; - *pChBitsAsc = bits_asc; - - add = (1 << bits_asc); - acc = *pChCodeAsc >> 6; - do - { - pWork->offs2E34[acc] = (unsigned char)count; - acc += add; - } - while(acc < 0x80); - } - } - else - { - bits_asc -= 8; - *pChBitsAsc = bits_asc; - - add = (1 << bits_asc); - acc = *pChCodeAsc >> 8; - do - { - pWork->offs2EB4[acc] = (unsigned char)count; - acc += add; - } - while(acc < 0x100); - } - } -} - -//----------------------------------------------------------------------------- -// Removes given number of bits in the bit buffer. New bits are reloaded from -// the input buffer, if needed. -// Returns: PKDCL_OK: Operation was successful -// PKDCL_STREAM_END: There are no more bits in the input buffer - -static int PKWAREAPI WasteBits(TDcmpStruct * pWork, unsigned int nBits) -{ - // If number of bits required is less than number of (bits in the buffer) ? - if(nBits <= pWork->extra_bits) - { - pWork->extra_bits -= nBits; - pWork->bit_buff >>= nBits; - return PKDCL_OK; - } - - // Load input buffer if necessary - pWork->bit_buff >>= pWork->extra_bits; - if(pWork->in_pos == pWork->in_bytes) - { - pWork->in_pos = sizeof(pWork->in_buff); - if((pWork->in_bytes = pWork->read_buf((char *)pWork->in_buff, &pWork->in_pos, pWork->param)) == 0) - return PKDCL_STREAM_END; - pWork->in_pos = 0; - } - - // Update bit buffer - pWork->bit_buff |= (pWork->in_buff[pWork->in_pos++] << 8); - pWork->bit_buff >>= (nBits - pWork->extra_bits); - pWork->extra_bits = (pWork->extra_bits - nBits) + 8; - return PKDCL_OK; -} - -//----------------------------------------------------------------------------- -// Decodes next literal from the input (compressed) data. -// Returns : 0x000: One byte 0x00 -// 0x001: One byte 0x01 -// ... -// 0x0FF: One byte 0xFF -// 0x100: Repetition, length of 0x02 bytes -// 0x101: Repetition, length of 0x03 bytes -// ... -// 0x304: Repetition, length of 0x206 bytes -// 0x305: End of stream -// 0x306: Error - -static unsigned int PKWAREAPI DecodeLit(TDcmpStruct * pWork) -{ - unsigned int extra_length_bits; // Number of bits of extra literal length - unsigned int length_code; // Length code - unsigned int value; - - // Test the current bit in byte buffer. If is not set, simply return the next 8 bits. - if(pWork->bit_buff & 1) - { - // Remove one bit from the input data - if(WasteBits(pWork, 1)) - return 0x306; - - // The next 8 bits hold the index to the length code table - length_code = pWork->LengthCodes[pWork->bit_buff & 0xFF]; - - // Remove the apropriate number of bits - if(WasteBits(pWork, pWork->LenBits[length_code])) - return 0x306; - - // Are there some extra bits for the obtained length code ? - if((extra_length_bits = pWork->ExLenBits[length_code]) != 0) - { - unsigned int extra_length = pWork->bit_buff & ((1 << extra_length_bits) - 1); - - if(WasteBits(pWork, extra_length_bits)) - { - if((length_code + extra_length) != 0x10E) - return 0x306; - } - length_code = pWork->LenBase[length_code] + extra_length; - } - - // In order to distinguish uncompressed byte from repetition length, - // we have to add 0x100 to the length. - return length_code + 0x100; - } - - // Remove one bit from the input data - if(WasteBits(pWork, 1)) - return 0x306; - - // If the binary compression type, read 8 bits and return them as one byte. - if(pWork->ctype == CMP_BINARY) - { - unsigned int uncompressed_byte = pWork->bit_buff & 0xFF; - - if(WasteBits(pWork, 8)) - return 0x306; - return uncompressed_byte; - } - - // When ASCII compression ... - if(pWork->bit_buff & 0xFF) - { - value = pWork->offs2C34[pWork->bit_buff & 0xFF]; - - if(value == 0xFF) - { - if(pWork->bit_buff & 0x3F) - { - if(WasteBits(pWork, 4)) - return 0x306; - - value = pWork->offs2D34[pWork->bit_buff & 0xFF]; - } - else - { - if(WasteBits(pWork, 6)) - return 0x306; - - value = pWork->offs2E34[pWork->bit_buff & 0x7F]; - } - } - } - else - { - if(WasteBits(pWork, 8)) - return 0x306; - - value = pWork->offs2EB4[pWork->bit_buff & 0xFF]; - } - - return WasteBits(pWork, pWork->ChBitsAsc[value]) ? 0x306 : value; -} - -//----------------------------------------------------------------------------- -// Decodes the distance of the repetition, backwards relative to the -// current output buffer position - -static unsigned int PKWAREAPI DecodeDist(TDcmpStruct * pWork, unsigned int rep_length) -{ - unsigned int dist_pos_code; // Distance position code - unsigned int dist_pos_bits; // Number of bits of distance position - unsigned int distance; // Distance position - - // Next 2-8 bits in the input buffer is the distance position code - dist_pos_code = pWork->DistPosCodes[pWork->bit_buff & 0xFF]; - dist_pos_bits = pWork->DistBits[dist_pos_code]; - if(WasteBits(pWork, dist_pos_bits)) - return 0; - - if(rep_length == 2) - { - // If the repetition is only 2 bytes length, - // then take 2 bits from the stream in order to get the distance - distance = (dist_pos_code << 2) | (pWork->bit_buff & 0x03); - if(WasteBits(pWork, 2)) - return 0; - } - else - { - // If the repetition is more than 2 bytes length, - // then take "dsize_bits" bits in order to get the distance - distance = (dist_pos_code << pWork->dsize_bits) | (pWork->bit_buff & pWork->dsize_mask); - if(WasteBits(pWork, pWork->dsize_bits)) - return 0; - } - return distance + 1; -} - -static unsigned int PKWAREAPI Expand(TDcmpStruct * pWork) -{ - unsigned int next_literal; // Literal decoded from the compressed data - unsigned int result; // Value to be returned - unsigned int copyBytes; // Number of bytes to copy to the output buffer - - pWork->outputPos = 0x1000; // Initialize output buffer position - - // Decode the next literal from the input data. - // The returned literal can either be an uncompressed byte (next_literal < 0x100) - // or an encoded length of the repeating byte sequence that - // is to be copied to the current buffer position - while((result = next_literal = DecodeLit(pWork)) < 0x305) - { - // If the literal is greater than 0x100, it holds length - // of repeating byte sequence - // literal of 0x100 means repeating sequence of 0x2 bytes - // literal of 0x101 means repeating sequence of 0x3 bytes - // ... - // literal of 0x305 means repeating sequence of 0x207 bytes - if(next_literal >= 0x100) - { - unsigned char * source; - unsigned char * target; - unsigned int rep_length; // Length of the repetition, in bytes - unsigned int minus_dist; // Backward distance to the repetition, relative to the current buffer position - - // Get the length of the repeating sequence. - // Note that the repeating block may overlap the current output position, - // for example if there was a sequence of equal bytes - rep_length = next_literal - 0xFE; - - // Get backward distance to the repetition - if((minus_dist = DecodeDist(pWork, rep_length)) == 0) - { - result = 0x306; - break; - } - - // Target and source pointer - target = &pWork->out_buff[pWork->outputPos]; - source = target - minus_dist; - - // Update buffer output position - pWork->outputPos += rep_length; - - // Copy the repeating sequence - while(rep_length-- > 0) - *target++ = *source++; - } - else - { - pWork->out_buff[pWork->outputPos++] = (unsigned char)next_literal; - } - - // Flush the output buffer, if number of extracted bytes has reached the end - if(pWork->outputPos >= 0x2000) - { - // Copy decompressed data into user buffer - copyBytes = 0x1000; - pWork->write_buf((char *)&pWork->out_buff[0x1000], ©Bytes, pWork->param); - - // Now copy the decompressed data to the first half of the buffer. - // This is needed because the decompression might reuse them as repetitions. - // Note that if the output buffer overflowed previously, the extra decompressed bytes - // are stored in "out_buff_overflow", and they will now be - // within decompressed part of the output buffer. - memmove(pWork->out_buff, &pWork->out_buff[0x1000], pWork->outputPos - 0x1000); - pWork->outputPos -= 0x1000; - } - } - - // Flush any remaining decompressed bytes - copyBytes = pWork->outputPos - 0x1000; - pWork->write_buf((char *)&pWork->out_buff[0x1000], ©Bytes, pWork->param); - return result; -} - - -//----------------------------------------------------------------------------- -// Main exploding function. - -unsigned int PKWAREAPI explode( - unsigned int (PKWAREAPI *read_buf)(char *buf, unsigned int *size, void *param), - void (PKWAREAPI *write_buf)(char *buf, unsigned int *size, void *param), - char *work_buf, - void *param) -{ - TDcmpStruct * pWork = (TDcmpStruct *)work_buf; - - // Initialize work struct and load compressed data - // Note: The caller must zero the "work_buff" before passing it to explode - pWork->read_buf = read_buf; - pWork->write_buf = write_buf; - pWork->param = param; - pWork->in_pos = sizeof(pWork->in_buff); - pWork->in_bytes = pWork->read_buf((char *)pWork->in_buff, &pWork->in_pos, pWork->param); - if(pWork->in_bytes <= 4) - return CMP_BAD_DATA; - - pWork->ctype = pWork->in_buff[0]; // Get the compression type (CMP_BINARY or CMP_ASCII) - pWork->dsize_bits = pWork->in_buff[1]; // Get the dictionary size - pWork->bit_buff = pWork->in_buff[2]; // Initialize 16-bit bit buffer - pWork->extra_bits = 0; // Extra (over 8) bits - pWork->in_pos = 3; // Position in input buffer - - // Test for the valid dictionary size - if(4 > pWork->dsize_bits || pWork->dsize_bits > 6) - return CMP_INVALID_DICTSIZE; - - pWork->dsize_mask = 0xFFFF >> (0x10 - pWork->dsize_bits); // Shifted by 'sar' instruction - - if(pWork->ctype != CMP_BINARY) - { - if(pWork->ctype != CMP_ASCII) - return CMP_INVALID_MODE; - - memcpy(pWork->ChBitsAsc, ChBitsAsc, sizeof(pWork->ChBitsAsc)); - GenAscTabs(pWork); - } - - memcpy(pWork->LenBits, LenBits, sizeof(pWork->LenBits)); - GenDecodeTabs(pWork->LengthCodes, LenCode, pWork->LenBits, sizeof(pWork->LenBits)); - memcpy(pWork->ExLenBits, ExLenBits, sizeof(pWork->ExLenBits)); - memcpy(pWork->LenBase, LenBase, sizeof(pWork->LenBase)); - memcpy(pWork->DistBits, DistBits, sizeof(pWork->DistBits)); - GenDecodeTabs(pWork->DistPosCodes, DistCode, pWork->DistBits, sizeof(pWork->DistBits)); - if(Expand(pWork) != 0x306) - return CMP_NO_ERROR; - - return CMP_ABORT; -} diff --git a/3rdParty/PKWare/implode.cpp b/3rdParty/PKWare/implode.cpp deleted file mode 100644 index 2ea8949d6..000000000 --- a/3rdParty/PKWare/implode.cpp +++ /dev/null @@ -1,775 +0,0 @@ -/*****************************************************************************/ -/* implode.cpp Copyright (c) Ladislav Zezula 2003 */ -/*---------------------------------------------------------------------------*/ -/* Implode function of PKWARE Data Compression library */ -/*---------------------------------------------------------------------------*/ -/* Date Ver Who Comment */ -/* -------- ---- --- ------- */ -/* 11.04.03 1.00 Lad First version of implode.cpp */ -/* 02.05.03 1.00 Lad Stress test done */ -/* 22.04.10 1.01 Lad Documented */ -/*****************************************************************************/ - -#include -#include - -#include "pkware.h" - -#if ((1200 < _MSC_VER) && (_MSC_VER < 1400)) -#pragma optimize("", off) -#endif - -//----------------------------------------------------------------------------- -// Defines - -#define MAX_REP_LENGTH 0x204 // The longest allowed repetition - -static char CopyrightPkware[] = "PKWARE Data Compression Library for Win32\r\n" - "Copyright 1989-1995 PKWARE Inc. All Rights Reserved\r\n" - "Patent No. 5,051,745\r\n" - "PKWARE Data Compression Library Reg. U.S. Pat. and Tm. Off.\r\n" - "Version 1.11\r\n"; - -//----------------------------------------------------------------------------- -// Tables - -static unsigned char DistBits[] = -{ - 0x02, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 -}; - -static unsigned char DistCode[] = -{ - 0x03, 0x0D, 0x05, 0x19, 0x09, 0x11, 0x01, 0x3E, 0x1E, 0x2E, 0x0E, 0x36, 0x16, 0x26, 0x06, 0x3A, - 0x1A, 0x2A, 0x0A, 0x32, 0x12, 0x22, 0x42, 0x02, 0x7C, 0x3C, 0x5C, 0x1C, 0x6C, 0x2C, 0x4C, 0x0C, - 0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08, - 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00 -}; - -static unsigned char ExLenBits[] = -{ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 -}; - -static unsigned char LenBits[] = -{ - 0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07 -}; - -static unsigned char LenCode[] = -{ - 0x05, 0x03, 0x01, 0x06, 0x0A, 0x02, 0x0C, 0x14, 0x04, 0x18, 0x08, 0x30, 0x10, 0x20, 0x40, 0x00 -}; - -static unsigned char ChBitsAsc[] = -{ - 0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x07, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x04, 0x0A, 0x08, 0x0C, 0x0A, 0x0C, 0x0A, 0x08, 0x07, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x08, - 0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x07, 0x07, 0x08, 0x08, 0x0C, 0x0B, 0x07, 0x09, 0x0B, - 0x0C, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x08, 0x08, 0x06, 0x0B, 0x09, 0x06, 0x07, 0x06, 0x06, - 0x07, 0x0B, 0x06, 0x06, 0x06, 0x07, 0x09, 0x08, 0x09, 0x09, 0x0B, 0x08, 0x0B, 0x09, 0x0C, 0x08, - 0x0C, 0x05, 0x06, 0x06, 0x06, 0x05, 0x06, 0x06, 0x06, 0x05, 0x0B, 0x07, 0x05, 0x06, 0x05, 0x05, - 0x06, 0x0A, 0x05, 0x05, 0x05, 0x05, 0x08, 0x07, 0x08, 0x08, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, - 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, - 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, - 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, - 0x0D, 0x0D, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D -}; - -static unsigned short ChCodeAsc[] = -{ - 0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0, - 0x01E0, 0x00B8, 0x0062, 0x0EE0, 0x06E0, 0x0022, 0x0AE0, 0x02E0, - 0x0CE0, 0x04E0, 0x08E0, 0x00E0, 0x0F60, 0x0760, 0x0B60, 0x0360, - 0x0D60, 0x0560, 0x1240, 0x0960, 0x0160, 0x0E60, 0x0660, 0x0A60, - 0x000F, 0x0250, 0x0038, 0x0260, 0x0050, 0x0C60, 0x0390, 0x00D8, - 0x0042, 0x0002, 0x0058, 0x01B0, 0x007C, 0x0029, 0x003C, 0x0098, - 0x005C, 0x0009, 0x001C, 0x006C, 0x002C, 0x004C, 0x0018, 0x000C, - 0x0074, 0x00E8, 0x0068, 0x0460, 0x0090, 0x0034, 0x00B0, 0x0710, - 0x0860, 0x0031, 0x0054, 0x0011, 0x0021, 0x0017, 0x0014, 0x00A8, - 0x0028, 0x0001, 0x0310, 0x0130, 0x003E, 0x0064, 0x001E, 0x002E, - 0x0024, 0x0510, 0x000E, 0x0036, 0x0016, 0x0044, 0x0030, 0x00C8, - 0x01D0, 0x00D0, 0x0110, 0x0048, 0x0610, 0x0150, 0x0060, 0x0088, - 0x0FA0, 0x0007, 0x0026, 0x0006, 0x003A, 0x001B, 0x001A, 0x002A, - 0x000A, 0x000B, 0x0210, 0x0004, 0x0013, 0x0032, 0x0003, 0x001D, - 0x0012, 0x0190, 0x000D, 0x0015, 0x0005, 0x0019, 0x0008, 0x0078, - 0x00F0, 0x0070, 0x0290, 0x0410, 0x0010, 0x07A0, 0x0BA0, 0x03A0, - 0x0240, 0x1C40, 0x0C40, 0x1440, 0x0440, 0x1840, 0x0840, 0x1040, - 0x0040, 0x1F80, 0x0F80, 0x1780, 0x0780, 0x1B80, 0x0B80, 0x1380, - 0x0380, 0x1D80, 0x0D80, 0x1580, 0x0580, 0x1980, 0x0980, 0x1180, - 0x0180, 0x1E80, 0x0E80, 0x1680, 0x0680, 0x1A80, 0x0A80, 0x1280, - 0x0280, 0x1C80, 0x0C80, 0x1480, 0x0480, 0x1880, 0x0880, 0x1080, - 0x0080, 0x1F00, 0x0F00, 0x1700, 0x0700, 0x1B00, 0x0B00, 0x1300, - 0x0DA0, 0x05A0, 0x09A0, 0x01A0, 0x0EA0, 0x06A0, 0x0AA0, 0x02A0, - 0x0CA0, 0x04A0, 0x08A0, 0x00A0, 0x0F20, 0x0720, 0x0B20, 0x0320, - 0x0D20, 0x0520, 0x0920, 0x0120, 0x0E20, 0x0620, 0x0A20, 0x0220, - 0x0C20, 0x0420, 0x0820, 0x0020, 0x0FC0, 0x07C0, 0x0BC0, 0x03C0, - 0x0DC0, 0x05C0, 0x09C0, 0x01C0, 0x0EC0, 0x06C0, 0x0AC0, 0x02C0, - 0x0CC0, 0x04C0, 0x08C0, 0x00C0, 0x0F40, 0x0740, 0x0B40, 0x0340, - 0x0300, 0x0D40, 0x1D00, 0x0D00, 0x1500, 0x0540, 0x0500, 0x1900, - 0x0900, 0x0940, 0x1100, 0x0100, 0x1E00, 0x0E00, 0x0140, 0x1600, - 0x0600, 0x1A00, 0x0E40, 0x0640, 0x0A40, 0x0A00, 0x1200, 0x0200, - 0x1C00, 0x0C00, 0x1400, 0x0400, 0x1800, 0x0800, 0x1000, 0x0000 -}; - -//----------------------------------------------------------------------------- -// Macros - -// Macro for calculating hash of the current byte pair. -// Note that most exact byte pair hash would be buffer[0] + buffer[1] << 0x08, -// but even this way gives nice indication of equal byte pairs, with significantly -// smaller size of the array that holds numbers of those hashes -#define BYTE_PAIR_HASH(buffer) ((buffer[0] * 4) + (buffer[1] * 5)) - -//----------------------------------------------------------------------------- -// Local functions - -// Builds the "hash_to_index" table and "pair_hash_offsets" table. -// Every element of "hash_to_index" will contain lowest index to the -// "pair_hash_offsets" table, effectively giving offset of the first -// occurence of the given PAIR_HASH in the input data. -static void PKWAREAPI SortBuffer(TCmpStruct * pWork, unsigned char * buffer_begin, unsigned char * buffer_end) -{ - unsigned short * phash_to_index; - unsigned char * buffer_ptr; - unsigned short total_sum = 0; - unsigned long byte_pair_hash; // Hash value of the byte pair - unsigned short byte_pair_offs; // Offset of the byte pair, relative to "work_buff" - - // Zero the entire "phash_to_index" table - memset(pWork->phash_to_index, 0, sizeof(pWork->phash_to_index)); - - // Step 1: Count amount of each PAIR_HASH in the input buffer - // The table will look like this: - // offs 0x000: Number of occurences of PAIR_HASH 0 - // offs 0x001: Number of occurences of PAIR_HASH 1 - // ... - // offs 0x8F7: Number of occurences of PAIR_HASH 0x8F7 (the highest hash value) - for(buffer_ptr = buffer_begin; buffer_ptr < buffer_end; buffer_ptr++) - pWork->phash_to_index[BYTE_PAIR_HASH(buffer_ptr)]++; - - // Step 2: Convert the table to the array of PAIR_HASH amounts. - // Each element contains count of PAIR_HASHes that is less or equal - // to element index - // The table will look like this: - // offs 0x000: Number of occurences of PAIR_HASH 0 or lower - // offs 0x001: Number of occurences of PAIR_HASH 1 or lower - // ... - // offs 0x8F7: Number of occurences of PAIR_HASH 0x8F7 or lower - for(phash_to_index = pWork->phash_to_index; phash_to_index < &pWork->phash_to_index_end; phash_to_index++) - { - total_sum = total_sum + phash_to_index[0]; - phash_to_index[0] = total_sum; - } - - // Step 3: Convert the table to the array of indexes. - // Now, each element contains index to the first occurence of given PAIR_HASH - for(buffer_end--; buffer_end >= buffer_begin; buffer_end--) - { - byte_pair_hash = BYTE_PAIR_HASH(buffer_end); - byte_pair_offs = (unsigned short)(buffer_end - pWork->work_buff); - - pWork->phash_to_index[byte_pair_hash]--; - pWork->phash_offs[pWork->phash_to_index[byte_pair_hash]] = byte_pair_offs; - } -} - -static void PKWAREAPI FlushBuf(TCmpStruct * pWork) -{ - unsigned char save_ch1; - unsigned char save_ch2; - unsigned int size = 0x800; - - pWork->write_buf(pWork->out_buff, &size, pWork->param); - - save_ch1 = pWork->out_buff[0x800]; - save_ch2 = pWork->out_buff[pWork->out_bytes]; - pWork->out_bytes -= 0x800; - - memset(pWork->out_buff, 0, sizeof(pWork->out_buff)); - - if(pWork->out_bytes != 0) - pWork->out_buff[0] = save_ch1; - if(pWork->out_bits != 0) - pWork->out_buff[pWork->out_bytes] = save_ch2; -} - -static void PKWAREAPI OutputBits(TCmpStruct * pWork, unsigned int nbits, unsigned long bit_buff) -{ - unsigned int out_bits; - - // If more than 8 bits to output, do recursion - if(nbits > 8) - { - OutputBits(pWork, 8, bit_buff); - bit_buff >>= 8; - nbits -= 8; - } - - // Add bits to the last out byte in out_buff; - out_bits = pWork->out_bits; - pWork->out_buff[pWork->out_bytes] |= (unsigned char)(bit_buff << out_bits); - pWork->out_bits += nbits; - - // If 8 or more bits, increment number of bytes - if(pWork->out_bits > 8) - { - pWork->out_bytes++; - bit_buff >>= (8 - out_bits); - - pWork->out_buff[pWork->out_bytes] = (unsigned char)bit_buff; - pWork->out_bits &= 7; - } - else - { - pWork->out_bits &= 7; - if(pWork->out_bits == 0) - pWork->out_bytes++; - } - - // If there is enough compressed bytes, flush them - if(pWork->out_bytes >= 0x800) - FlushBuf(pWork); -} - -// This function searches for a repetition -// (a previous occurence of the current byte sequence) -// Returns length of the repetition, and stores the backward distance -// to pWork structure. -static unsigned int PKWAREAPI FindRep(TCmpStruct * pWork, unsigned char * input_data) -{ - unsigned short * phash_to_index; // Pointer into pWork->phash_to_index table - unsigned short * phash_offs; // Pointer to the table containing offsets of each PAIR_HASH - unsigned char * repetition_limit; // An eventual repetition must be at position below this pointer - unsigned char * prev_repetition; // Pointer to the previous occurence of the current PAIR_HASH - unsigned char * prev_rep_end; // End of the previous repetition - unsigned char * input_data_ptr; - unsigned short phash_offs_index; // Index to the table with PAIR_HASH positions - unsigned short min_phash_offs; // The lowest allowed hash offset - unsigned short offs_in_rep; // Offset within found repetition - unsigned int equal_byte_count; // Number of bytes that are equal to the previous occurence - unsigned int rep_length = 1; // Length of the found repetition - unsigned int rep_length2; // Secondary repetition - unsigned char pre_last_byte; // Last but one byte from a repetion - unsigned short di_val; - - // Calculate the previous position of the PAIR_HASH - phash_to_index = pWork->phash_to_index + BYTE_PAIR_HASH(input_data); - min_phash_offs = (unsigned short)((input_data - pWork->work_buff) - pWork->dsize_bytes + 1); - phash_offs_index = phash_to_index[0]; - - // If the PAIR_HASH offset is below the limit, find a next one - phash_offs = pWork->phash_offs + phash_offs_index; - if(*phash_offs < min_phash_offs) - { - while(*phash_offs < min_phash_offs) - { - phash_offs_index++; - phash_offs++; - } - *phash_to_index = phash_offs_index; - } - - // Get the first location of the PAIR_HASH, - // and thus the first eventual location of byte repetition - phash_offs = pWork->phash_offs + phash_offs_index; - prev_repetition = pWork->work_buff + phash_offs[0]; - repetition_limit = input_data - 1; - - // If the current PAIR_HASH was not encountered before, - // we haven't found a repetition. - if(prev_repetition >= repetition_limit) - return 0; - - // We have found a match of a PAIR_HASH. Now we have to make sure - // that it is also a byte match, because PAIR_HASH is not unique. - // We compare the bytes and count the length of the repetition - input_data_ptr = input_data; - for(;;) - { - // If the first byte of the repetition and the so-far-last byte - // of the repetition are equal, we will compare the blocks. - if(*input_data_ptr == *prev_repetition && input_data_ptr[rep_length-1] == prev_repetition[rep_length-1]) - { - // Skip the current byte - prev_repetition++; - input_data_ptr++; - equal_byte_count = 2; - - // Now count how many more bytes are equal - while(equal_byte_count < MAX_REP_LENGTH) - { - prev_repetition++; - input_data_ptr++; - - // Are the bytes different ? - if(*prev_repetition != *input_data_ptr) - break; - - equal_byte_count++; - } - - // If we found a repetition of at least the same length, take it. - // If there are multiple repetitions in the input buffer, this will - // make sure that we find the most recent one, which in turn allows - // us to store backward length in less amount of bits - input_data_ptr = input_data; - if(equal_byte_count >= rep_length) - { - // Calculate the backward distance of the repetition. - // Note that the distance is stored as decremented by 1 - pWork->distance = (unsigned int)(input_data - prev_repetition + equal_byte_count - 1); - - // Repetitions longer than 10 bytes will be stored in more bits, - // so they need a bit different handling - if((rep_length = equal_byte_count) > 10) - break; - } - } - - // Move forward in the table of PAIR_HASH repetitions. - // There might be a more recent occurence of the same repetition. - phash_offs_index++; - phash_offs++; - prev_repetition = pWork->work_buff + phash_offs[0]; - - // If the next repetition is beyond the minimum allowed repetition, we are done. - if(prev_repetition >= repetition_limit) - { - // A repetition must have at least 2 bytes, otherwise it's not worth it - return (rep_length >= 2) ? rep_length : 0; - } - } - - // If the repetition has max length of 0x204 bytes, we can't go any fuhrter - if(equal_byte_count == MAX_REP_LENGTH) - { - pWork->distance--; - return equal_byte_count; - } - - // Check for possibility of a repetition that occurs at more recent position - phash_offs = pWork->phash_offs + phash_offs_index; - if(pWork->work_buff + phash_offs[1] >= repetition_limit) - return rep_length; - - // - // The following part checks if there isn't a longer repetition at - // a latter offset, that would lead to better compression. - // - // Example of data that can trigger this optimization: - // - // "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEQQQQQQQQQQQQ" - // "XYZ" - // "EEEEEEEEEEEEEEEEQQQQQQQQQQQQ"; - // - // Description of data in this buffer - // [0x00] Single byte "E" - // [0x01] Single byte "E" - // [0x02] Repeat 0x1E bytes from [0x00] - // [0x20] Single byte "X" - // [0x21] Single byte "Y" - // [0x22] Single byte "Z" - // [0x23] 17 possible previous repetitions of length at least 0x10 bytes: - // - Repetition of 0x10 bytes from [0x00] "EEEEEEEEEEEEEEEE" - // - Repetition of 0x10 bytes from [0x01] "EEEEEEEEEEEEEEEE" - // - Repetition of 0x10 bytes from [0x02] "EEEEEEEEEEEEEEEE" - // ... - // - Repetition of 0x10 bytes from [0x0F] "EEEEEEEEEEEEEEEE" - // - Repetition of 0x1C bytes from [0x10] "EEEEEEEEEEEEEEEEQQQQQQQQQQQQ" - // The last repetition is the best one. - // - - pWork->offs09BC[0] = 0xFFFF; - pWork->offs09BC[1] = 0x0000; - di_val = 0; - - // Note: I failed to figure out what does the table "offs09BC" mean. - // If anyone has an idea, let me know to zezula_at_volny_dot_cz - for(offs_in_rep = 1; offs_in_rep < rep_length; ) - { - if(input_data[offs_in_rep] != input_data[di_val]) - { - di_val = pWork->offs09BC[di_val]; - if(di_val != 0xFFFF) - continue; - } - pWork->offs09BC[++offs_in_rep] = ++di_val; - } - - // - // Now go through all the repetitions from the first found one - // to the current input data, and check if any of them migh be - // a start of a greater sequence match. - // - - prev_repetition = pWork->work_buff + phash_offs[0]; - prev_rep_end = prev_repetition + rep_length; - rep_length2 = rep_length; - - for(;;) - { - rep_length2 = pWork->offs09BC[rep_length2]; - if(rep_length2 == 0xFFFF) - rep_length2 = 0; - - // Get the pointer to the previous repetition - phash_offs = pWork->phash_offs + phash_offs_index; - - // Skip those repetitions that don't reach the end - // of the first found repetition - do - { - phash_offs++; - phash_offs_index++; - prev_repetition = pWork->work_buff + *phash_offs; - if(prev_repetition >= repetition_limit) - return rep_length; - } - while(prev_repetition + rep_length2 < prev_rep_end); - - // Verify if the last but one byte from the repetition matches - // the last but one byte from the input data. - // If not, find a next repetition - pre_last_byte = input_data[rep_length - 2]; - if(pre_last_byte == prev_repetition[rep_length - 2]) - { - // If the new repetition reaches beyond the end - // of previously found repetition, reset the repetition length to zero. - if(prev_repetition + rep_length2 != prev_rep_end) - { - prev_rep_end = prev_repetition; - rep_length2 = 0; - } - } - else - { - phash_offs = pWork->phash_offs + phash_offs_index; - do - { - phash_offs++; - phash_offs_index++; - prev_repetition = pWork->work_buff + *phash_offs; - if(prev_repetition >= repetition_limit) - return rep_length; - } - while(prev_repetition[rep_length - 2] != pre_last_byte || prev_repetition[0] != input_data[0]); - - // Reset the length of the repetition to 2 bytes only - prev_rep_end = prev_repetition + 2; - rep_length2 = 2; - } - - // Find out how many more characters are equal to the first repetition. - while(*prev_rep_end == input_data[rep_length2]) - { - if(++rep_length2 >= 0x204) - break; - prev_rep_end++; - } - - // Is the newly found repetion at least as long as the previous one ? - if(rep_length2 >= rep_length) - { - // Calculate the distance of the new repetition - pWork->distance = (unsigned int)(input_data - prev_repetition - 1); - if((rep_length = rep_length2) == 0x204) - return rep_length; - - // Update the additional elements in the "offs09BC" table - // to reflect new rep length - while(offs_in_rep < rep_length2) - { - if(input_data[offs_in_rep] != input_data[di_val]) - { - di_val = pWork->offs09BC[di_val]; - if(di_val != 0xFFFF) - continue; - } - pWork->offs09BC[++offs_in_rep] = ++di_val; - } - } - } -} - -static void PKWAREAPI WriteCmpData(TCmpStruct * pWork) -{ - unsigned char * input_data_end; // Pointer to the end of the input data - unsigned char * input_data = pWork->work_buff + pWork->dsize_bytes + 0x204; - unsigned int input_data_ended = 0; // If 1, then all data from the input stream have been already loaded - unsigned int save_rep_length; // Saved length of current repetition - unsigned int save_distance = 0; // Saved distance of current repetition - unsigned int rep_length; // Length of the found repetition - unsigned int phase = 0; // - - // Store the compression type and dictionary size - pWork->out_buff[0] = (char)pWork->ctype; - pWork->out_buff[1] = (char)pWork->dsize_bits; - pWork->out_bytes = 2; - - // Reset output buffer to zero - memset(&pWork->out_buff[2], 0, sizeof(pWork->out_buff) - 2); - pWork->out_bits = 0; - - while(input_data_ended == 0) - { - unsigned int bytes_to_load = 0x1000; - int total_loaded = 0; - int bytes_loaded; - - // Load the bytes from the input stream, up to 0x1000 bytes - while(bytes_to_load != 0) - { - bytes_loaded = pWork->read_buf((char *)pWork->work_buff + pWork->dsize_bytes + 0x204 + total_loaded, - &bytes_to_load, - pWork->param); - if(bytes_loaded == 0) - { - if(total_loaded == 0 && phase == 0) - goto __Exit; - input_data_ended = 1; - break; - } - else - { - bytes_to_load -= bytes_loaded; - total_loaded += bytes_loaded; - } - } - - input_data_end = pWork->work_buff + pWork->dsize_bytes + total_loaded; - if(input_data_ended) - input_data_end += 0x204; - - // - // Warning: The end of the buffer passed to "SortBuffer" is actually 2 bytes beyond - // valid data. It is questionable if this is actually a bug or not, - // but it might cause the compressed data output to be dependent on random bytes - // that are in the buffer. - // To prevent that, the calling application must always zero the compression - // buffer before passing it to "implode" - // - - // Search the PAIR_HASHes of the loaded blocks. Also, include - // previously compressed data, if any. - switch(phase) - { - case 0: - SortBuffer(pWork, input_data, input_data_end + 1); - phase++; - if(pWork->dsize_bytes != 0x1000) - phase++; - break; - - case 1: - SortBuffer(pWork, input_data - pWork->dsize_bytes + 0x204, input_data_end + 1); - phase++; - break; - - default: - SortBuffer(pWork, input_data - pWork->dsize_bytes, input_data_end + 1); - break; - } - - // Perform the compression of the current block - while(input_data < input_data_end) - { - // Find if the current byte sequence wasn't there before. - rep_length = FindRep(pWork, input_data); - while(rep_length != 0) - { - // If we found repetition of 2 bytes, that is 0x100 or fuhrter back, - // don't bother. Storing the distance of 0x100 bytes would actually - // take more space than storing the 2 bytes as-is. - if(rep_length == 2 && pWork->distance >= 0x100) - break; - - // When we are at the end of the input data, we cannot allow - // the repetition to go past the end of the input data. - if(input_data_ended && input_data + rep_length > input_data_end) - { - // Shorten the repetition length so that it only covers valid data - rep_length = (unsigned long)(input_data_end - input_data); - if(rep_length < 2) - break; - - // If we got repetition of 2 bytes, that is 0x100 or more backward, don't bother - if(rep_length == 2 && pWork->distance >= 0x100) - break; - goto __FlushRepetition; - } - - if(rep_length >= 8 || input_data + 1 >= input_data_end) - goto __FlushRepetition; - - // Try to find better repetition 1 byte later. - // Example: "ARROCKFORT" "AROCKFORT" - // When "input_data" points to the second string, FindRep - // returns the occurence of "AR". But there is longer repetition "ROCKFORT", - // beginning 1 byte after. - save_rep_length = rep_length; - save_distance = pWork->distance; - rep_length = FindRep(pWork, input_data + 1); - - // Only use the new repetition if it's length is greater than the previous one - if(rep_length > save_rep_length) - { - // If the new repetition if only 1 byte better - // and the previous distance is less than 0x80 bytes, use the previous repetition - if(rep_length > save_rep_length + 1 || save_distance > 0x80) - { - // Flush one byte, so that input_data will point to the secondary repetition - OutputBits(pWork, pWork->nChBits[*input_data], pWork->nChCodes[*input_data]); - input_data++; - continue; - } - } - - // Revert to the previous repetition - rep_length = save_rep_length; - pWork->distance = save_distance; - - __FlushRepetition: - - OutputBits(pWork, pWork->nChBits[rep_length + 0xFE], pWork->nChCodes[rep_length + 0xFE]); - if(rep_length == 2) - { - OutputBits(pWork, pWork->dist_bits[pWork->distance >> 2], - pWork->dist_codes[pWork->distance >> 2]); - OutputBits(pWork, 2, pWork->distance & 3); - } - else - { - OutputBits(pWork, pWork->dist_bits[pWork->distance >> pWork->dsize_bits], - pWork->dist_codes[pWork->distance >> pWork->dsize_bits]); - OutputBits(pWork, pWork->dsize_bits, pWork->dsize_mask & pWork->distance); - } - - // Move the begin of the input data by the length of the repetition - input_data += rep_length; - goto _00402252; - } - - // If there was no previous repetition for the current position in the input data, - // just output the 9-bit literal for the one character - OutputBits(pWork, pWork->nChBits[*input_data], pWork->nChCodes[*input_data]); - input_data++; -_00402252:; - } - - if(input_data_ended == 0) - { - input_data -= 0x1000; - memmove(pWork->work_buff, pWork->work_buff + 0x1000, pWork->dsize_bytes + 0x204); - } - } - -__Exit: - - // Write the termination literal - OutputBits(pWork, pWork->nChBits[0x305], pWork->nChCodes[0x305]); - if(pWork->out_bits != 0) - pWork->out_bytes++; - pWork->write_buf(pWork->out_buff, &pWork->out_bytes, pWork->param); - return; -} - -//----------------------------------------------------------------------------- -// Main imploding function - -unsigned int PKWAREAPI implode( - unsigned int (PKWAREAPI *read_buf)(char *buf, unsigned int *size, void *param), - void (PKWAREAPI *write_buf)(char *buf, unsigned int *size, void *param), - char *work_buf, - void *param, - unsigned int *type, - unsigned int *dsize) -{ - TCmpStruct * pWork = (TCmpStruct *)work_buf; - unsigned int nChCode; - unsigned int nCount; - unsigned int i; - int nCount2; - - // Fill the work buffer information - // Note: The caller must zero the "work_buff" before passing it to implode - pWork->read_buf = read_buf; - pWork->write_buf = write_buf; - pWork->dsize_bytes = *dsize; - pWork->ctype = *type; - pWork->param = param; - pWork->dsize_bits = 4; - pWork->dsize_mask = 0x0F; - - // Test dictionary size - switch(*dsize) - { - case CMP_IMPLODE_DICT_SIZE3: // 0x1000 bytes - pWork->dsize_bits++; - pWork->dsize_mask |= 0x20; - // No break here !!! - - case CMP_IMPLODE_DICT_SIZE2: // 0x800 bytes - pWork->dsize_bits++; - pWork->dsize_mask |= 0x10; - // No break here !!! - - case CMP_IMPLODE_DICT_SIZE1: // 0x400 - break; - - default: - return CMP_INVALID_DICTSIZE; - } - - // Test the compression type - switch(*type) - { - case CMP_BINARY: // We will compress data with binary compression type - for(nChCode = 0, nCount = 0; nCount < 0x100; nCount++) - { - pWork->nChBits[nCount] = 9; - pWork->nChCodes[nCount] = (unsigned short)nChCode; - nChCode = (nChCode & 0x0000FFFF) + 2; - } - break; - - - case CMP_ASCII: // We will compress data with ASCII compression type - for(nCount = 0; nCount < 0x100; nCount++) - { - pWork->nChBits[nCount] = (unsigned char )(ChBitsAsc[nCount] + 1); - pWork->nChCodes[nCount] = (unsigned short)(ChCodeAsc[nCount] * 2); - } - break; - - default: - return CMP_INVALID_MODE; - } - - for(i = 0; i < 0x10; i++) - { - if(1 << ExLenBits[i]) - { - for(nCount2 = 0; nCount2 < (1 << ExLenBits[i]); nCount2++) - { - pWork->nChBits[nCount] = (unsigned char)(ExLenBits[i] + LenBits[i] + 1); - pWork->nChCodes[nCount] = (unsigned short)((nCount2 << (LenBits[i] + 1)) | ((LenCode[i] & 0xFFFF00FF) * 2) | 1); - nCount++; - } - } - } - - // Copy the distance codes and distance bits and perform the compression - memcpy(&pWork->dist_codes, DistCode, sizeof(DistCode)); - memcpy(&pWork->dist_bits, DistBits, sizeof(DistBits)); - WriteCmpData(pWork); - return CMP_NO_ERROR; -} diff --git a/3rdParty/PKWare/pkware.h b/3rdParty/PKWare/pkware.h deleted file mode 100644 index 37ec0b808..000000000 --- a/3rdParty/PKWare/pkware.h +++ /dev/null @@ -1,142 +0,0 @@ -/*****************************************************************************/ -/* pkware.h Copyright (c) Ladislav Zezula 2003 */ -/*---------------------------------------------------------------------------*/ -/* Header file for PKWARE Data Compression Library */ -/*---------------------------------------------------------------------------*/ -/* Date Ver Who Comment */ -/* -------- ---- --- ------- */ -/* 31.03.03 1.00 Lad The first version of pkware.h */ -/*****************************************************************************/ - -#ifndef __PKWARE_H__ -#define __PKWARE_H__ - -//----------------------------------------------------------------------------- -// Defines - -#define CMP_BINARY 0 // Binary compression -#define CMP_ASCII 1 // Ascii compression - -#define CMP_NO_ERROR 0 -#define CMP_INVALID_DICTSIZE 1 -#define CMP_INVALID_MODE 2 -#define CMP_BAD_DATA 3 -#define CMP_ABORT 4 - -#define CMP_IMPLODE_DICT_SIZE1 1024 // Dictionary size of 1024 -#define CMP_IMPLODE_DICT_SIZE2 2048 // Dictionary size of 2048 -#define CMP_IMPLODE_DICT_SIZE3 4096 // Dictionary size of 4096 - -//----------------------------------------------------------------------------- -// Define calling convention - -#ifndef PKWAREAPI -#ifdef WIN32 -#define PKWAREAPI __cdecl // Use for normal __cdecl calling -#else -#define PKWAREAPI -#endif -#endif - -//----------------------------------------------------------------------------- -// Internal structures - -// Compression structure -typedef struct -{ - unsigned int distance; // 0000: Backward distance of the currently found repetition, decreased by 1 - unsigned int out_bytes; // 0004: # bytes available in out_buff - unsigned int out_bits; // 0008: # of bits available in the last out byte - unsigned int dsize_bits; // 000C: Number of bits needed for dictionary size. 4 = 0x400, 5 = 0x800, 6 = 0x1000 - unsigned int dsize_mask; // 0010: Bit mask for dictionary. 0x0F = 0x400, 0x1F = 0x800, 0x3F = 0x1000 - unsigned int ctype; // 0014: Compression type (CMP_ASCII or CMP_BINARY) - unsigned int dsize_bytes; // 0018: Dictionary size in bytes - unsigned char dist_bits[0x40]; // 001C: Distance bits - unsigned char dist_codes[0x40]; // 005C: Distance codes - unsigned char nChBits[0x306]; // 009C: Table of literal bit lengths to be put to the output stream - unsigned short nChCodes[0x306]; // 03A2: Table of literal codes to be put to the output stream - unsigned short offs09AE; // 09AE: - - void * param; // 09B0: User parameter - unsigned int (PKWAREAPI *read_buf)(char *buf, unsigned int *size, void *param); // 9B4 - void (PKWAREAPI *write_buf)(char *buf, unsigned int *size, void *param); // 9B8 - - unsigned short offs09BC[0x204]; // 09BC: - unsigned long offs0DC4; // 0DC4: - unsigned short phash_to_index[0x900]; // 0DC8: Array of indexes (one for each PAIR_HASH) to the "pair_hash_offsets" table - unsigned short phash_to_index_end; // 1FC8: End marker for "phash_to_index" table - char out_buff[0x802]; // 1FCA: Compressed data - unsigned char work_buff[0x2204]; // 27CC: Work buffer - // + DICT_OFFSET => Dictionary - // + UNCMP_OFFSET => Uncompressed data - unsigned short phash_offs[0x2204]; // 49D0: Table of offsets for each PAIR_HASH -} TCmpStruct; - -#define CMP_BUFFER_SIZE sizeof(TCmpStruct) // Size of compression structure. - // Defined as 36312 in pkware header file - - -// Decompression structure -typedef struct -{ - unsigned long offs0000; // 0000 - unsigned long ctype; // 0004: Compression type (CMP_BINARY or CMP_ASCII) - unsigned long outputPos; // 0008: Position in output buffer - unsigned long dsize_bits; // 000C: Dict size (4, 5, 6 for 0x400, 0x800, 0x1000) - unsigned long dsize_mask; // 0010: Dict size bitmask (0x0F, 0x1F, 0x3F for 0x400, 0x800, 0x1000) - unsigned long bit_buff; // 0014: 16-bit buffer for processing input data - unsigned long extra_bits; // 0018: Number of extra (above 8) bits in bit buffer - unsigned int in_pos; // 001C: Position in in_buff - unsigned long in_bytes; // 0020: Number of bytes in input buffer - void * param; // 0024: Custom parameter - unsigned int (PKWAREAPI *read_buf)(char *buf, unsigned int *size, void *param); // Pointer to function that reads data from the input stream - void (PKWAREAPI *write_buf)(char *buf, unsigned int *size, void *param);// Pointer to function that writes data to the output stream - - unsigned char out_buff[0x2204]; // 0030: Output circle buffer. - // 0x0000 - 0x0FFF: Previous uncompressed data, kept for repetitions - // 0x1000 - 0x1FFF: Currently decompressed data - // 0x2000 - 0x2203: Reserve space for the longest possible repetition - unsigned char in_buff[0x800]; // 2234: Buffer for data to be decompressed - unsigned char DistPosCodes[0x100]; // 2A34: Table of distance position codes - unsigned char LengthCodes[0x100]; // 2B34: Table of length codes - unsigned char offs2C34[0x100]; // 2C34: Buffer for - unsigned char offs2D34[0x100]; // 2D34: Buffer for - unsigned char offs2E34[0x80]; // 2EB4: Buffer for - unsigned char offs2EB4[0x100]; // 2EB4: Buffer for - unsigned char ChBitsAsc[0x100]; // 2FB4: Buffer for - unsigned char DistBits[0x40]; // 30B4: Numbers of bytes to skip copied block length - unsigned char LenBits[0x10]; // 30F4: Numbers of bits for skip copied block length - unsigned char ExLenBits[0x10]; // 3104: Number of valid bits for copied block - unsigned short LenBase[0x10]; // 3114: Buffer for -} TDcmpStruct; - -#define EXP_BUFFER_SIZE sizeof(TDcmpStruct) // Size of decompression structure - // Defined as 12596 in pkware headers - -//----------------------------------------------------------------------------- -// Public functions - -#ifdef __cplusplus - extern "C" { -#endif - -unsigned int PKWAREAPI implode( - unsigned int (PKWAREAPI *read_buf)(char *buf, unsigned int *size, void *param), - void (PKWAREAPI *write_buf)(char *buf, unsigned int *size, void *param), - char *work_buf, - void *param, - unsigned int *type, - unsigned int *dsize); - - -unsigned int PKWAREAPI explode( - unsigned int (PKWAREAPI *read_buf)(char *buf, unsigned int *size, void *param), - void (PKWAREAPI *write_buf)(char *buf, unsigned int *size, void *param), - char *work_buf, - void *param); - -#ifdef __cplusplus - } // End of 'extern "C"' declaration -#endif - -#endif // __PKWARE_H__ diff --git a/3rdParty/libmpq/CMakeLists.txt b/3rdParty/libmpq/CMakeLists.txt deleted file mode 100644 index ad6cd4ad2..000000000 --- a/3rdParty/libmpq/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -if(NOT TARGET ZLIB::ZLIB) - find_package(ZLIB REQUIRED) -endif() - -if(NOT TARGET BZip2::BZip2) - find_package(BZip2 REQUIRED) -endif() - -include(functions/FetchContent_ExcludeFromAll_backport) - -include(FetchContent) -FetchContent_Declare_ExcludeFromAll(libmpq - URL https://github.com/diasurgical/libmpq/archive/7c2924d4553513eba1a70bbdb558198dd8c2726a.tar.gz - URL_HASH MD5=315c88c02b45851cdfee8460322de044 -) -FetchContent_MakeAvailable_ExcludeFromAll(libmpq) - -add_library(libmpq STATIC - ${libmpq_SOURCE_DIR}/libmpq/common.c - ${libmpq_SOURCE_DIR}/libmpq/explode.c - ${libmpq_SOURCE_DIR}/libmpq/extract.c - ${libmpq_SOURCE_DIR}/libmpq/huffman.c - ${libmpq_SOURCE_DIR}/libmpq/mpq.c - ${libmpq_SOURCE_DIR}/libmpq/wave.c -) - -target_include_directories(libmpq PUBLIC ${libmpq_SOURCE_DIR}) -target_include_directories(libmpq PRIVATE ${CMAKE_CURRENT_LIST_DIR}) - -target_link_libraries(libmpq PRIVATE ZLIB::ZLIB BZip2::BZip2) - -if(LIBMPQ_FILE_BUFFER_SIZE) - target_compile_definitions(libmpq PRIVATE "LIBMPQ_FILE_BUFFER_SIZE=${LIBMPQ_FILE_BUFFER_SIZE}") -endif() - -if(DEVILUTIONX_WINDOWS_NO_WCHAR) - target_compile_definitions(libmpq PRIVATE LIBMPQ_WINDOWS_NO_WCHAR) -endif() diff --git a/3rdParty/libmpq/config.h b/3rdParty/libmpq/config.h deleted file mode 100644 index 05abf176b..000000000 --- a/3rdParty/libmpq/config.h +++ /dev/null @@ -1 +0,0 @@ -#define VERSION "0.4.2" diff --git a/3rdParty/mpqfs/CMakeLists.txt b/3rdParty/mpqfs/CMakeLists.txt new file mode 100644 index 000000000..05a4a5054 --- /dev/null +++ b/3rdParty/mpqfs/CMakeLists.txt @@ -0,0 +1,9 @@ +include(functions/FetchContent_ExcludeFromAll_backport) + +include(FetchContent) + +FetchContent_Declare_ExcludeFromAll(mpqfs + URL https://github.com/diasurgical/mpqfs/archive/27144129a5d8f84ddbe058a7c5d7f2c8a724af67.tar.gz + URL_HASH MD5=c137f8eb7f5e097c63f9b46769ff2b5c +) +FetchContent_MakeAvailable_ExcludeFromAll(mpqfs) diff --git a/CMake/Dependencies.cmake b/CMake/Dependencies.cmake index a574210b2..40b50038e 100644 --- a/CMake/Dependencies.cmake +++ b/CMake/Dependencies.cmake @@ -13,7 +13,7 @@ else() endif() if(SUPPORTS_MPQ) - # bzip2 is a libmpq dependency. + # bzip2 is an mpqfs dependency. if(EMSCRIPTEN) emscripten_system_library("bzip2" BZip2::BZip2 USE_BZIP2=1) else() @@ -248,7 +248,15 @@ if(WIN32 AND NOT UWP_LIB) endif() if(SUPPORTS_MPQ) - add_subdirectory(3rdParty/libmpq) + set(MPQFS_BUILD_TESTS OFF CACHE BOOL "" FORCE) + if(USE_SDL1) + set(MPQFS_SDL_VERSION "1" CACHE STRING "" FORCE) + elseif(USE_SDL3) + set(MPQFS_SDL_VERSION "3" CACHE STRING "" FORCE) + else() + set(MPQFS_SDL_VERSION "2" CACHE STRING "" FORCE) + endif() + add_subdirectory(3rdParty/mpqfs) endif() add_subdirectory(3rdParty/tl) @@ -301,10 +309,6 @@ else() add_subdirectory(3rdParty/magic_enum) endif() -if(SUPPORTS_MPQ OR NOT NONET) - add_subdirectory(3rdParty/PKWare) -endif() - if(NOT NONET AND NOT DISABLE_TCP) add_subdirectory(3rdParty/asio) endif() diff --git a/CMake/platforms/n3ds.cmake b/CMake/platforms/n3ds.cmake index 21dfa48cb..cf39f02cf 100644 --- a/CMake/platforms/n3ds.cmake +++ b/CMake/platforms/n3ds.cmake @@ -8,7 +8,6 @@ set(DEVILUTIONX_SYSTEM_LIBFMT OFF) set(DEVILUTIONX_STATIC_LIBSODIUM ON) set(DEVILUTIONX_STATIC_LIBFMT ON) set(DISABLE_ZERO_TIER ON) -set(LIBMPQ_FILE_BUFFER_SIZE 32768) set(NOEXIT ON) # 3DS libraries and compile definitions diff --git a/CMake/platforms/xbox_nxdk.cmake b/CMake/platforms/xbox_nxdk.cmake index 9a0137566..43a3dd0c4 100644 --- a/CMake/platforms/xbox_nxdk.cmake +++ b/CMake/platforms/xbox_nxdk.cmake @@ -7,6 +7,12 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/xbox_nxdk/finders") set(DEVILUTIONX_SYSTEM_BZIP2 OFF) set(DEVILUTIONX_SYSTEM_LIBFMT OFF) +# Diablo/Hellfire MPQs only use PKWARE DCL implode, not zlib/bzip2. +# Disable these in mpqfs to avoid pulling in zlib (which fails to build +# from source on nxdk due to missing ). +set(MPQFS_USE_ZLIB OFF CACHE BOOL "" FORCE) +set(MPQFS_USE_BZIP2 OFF CACHE BOOL "" FORCE) + set(BUILD_ASSETS_MPQ OFF) set(DEVILUTIONX_ASSETS_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/pkg/assets") set(DEVILUTIONX_WINDOWS_NO_WCHAR ON) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 4501d6cc9..aeef1df35 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -589,7 +589,7 @@ if(SUPPORTS_MPQ) DevilutionX::SDL fmt::fmt tl - libmpq + mpqfs::mpqfs libdevilutionx_file_util libdevilutionx_logged_fstream libdevilutionx_pkware_encrypt @@ -640,7 +640,7 @@ if(SUPPORTS_MPQ OR NOT NONET) ) target_link_dependencies(libdevilutionx_pkware_encrypt PUBLIC DevilutionX::SDL - PKWare + mpqfs::mpqfs ) else() add_library(libdevilutionx_pkware_encrypt INTERFACE) diff --git a/Source/encrypt.cpp b/Source/encrypt.cpp index ca4b6fd41..9e820d080 100644 --- a/Source/encrypt.cpp +++ b/Source/encrypt.cpp @@ -3,114 +3,48 @@ * * Implementation of functions for compression and decompressing MPQ data. */ -#include -#include #include #include #include -#include +#include #include "encrypt.h" namespace devilution { -namespace { - -struct TDataInfo { - std::byte *srcData; - uint32_t srcOffset; - uint32_t srcSize; - std::byte *destData; - uint32_t destOffset; - size_t destSize; - bool error; -}; - -unsigned int PkwareBufferRead(char *buf, unsigned int *size, void *param) // NOLINT(readability-non-const-parameter) -{ - auto *pInfo = reinterpret_cast(param); - - uint32_t sSize; - if (*size >= pInfo->srcSize - pInfo->srcOffset) { - sSize = pInfo->srcSize - pInfo->srcOffset; - } else { - sSize = *size; - } - - memcpy(buf, pInfo->srcData + pInfo->srcOffset, sSize); - pInfo->srcOffset += sSize; - - return sSize; -} - -void PkwareBufferWrite(char *buf, unsigned int *size, void *param) // NOLINT(readability-non-const-parameter) -{ - auto *pInfo = reinterpret_cast(param); - - pInfo->error = pInfo->error || pInfo->destOffset + *size > pInfo->destSize; - if (pInfo->error) { - return; - } - - memcpy(pInfo->destData + pInfo->destOffset, buf, *size); - pInfo->destOffset += *size; -} - -} // namespace - uint32_t PkwareCompress(std::byte *srcData, uint32_t size) { - const std::unique_ptr ptr = std::make_unique(CMP_BUFFER_SIZE); + size_t dstCap = size * 2 + 64; + auto dst = std::make_unique(dstCap); + size_t dstSize = dstCap; - unsigned destSize = 2 * size; - if (destSize < 2 * 4096) - destSize = 2 * 4096; + int rc = mpqfs_pk_implode( + reinterpret_cast(srcData), size, + dst.get(), &dstSize, /*dict_bits=*/6); - const std::unique_ptr destData { new std::byte[destSize] }; - - TDataInfo param; - param.srcData = srcData; - param.srcOffset = 0; - param.srcSize = size; - param.destData = destData.get(); - param.destOffset = 0; - param.destSize = destSize; - param.error = false; - - unsigned type = 0; - unsigned dsize = 4096; - implode(PkwareBufferRead, PkwareBufferWrite, ptr.get(), ¶m, &type, &dsize); - - if (param.destOffset < size) { - memcpy(srcData, destData.get(), param.destOffset); - size = param.destOffset; + if (rc == 0 && dstSize < size) { + std::memcpy(srcData, dst.get(), dstSize); + return static_cast(dstSize); } + // Compression didn't help — return original size. return size; } uint32_t PkwareDecompress(std::byte *inBuff, uint32_t recvSize, size_t maxBytes) { - const std::unique_ptr ptr = std::make_unique(CMP_BUFFER_SIZE); - const std::unique_ptr outBuff { new std::byte[maxBytes] }; + auto out = std::make_unique(maxBytes); + size_t outSize = maxBytes; - TDataInfo info; - info.srcData = inBuff; - info.srcOffset = 0; - info.srcSize = recvSize; - info.destData = outBuff.get(); - info.destOffset = 0; - info.destSize = maxBytes; - info.error = false; - - explode(PkwareBufferRead, PkwareBufferWrite, ptr.get(), &info); - if (info.error) { + int rc = mpqfs_pk_explode( + reinterpret_cast(inBuff), recvSize, + out.get(), &outSize); + if (rc != 0) return 0; - } - memcpy(inBuff, outBuff.get(), info.destOffset); - return info.destOffset; + std::memcpy(inBuff, out.get(), outSize); + return static_cast(outSize); } } // namespace devilution diff --git a/Source/engine/assets.cpp b/Source/engine/assets.cpp index d51164e17..fa33b7cbb 100644 --- a/Source/engine/assets.cpp +++ b/Source/engine/assets.cpp @@ -65,13 +65,13 @@ SDL_IOStream *OpenOptionalRWops(const std::string &path) return SDL_IOFromFile(path.c_str(), "rb"); }; -bool FindMpqFile(std::string_view filename, MpqArchive **archive, uint32_t *fileNumber) +bool FindMpqFile(std::string_view filename, MpqArchive **archive, uint32_t *hashIndex) { - const MpqFileHash fileHash = CalculateMpqFileHash(filename); - for (auto &[_, mpqArchive] : MpqArchives) { - if (mpqArchive.GetFileNumber(fileHash, *fileNumber)) { + uint32_t hash = mpqArchive.FindHash(filename); + if (hash != UINT32_MAX) { *archive = &mpqArchive; + *hashIndex = hash; return true; } } @@ -155,7 +155,7 @@ AssetRef FindAsset(std::string_view filename) } // Look for the file in all the MPQ archives: - if (FindMpqFile(filename, &result.archive, &result.fileNumber)) { + if (FindMpqFile(filename, &result.archive, &result.hashIndex)) { result.filename = filename; return result; } @@ -185,7 +185,7 @@ AssetHandle OpenAsset(AssetRef &&ref, bool threadsafe) return AssetHandle { OpenFile(ref.path, "rb") }; #else if (ref.archive != nullptr) - return AssetHandle { SDL_RWops_FromMpqFile(*ref.archive, ref.fileNumber, ref.filename, threadsafe) }; + return AssetHandle { SDL_RWops_FromMpqFile(*ref.archive, ref.hashIndex, ref.filename, threadsafe) }; if (ref.directHandle != nullptr) { // Transfer handle ownership: auto *handle = ref.directHandle; @@ -316,7 +316,7 @@ bool LoadMPQ(std::span paths, std::string_view mpqName, int p return true; } if (error != 0) { - LogError("Error {}: {}", MpqArchive::ErrorMessage(error), mpqAbsPath); + LogError("Error {}: {}", MpqArchive::ErrorMessage(), mpqAbsPath); } } if (error == 0) { diff --git a/Source/engine/assets.hpp b/Source/engine/assets.hpp index be8d4167c..346d4596b 100644 --- a/Source/engine/assets.hpp +++ b/Source/engine/assets.hpp @@ -118,7 +118,7 @@ struct AssetHandle { struct AssetRef { // An MPQ file reference: MpqArchive *archive = nullptr; - uint32_t fileNumber; + uint32_t hashIndex = UINT32_MAX; std::string_view filename; // Alternatively, a direct SDL_IOStream handle: @@ -128,7 +128,7 @@ struct AssetRef { AssetRef(AssetRef &&other) noexcept : archive(other.archive) - , fileNumber(other.fileNumber) + , hashIndex(other.hashIndex) , filename(other.filename) , directHandle(other.directHandle) { @@ -139,7 +139,7 @@ struct AssetRef { { closeDirectHandle(); archive = other.archive; - fileNumber = other.fileNumber; + hashIndex = other.hashIndex; filename = other.filename; directHandle = other.directHandle; other.directHandle = nullptr; @@ -165,8 +165,9 @@ struct AssetRef { [[nodiscard]] size_t size() const { if (archive != nullptr) { - int32_t error; - return archive->GetUnpackedFileSize(fileNumber, error); + if (hashIndex != UINT32_MAX) + return archive->GetFileSizeFromHash(hashIndex); + return archive->GetFileSize(filename); } return static_cast(SDL_GetIOSize(directHandle)); } diff --git a/Source/init.cpp b/Source/init.cpp index d2acda710..1d73960b9 100644 --- a/Source/init.cpp +++ b/Source/init.cpp @@ -37,7 +37,6 @@ #include "utils/utf8.hpp" #ifndef UNPACKED_MPQS -#include "mpq/mpq_common.hpp" #include "mpq/mpq_reader.hpp" #endif @@ -99,13 +98,10 @@ bool AreExtraFontsOutOfDate(std::string_view path) bool AreExtraFontsOutOfDate(MpqArchive &archive) { const char filename[] = "fonts\\VERSION"; - const MpqFileHash fileHash = CalculateMpqFileHash(filename); - uint32_t fileNumber; - if (!archive.GetFileNumber(fileHash, fileNumber)) + if (!archive.HasFile(filename)) return true; AssetRef ref; ref.archive = &archive; - ref.fileNumber = fileNumber; ref.filename = filename; return CheckExtraFontsVersion(std::move(ref)); } diff --git a/Source/mpq/mpq_common.cpp b/Source/mpq/mpq_common.cpp index 9a11a1179..0fc738f16 100644 --- a/Source/mpq/mpq_common.cpp +++ b/Source/mpq/mpq_common.cpp @@ -2,7 +2,7 @@ #include -#include +#include namespace devilution { @@ -10,9 +10,9 @@ namespace devilution { MpqFileHash CalculateMpqFileHash(std::string_view filename) { MpqFileHash fileHash; - libmpq__file_hash_s(filename.data(), filename.size(), &fileHash[0], &fileHash[1], &fileHash[2]); + mpqfs_file_hash_s(filename.data(), filename.size(), &fileHash[0], &fileHash[1], &fileHash[2]); return fileHash; } #endif -} // namespace devilution +} // namespace devilution \ No newline at end of file diff --git a/Source/mpq/mpq_reader.cpp b/Source/mpq/mpq_reader.cpp index b1603632d..3949b54a1 100644 --- a/Source/mpq/mpq_reader.cpp +++ b/Source/mpq/mpq_reader.cpp @@ -1,149 +1,145 @@ #include "mpq/mpq_reader.hpp" -#include -#include -#include -#include +#include +#include -#include +#include + +#include "utils/file_util.h" namespace devilution { -std::optional MpqArchive::Open(const char *path, int32_t &error) +// Helper: NUL-terminate a string_view into a stack buffer. +// Returns false if the name doesn't fit. +static bool CopyToPathBuf(std::string_view sv, char *buf, size_t bufSize) { - mpq_archive_s *archive; - error = libmpq__archive_open(&archive, path, -1); - if (error != 0) { - if (error == LIBMPQ_ERROR_EXIST) - error = 0; - return std::nullopt; - } - return MpqArchive { std::string(path), archive }; + if (sv.size() >= bufSize) return false; + std::memcpy(buf, sv.data(), sv.size()); + buf[sv.size()] = '\0'; + return true; } -std::optional MpqArchive::Clone(int32_t &error) +MpqArchive::MpqArchive(std::string path, mpqfs_archive_t *archive) + : path_(std::move(path)) + , archive_(archive) { - mpq_archive_s *copy; - error = libmpq__archive_dup(archive_, path_.c_str(), ©); - if (error != 0) - return std::nullopt; - return MpqArchive { path_, copy }; } -const char *MpqArchive::ErrorMessage(int32_t errorCode) +MpqArchive::MpqArchive(MpqArchive &&other) noexcept + : path_(std::move(other.path_)) + , archive_(other.archive_) { - return libmpq__strerror(errorCode); + other.archive_ = nullptr; } MpqArchive &MpqArchive::operator=(MpqArchive &&other) noexcept { - path_ = std::move(other.path_); - if (archive_ != nullptr) - libmpq__archive_close(archive_); - archive_ = other.archive_; - other.archive_ = nullptr; - tmp_buf_ = std::move(other.tmp_buf_); + if (this != &other) { + mpqfs_close(archive_); + path_ = std::move(other.path_); + archive_ = other.archive_; + other.archive_ = nullptr; + } return *this; } MpqArchive::~MpqArchive() { - if (archive_ != nullptr) - libmpq__archive_close(archive_); + mpqfs_close(archive_); } -bool MpqArchive::GetFileNumber(MpqFileHash fileHash, uint32_t &fileNumber) +std::optional MpqArchive::Open(const char *path, int32_t &error) { - return libmpq__file_number_from_hash(archive_, fileHash[0], fileHash[1], fileHash[2], &fileNumber) == 0; + if (!FileExists(path)) { + error = 0; + return std::nullopt; + } + mpqfs_archive_t *handle = mpqfs_open(path); + if (!handle) { + error = -1; + return std::nullopt; + } + error = 0; + return MpqArchive(path, handle); } -std::unique_ptr MpqArchive::ReadFile(std::string_view filename, std::size_t &fileSize, int32_t &error) +std::optional MpqArchive::Clone(int32_t &error) { - std::unique_ptr result; - std::uint32_t fileNumber; - error = libmpq__file_number_s(archive_, filename.data(), filename.size(), &fileNumber); - if (error != 0) - return result; - - libmpq__off_t unpackedSize; - error = libmpq__file_size_unpacked(archive_, fileNumber, &unpackedSize); - if (error != 0) - return result; - - error = OpenBlockOffsetTable(fileNumber, filename); - if (error != 0) - return result; - - result = std::make_unique(static_cast(unpackedSize)); - - const std::size_t blockSize = GetBlockSize(fileNumber, 0, error); - if (error != 0) - return result; - - std::vector &tmp = GetTemporaryBuffer(blockSize); - if (error != 0) - return result; - - error = libmpq__file_read_with_filename_and_temporary_buffer_s( - archive_, fileNumber, filename.data(), filename.size(), reinterpret_cast(result.get()), unpackedSize, - tmp.data(), static_cast(blockSize), nullptr); - if (error != 0) { - result = nullptr; - CloseBlockOffsetTable(fileNumber); - return result; + mpqfs_archive_t *clone = mpqfs_clone(archive_); + if (!clone) { + error = -1; + return std::nullopt; } - CloseBlockOffsetTable(fileNumber); - - fileSize = static_cast(unpackedSize); - return result; + error = 0; + return MpqArchive(path_, clone); } -int32_t MpqArchive::ReadBlock(uint32_t fileNumber, uint32_t blockNumber, uint8_t *out, size_t outSize) +const char *MpqArchive::ErrorMessage() { - std::vector &tmpBuf = GetTemporaryBuffer(outSize); - return libmpq__block_read_with_temporary_buffer( - archive_, fileNumber, blockNumber, out, static_cast(outSize), - tmpBuf.data(), outSize, - /*transferred=*/nullptr); + const char *msg = mpqfs_last_error(); + return msg ? msg : "Unknown error"; } -std::size_t MpqArchive::GetUnpackedFileSize(uint32_t fileNumber, int32_t &error) +bool MpqArchive::HasFile(std::string_view filename) const { - libmpq__off_t unpackedSize; - error = libmpq__file_size_unpacked(archive_, fileNumber, &unpackedSize); - return static_cast(unpackedSize); + char buf[256]; + if (!CopyToPathBuf(filename, buf, sizeof(buf))) + return false; + return mpqfs_has_file(archive_, buf); } -uint32_t MpqArchive::GetNumBlocks(uint32_t fileNumber, int32_t &error) +size_t MpqArchive::GetFileSize(std::string_view filename) const { - uint32_t numBlocks; - error = libmpq__file_blocks(archive_, fileNumber, &numBlocks); - return numBlocks; + char buf[256]; + if (!CopyToPathBuf(filename, buf, sizeof(buf))) + return 0; + return mpqfs_file_size(archive_, buf); } -int32_t MpqArchive::OpenBlockOffsetTable(uint32_t fileNumber, std::string_view filename) +uint32_t MpqArchive::FindHash(std::string_view filename) const { - return libmpq__block_open_offset_with_filename_s(archive_, fileNumber, filename.data(), filename.size()); + char buf[256]; + if (!CopyToPathBuf(filename, buf, sizeof(buf))) + return UINT32_MAX; + return mpqfs_find_hash(archive_, buf); } -int32_t MpqArchive::CloseBlockOffsetTable(uint32_t fileNumber) +bool MpqArchive::HasFileHash(uint32_t hash) const { - return libmpq__block_close_offset(archive_, fileNumber); + return mpqfs_has_file_hash(archive_, hash); } -// Requires the block offset table to be open -std::size_t MpqArchive::GetBlockSize(uint32_t fileNumber, uint32_t blockNumber, int32_t &error) +size_t MpqArchive::GetFileSizeFromHash(uint32_t hash) const { - libmpq__off_t blockSize; - error = libmpq__block_size_unpacked(archive_, fileNumber, blockNumber, &blockSize); - return static_cast(blockSize); + return mpqfs_file_size_from_hash(archive_, hash); } -bool MpqArchive::HasFile(std::string_view filename) const +std::unique_ptr MpqArchive::ReadFile( + std::string_view filename, std::size_t &fileSize, int32_t &error) { - std::uint32_t fileNumber; - const int32_t error = libmpq__file_number_s(archive_, filename.data(), filename.size(), &fileNumber); - return error == 0; + char buf[256]; + if (!CopyToPathBuf(filename, buf, sizeof(buf))) { + error = -1; + return nullptr; + } + + const size_t size = mpqfs_file_size(archive_, buf); + if (size == 0) { + error = -1; + return nullptr; + } + + auto result = std::make_unique(size); + const size_t read = mpqfs_read_file_into(archive_, buf, + result.get(), size); + if (read == 0) { + error = -1; + return nullptr; + } + + error = 0; + fileSize = read; + return result; } } // namespace devilution diff --git a/Source/mpq/mpq_reader.hpp b/Source/mpq/mpq_reader.hpp index d2fd78a43..c56dfaddb 100644 --- a/Source/mpq/mpq_reader.hpp +++ b/Source/mpq/mpq_reader.hpp @@ -6,76 +6,46 @@ #include #include #include -#include -#include -#include "mpq/mpq_common.hpp" - -// Forward-declare so that we can avoid exposing libmpq. -struct mpq_archive; -using mpq_archive_s = struct mpq_archive; +// Forward-declare so that we can avoid exposing mpqfs.h to all consumers. +struct mpqfs_archive; +typedef struct mpqfs_archive mpqfs_archive_t; namespace devilution { class MpqArchive { public: - // If the file does not exist, returns nullopt without an error. static std::optional Open(const char *path, int32_t &error); - std::optional Clone(int32_t &error); + static const char *ErrorMessage(); - static const char *ErrorMessage(int32_t errorCode); - - MpqArchive(MpqArchive &&other) noexcept - : path_(std::move(other.path_)) - , archive_(other.archive_) - , tmp_buf_(std::move(other.tmp_buf_)) - { - other.archive_ = nullptr; - } - + MpqArchive(MpqArchive &&other) noexcept; MpqArchive &operator=(MpqArchive &&other) noexcept; - ~MpqArchive(); - // Returns false if the file does not exit. - bool GetFileNumber(MpqFileHash fileHash, uint32_t &fileNumber); - - std::unique_ptr ReadFile(std::string_view filename, std::size_t &fileSize, int32_t &error); + MpqArchive(const MpqArchive &) = delete; + MpqArchive &operator=(const MpqArchive &) = delete; - // Returns error code. - int32_t ReadBlock(uint32_t fileNumber, uint32_t blockNumber, uint8_t *out, size_t outSize); - - std::size_t GetUnpackedFileSize(uint32_t fileNumber, int32_t &error); - - uint32_t GetNumBlocks(uint32_t fileNumber, int32_t &error); - - int32_t OpenBlockOffsetTable(uint32_t fileNumber, std::string_view filename); + bool HasFile(std::string_view filename) const; + size_t GetFileSize(std::string_view filename) const; - int32_t CloseBlockOffsetTable(uint32_t fileNumber); + // Hash-based lookup: resolve once, reuse the index. + uint32_t FindHash(std::string_view filename) const; + bool HasFileHash(uint32_t hash) const; + size_t GetFileSizeFromHash(uint32_t hash) const; - // Requires the block offset table to be open - std::size_t GetBlockSize(uint32_t fileNumber, uint32_t blockNumber, int32_t &error); + std::unique_ptr ReadFile( + std::string_view filename, + std::size_t &fileSize, + int32_t &error); - bool HasFile(std::string_view filename) const; + mpqfs_archive_t *handle() const { return archive_; } private: - MpqArchive(std::string path, mpq_archive_s *archive) - : path_(std::move(path)) - , archive_(archive) - { - } - - std::vector &GetTemporaryBuffer(std::size_t size) - { - if (tmp_buf_.size() < size) - tmp_buf_.resize(size); - return tmp_buf_; - } + MpqArchive(std::string path, mpqfs_archive_t *archive); std::string path_; - mpq_archive_s *archive_; - std::vector tmp_buf_; + mpqfs_archive_t *archive_ = nullptr; }; } // namespace devilution diff --git a/Source/mpq/mpq_sdl_rwops.cpp b/Source/mpq/mpq_sdl_rwops.cpp index cc135d6e0..63fc71a12 100644 --- a/Source/mpq/mpq_sdl_rwops.cpp +++ b/Source/mpq/mpq_sdl_rwops.cpp @@ -1,295 +1,60 @@ #include "mpq/mpq_sdl_rwops.hpp" -#include #include -#include -#include -#include -#ifdef USE_SDL3 -#include -#else -#include - -#include "utils/sdl_compat.h" -#endif +#include namespace devilution { namespace { +constexpr size_t MaxMpqPathSize = 256; +} // namespace -struct Data { - // File information: - std::optional ownedArchive; - MpqArchive *mpqArchive; - uint32_t fileNumber; - size_t blockSize; - size_t lastBlockSize; - uint32_t numBlocks; - size_t size; - - // State: - size_t position; - bool blockRead; - std::unique_ptr blockData; -}; - -#ifdef USE_SDL3 -Data *GetData(void *userdata) { return reinterpret_cast(userdata); } -#else -Data *GetData(struct SDL_RWops *context) -{ - return reinterpret_cast(context->hidden.unknown.data1); -} - -void SetData(struct SDL_RWops *context, Data *data) -{ - context->hidden.unknown.data1 = data; -} -#endif - -#ifndef USE_SDL1 -using OffsetType = Sint64; -using SizeType = size_t; -#else -using OffsetType = int; -using SizeType = int; -#endif - -extern "C" { - -#ifndef USE_SDL1 -static Sint64 MpqFileRwSize( -#ifdef USE_SDL3 - void * -#else - struct SDL_RWops * -#endif - context) -{ - return static_cast(GetData(context)->size); -} -#endif - -#ifdef USE_SDL3 -static Sint64 MpqFileRwSeek(void *context, Sint64 offset, SDL_IOWhence whence) -#else -static OffsetType MpqFileRwSeek(struct SDL_RWops *context, OffsetType offset, int whence) -#endif +SdlRwopsType *SDL_RWops_FromMpqFile(MpqArchive &archive, + uint32_t hashIndex, + std::string_view filename, + bool threadsafe) { - Data &data = *GetData(context); - OffsetType newPosition; - switch (whence) { - case SDL_IO_SEEK_SET: - newPosition = offset; - break; - case SDL_IO_SEEK_CUR: - newPosition = static_cast(data.position + offset); - break; - case SDL_IO_SEEK_END: - newPosition = static_cast(data.size + offset); - break; - default: - return -1; - } - - if (newPosition == static_cast(data.position)) - return newPosition; - - if (newPosition > static_cast(data.size)) { - SDL_SetError("MpqFileRwSeek beyond EOF (%d > %u)", static_cast(newPosition), static_cast(data.size)); - return -1; - } - - if (newPosition < 0) { - SDL_SetError("MpqFileRwSeek beyond BOF (%d < 0)", static_cast(newPosition)); - return -1; - } - - if (data.position / data.blockSize != static_cast(newPosition) / data.blockSize) - data.blockRead = false; - - data.position = static_cast(newPosition); - - return newPosition; -} + char pathBuf[MaxMpqPathSize]; + if (filename.size() >= sizeof(pathBuf)) + return nullptr; + std::memcpy(pathBuf, filename.data(), filename.size()); + pathBuf[filename.size()] = '\0'; + if (threadsafe) { + if (hashIndex != UINT32_MAX) { #ifdef USE_SDL3 -static SizeType MpqFileRwRead(void *context, void *ptr, size_t size, SDL_IOStatus *status) + SdlRwopsType *result = mpqfs_open_io_threadsafe_from_hash(archive.handle(), hashIndex); #else -static SizeType MpqFileRwRead(struct SDL_RWops *context, void *ptr, SizeType size, SizeType maxnum) + SdlRwopsType *result = mpqfs_open_rwops_threadsafe_from_hash(archive.handle(), hashIndex); #endif -{ -#ifdef USE_SDL3 - const size_t maxnum = 1; -#endif - Data &data = *GetData(context); - const size_t totalSize = size * maxnum; - size_t remainingSize = totalSize; - - auto *out = static_cast(ptr); - - if (data.blockData == nullptr) { - data.blockData = std::unique_ptr { new uint8_t[data.blockSize] }; - } - - auto blockNumber = static_cast(data.position / data.blockSize); - while (remainingSize > 0) { - if (data.position == data.size) { -#ifdef USE_SDL3 - *status = SDL_IO_STATUS_EOF; -#endif - break; + if (result != nullptr) + return result; } - - const size_t currentBlockSize = blockNumber + 1 == data.numBlocks ? data.lastBlockSize : data.blockSize; - - if (!data.blockRead) { - const int32_t error = data.mpqArchive->ReadBlock(data.fileNumber, blockNumber, data.blockData.get(), currentBlockSize); - if (error != 0) { - SDL_SetError("MpqFileRwRead ReadBlock: %s", MpqArchive::ErrorMessage(error)); - return 0; - } - data.blockRead = true; - } - - const size_t blockPosition = data.position - (blockNumber * data.blockSize); - const size_t remainingBlockSize = currentBlockSize - blockPosition; - - if (remainingSize < remainingBlockSize) { - std::memcpy(out, data.blockData.get() + blockPosition, remainingSize); - data.position += remainingSize; #ifdef USE_SDL3 - return size; + return mpqfs_open_io_threadsafe(archive.handle(), pathBuf); #else - return maxnum; + return mpqfs_open_rwops_threadsafe(archive.handle(), pathBuf); #endif - } - - std::memcpy(out, data.blockData.get() + blockPosition, remainingBlockSize); - out += remainingBlockSize; - data.position += remainingBlockSize; - remainingSize -= remainingBlockSize; - ++blockNumber; - data.blockRead = false; } + // Non-threadsafe path: use hash-based open if available to avoid re-hashing. + // This may fail for encrypted files (which need the filename for key derivation), + // so we fall back to filename-based open on failure. + if (hashIndex != UINT32_MAX) { #ifdef USE_SDL3 - return static_cast(totalSize - remainingSize); -#else - return static_cast((totalSize - remainingSize) / size); -#endif -} - -#ifdef USE_SDL3 -static bool MpqFileRwClose(void *context) -#else -static int MpqFileRwClose(struct SDL_RWops *context) -#endif -{ - Data *data = GetData(context); - data->mpqArchive->CloseBlockOffsetTable(data->fileNumber); - delete data; -#ifdef USE_SDL3 - return true; -#else - delete context; - return 0; -#endif -} - -} // extern "C" - -} // namespace - -SDL_IOStream *SDL_RWops_FromMpqFile(MpqArchive &mpqArchive, uint32_t fileNumber, std::string_view filename, bool threadsafe) -{ -#ifdef USE_SDL3 - SDL_IOStreamInterface interface; - SDL_INIT_INTERFACE(&interface); - SDL_IOStreamInterface *result = &interface; + SdlRwopsType *result = mpqfs_open_io_from_hash(archive.handle(), hashIndex); #else - auto result = std::make_unique(); - std::memset(result.get(), 0, sizeof(*result)); -#endif - -#ifndef USE_SDL1 - result->size = &MpqFileRwSize; -#ifndef USE_SDL3 - result->type = SDL_RWOPS_UNKNOWN; + SdlRwopsType *result = mpqfs_open_rwops_from_hash(archive.handle(), hashIndex); #endif -#else - result->type = 0; -#endif - - result->seek = &MpqFileRwSeek; - result->read = &MpqFileRwRead; - result->write = nullptr; - result->close = &MpqFileRwClose; -#ifdef USE_SDL3 - result->flush = nullptr; -#endif - - auto data = std::make_unique(); - int32_t error = 0; - - if (threadsafe) { - data->ownedArchive = mpqArchive.Clone(error); - if (error != 0) { - SDL_SetError("MpqFileRwRead Clone: %s", MpqArchive::ErrorMessage(error)); - return nullptr; - } - data->mpqArchive = &*data->ownedArchive; - } else { - data->mpqArchive = &mpqArchive; - } - data->fileNumber = fileNumber; - MpqArchive &archive = *data->mpqArchive; - - error = archive.OpenBlockOffsetTable(fileNumber, filename); - if (error != 0) { - SDL_SetError("MpqFileRwRead OpenBlockOffsetTable: %s", MpqArchive::ErrorMessage(error)); - return nullptr; + if (result != nullptr) + return result; } - data->size = archive.GetUnpackedFileSize(fileNumber, error); - if (error != 0) { - SDL_SetError("MpqFileRwRead GetUnpackedFileSize: %s", MpqArchive::ErrorMessage(error)); - return nullptr; - } - - const std::uint32_t numBlocks = archive.GetNumBlocks(fileNumber, error); - if (error != 0) { - SDL_SetError("MpqFileRwRead GetNumBlocks: %s", MpqArchive::ErrorMessage(error)); - return nullptr; - } - data->numBlocks = numBlocks; - - const size_t blockSize = archive.GetBlockSize(fileNumber, 0, error); - if (error != 0) { - SDL_SetError("MpqFileRwRead GetBlockSize: %s", MpqArchive::ErrorMessage(error)); - return nullptr; - } - data->blockSize = blockSize; - - if (numBlocks > 1) { - data->lastBlockSize = archive.GetBlockSize(fileNumber, numBlocks - 1, error); - if (error != 0) { - SDL_SetError("MpqFileRwRead GetBlockSize: %s", MpqArchive::ErrorMessage(error)); - return nullptr; - } - } else { - data->lastBlockSize = blockSize; - } - - data->position = 0; - data->blockRead = false; - #ifdef USE_SDL3 - return SDL_OpenIO(&interface, data.release()); + return mpqfs_open_io(archive.handle(), pathBuf); #else - SetData(result.get(), data.release()); - return result.release(); + return mpqfs_open_rwops(archive.handle(), pathBuf); #endif } diff --git a/Source/mpq/mpq_sdl_rwops.hpp b/Source/mpq/mpq_sdl_rwops.hpp index 07ba794c0..2a1aee118 100644 --- a/Source/mpq/mpq_sdl_rwops.hpp +++ b/Source/mpq/mpq_sdl_rwops.hpp @@ -1,20 +1,23 @@ #pragma once -#include #include +#include "mpq/mpq_reader.hpp" + +// Forward-declare the SDL type for the active SDL version. #ifdef USE_SDL3 -#include +struct SDL_IOStream; +using SdlRwopsType = SDL_IOStream; #else -#include - -#include "utils/sdl_compat.h" +struct SDL_RWops; +using SdlRwopsType = SDL_RWops; #endif -#include "mpq/mpq_reader.hpp" - namespace devilution { -SDL_IOStream *SDL_RWops_FromMpqFile(MpqArchive &mpqArchive, uint32_t fileNumber, std::string_view filename, bool threadsafe); +SdlRwopsType *SDL_RWops_FromMpqFile(MpqArchive &archive, + uint32_t hashIndex, + std::string_view filename, + bool threadsafe); } // namespace devilution diff --git a/Source/mpq/mpq_writer.cpp b/Source/mpq/mpq_writer.cpp index 1ba4c5cb2..69a61fb5a 100644 --- a/Source/mpq/mpq_writer.cpp +++ b/Source/mpq/mpq_writer.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include "appfat.h" #include "encrypt.h" @@ -140,7 +140,7 @@ MpqWriter::MpqWriter(const char *path) error = "Failed to read block table"; goto on_error; } - libmpq__decrypt_block(reinterpret_cast(blockTable_.get()), fhdr.blockEntriesCount * sizeof(MpqBlockEntry), LIBMPQ_BLOCK_TABLE_HASH_KEY); + mpqfs_decrypt_block(reinterpret_cast(blockTable_.get()), fhdr.blockEntriesCount * sizeof(MpqBlockEntry) / sizeof(uint32_t), MPQFS_BLOCK_TABLE_KEY); } hashTable_ = std::make_unique(HashEntriesCount); @@ -152,7 +152,7 @@ MpqWriter::MpqWriter(const char *path) error = "Failed to read hash entries"; goto on_error; } - libmpq__decrypt_block(reinterpret_cast(hashTable_.get()), fhdr.hashEntriesCount * sizeof(MpqHashEntry), LIBMPQ_HASH_TABLE_HASH_KEY); + mpqfs_decrypt_block(reinterpret_cast(hashTable_.get()), fhdr.hashEntriesCount * sizeof(MpqHashEntry) / sizeof(uint32_t), MPQFS_HASH_TABLE_KEY); } #ifndef CAN_SEEKP_BEYOND_EOF @@ -469,17 +469,17 @@ bool MpqWriter::WriteHeader() bool MpqWriter::WriteBlockTable() { - libmpq__encrypt_block(reinterpret_cast(blockTable_.get()), BlockEntrySize, LIBMPQ_BLOCK_TABLE_HASH_KEY); + mpqfs_encrypt_block(reinterpret_cast(blockTable_.get()), BlockEntrySize / sizeof(uint32_t), MPQFS_BLOCK_TABLE_KEY); const bool success = stream_.Write(reinterpret_cast(blockTable_.get()), BlockEntrySize); - libmpq__decrypt_block(reinterpret_cast(blockTable_.get()), BlockEntrySize, LIBMPQ_BLOCK_TABLE_HASH_KEY); + mpqfs_decrypt_block(reinterpret_cast(blockTable_.get()), BlockEntrySize / sizeof(uint32_t), MPQFS_BLOCK_TABLE_KEY); return success; } bool MpqWriter::WriteHashTable() { - libmpq__encrypt_block(reinterpret_cast(hashTable_.get()), HashEntrySize, LIBMPQ_HASH_TABLE_HASH_KEY); + mpqfs_encrypt_block(reinterpret_cast(hashTable_.get()), HashEntrySize / sizeof(uint32_t), MPQFS_HASH_TABLE_KEY); const bool success = stream_.Write(reinterpret_cast(hashTable_.get()), HashEntrySize); - libmpq__decrypt_block(reinterpret_cast(hashTable_.get()), HashEntrySize, LIBMPQ_HASH_TABLE_HASH_KEY); + mpqfs_decrypt_block(reinterpret_cast(hashTable_.get()), HashEntrySize / sizeof(uint32_t), MPQFS_HASH_TABLE_KEY); return success; } diff --git a/test/writehero_test.cpp b/test/writehero_test.cpp index 892f7e18d..e748c14e7 100644 --- a/test/writehero_test.cpp +++ b/test/writehero_test.cpp @@ -413,7 +413,7 @@ TEST(Writehero, pfile_write_hero) std::vector s(picosha2::k_digest_size); picosha2::hash256(data.get(), data.get() + size, s.begin(), s.end()); EXPECT_EQ(picosha2::bytes_to_hex_string(s.begin(), s.end()), - "a79367caae6192d54703168d82e0316aa289b2a33251255fad8abe34889c1d3a"); + "4dffacf96d5aab89c07c38de54c5b90006c6379fdaf90785885872d8edccec40"); } } // namespace diff --git a/uwp-project/devilutionx.vcxproj b/uwp-project/devilutionx.vcxproj index a75ace99d..40a48f4c9 100644 --- a/uwp-project/devilutionx.vcxproj +++ b/uwp-project/devilutionx.vcxproj @@ -73,8 +73,8 @@ - sdl_image.lib;libpng16_staticd.lib;pkware.lib;fmtd.lib;zlibstatic.lib;bzip2.lib;libsmackerdec.lib;libmpq.lib;libdevilutionx.lib;lua_static.lib;sdl2.lib;sdl_audiolib.lib;asio.lib;sodium.lib;zt.lib;lwip_pic.lib;miniupnpc_pic.lib;natpmp_pic.lib;zt_pic.lib;zto_pic.lib;shlwapi.lib;shell32.lib;%(AdditionalDependencies) - ..\build\SDL\VisualC-WinRT\x64\Debug\SDL-UWP;..\build\3rdParty\SDL_image\Debug;..\build\_deps\zlib-build\Debug;..\build\3rdParty\PKWare\Debug;..\build\3rdParty\bzip2\Debug;..\build\3rdParty\libsmackerdec\Debug;..\build\3rdParty\libmpq\Debug;..\build\_deps\lua-build\lua-5.4.7\Debug;..\build\_deps\sdl_audiolib-build\Debug;..\build\3rdParty\asio\Debug;..\build\_deps\libsodium-build\Debug;..\build\_deps\libzt-build\lib\Debug;..\build\_deps\libfmt-build\Debug;..\build\_deps\libpng-build\Debug;..\build\Source\libdevilutionx.dir\Debug;%(AdditionalLibraryDirectories) + sdl_image.lib;libpng16_staticd.lib;mpqfs.lib;fmtd.lib;zlibstatic.lib;bzip2.lib;libsmackerdec.lib;libdevilutionx.lib;lua_static.lib;sdl2.lib;sdl_audiolib.lib;asio.lib;sodium.lib;zt.lib;lwip_pic.lib;miniupnpc_pic.lib;natpmp_pic.lib;zt_pic.lib;zto_pic.lib;shlwapi.lib;shell32.lib;%(AdditionalDependencies) + ..\build\SDL\VisualC-WinRT\x64\Debug\SDL-UWP;..\build\3rdParty\SDL_image\Debug;..\build\_deps\zlib-build\Debug;..\build\3rdParty\bzip2\Debug;..\build\3rdParty\libsmackerdec\Debug;..\build\_deps\mpqfs-build\Debug;..\build\_deps\lua-build\lua-5.4.7\Debug;..\build\_deps\sdl_audiolib-build\Debug;..\build\3rdParty\asio\Debug;..\build\_deps\libsodium-build\Debug;..\build\_deps\libzt-build\lib\Debug;..\build\_deps\libfmt-build\Debug;..\build\_deps\libpng-build\Debug;..\build\Source\libdevilutionx.dir\Debug;%(AdditionalLibraryDirectories) pch.h @@ -92,8 +92,8 @@ - sdl_image.lib;libpng16_static.lib;pkware.lib;fmt.lib;zlibstatic.lib;bzip2.lib;libsmackerdec.lib;libmpq.lib;libdevilutionx.lib;lua_static.lib;sdl2.lib;sdl_audiolib.lib;sheenbidi.lib;asio.lib;sodium.lib;zt.lib;lwip_pic.lib;miniupnpc_pic.lib;natpmp_pic.lib;zt_pic.lib;zto_pic.lib;shlwapi.lib;shell32.lib;%(AdditionalDependencies) - ..\build\SDL\VisualC-WinRT\x64\Release\SDL-UWP;..\build\3rdParty\SDL_image\Release;..\build\_deps\zlib-build\Release;..\build\3rdParty\PKWare\Release;..\build\3rdParty\bzip2\Release;..\build\3rdParty\libsmackerdec\Release;..\build\3rdParty\libmpq\Release;..\build\_deps\lua-build\lua-5.4.7\Release;..\build\_deps\sdl_audiolib-build\Release;..\build\3rdParty\asio\Release;..\build\_deps\libsodium-build\Release;..\build\_deps\libzt-build\lib\Release;..\build\_deps\libfmt-build\Release;..\build\_deps\libpng-build\Release;..\build\_deps\sheenbidi-build\Release;..\build\Source\libdevilutionx.dir\Release;%(AdditionalLibraryDirectories) + sdl_image.lib;libpng16_static.lib;mpqfs.lib;fmt.lib;zlibstatic.lib;bzip2.lib;libsmackerdec.lib;libdevilutionx.lib;lua_static.lib;sdl2.lib;sdl_audiolib.lib;sheenbidi.lib;asio.lib;sodium.lib;zt.lib;lwip_pic.lib;miniupnpc_pic.lib;natpmp_pic.lib;zt_pic.lib;zto_pic.lib;shlwapi.lib;shell32.lib;%(AdditionalDependencies) + ..\build\SDL\VisualC-WinRT\x64\Release\SDL-UWP;..\build\3rdParty\SDL_image\Release;..\build\_deps\zlib-build\Release;..\build\3rdParty\bzip2\Release;..\build\3rdParty\libsmackerdec\Release;..\build\_deps\mpqfs-build\Release;..\build\_deps\lua-build\lua-5.4.7\Release;..\build\_deps\sdl_audiolib-build\Release;..\build\3rdParty\asio\Release;..\build\_deps\libsodium-build\Release;..\build\_deps\libzt-build\lib\Release;..\build\_deps\libfmt-build\Release;..\build\_deps\libpng-build\Release;..\build\_deps\sheenbidi-build\Release;..\build\Source\libdevilutionx.dir\Release;%(AdditionalLibraryDirectories) pch.h