mirror of
https://github.com/mangosfour/server.git
synced 2025-12-12 10:37:03 +00:00
[Build] Larger build system update
FIXED: Core now builds under cmake for Windows TODO: Map and mmap gen currently does not build TODO: More sync work is required for Eluna, thus Eluna is temporarily disabled This has only been tested under Windows with CMAKE and VS
This commit is contained in:
parent
c2f9316581
commit
13292befd6
640 changed files with 100354 additions and 29918 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,3 +1,6 @@
|
||||||
[submodule "src/realmd"]
|
[submodule "src/realmd"]
|
||||||
path = src/realmd
|
path = src/realmd
|
||||||
url = https://github.com/mangos/realmd.git
|
url = https://github.com/mangos/realmd.git
|
||||||
|
[submodule "src/modules/Eluna"]
|
||||||
|
path = src/modules/Eluna
|
||||||
|
url = https://github.com/ElunaLuaEngine/Eluna.git
|
||||||
|
|
|
||||||
291
CMakeLists.txt
291
CMakeLists.txt
|
|
@ -1,5 +1,5 @@
|
||||||
#
|
#
|
||||||
# This file is part of the MaNGOS Project. See AUTHORS file for Copyright information
|
# This code is part of MaNGOS. Contributor & Copyright details are in AUTHORS/THANKS.
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
|
@ -16,45 +16,65 @@
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# CMake policies
|
||||||
cmake_minimum_required(VERSION 2.8)
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
# Until CMake 3.0 is the standard
|
||||||
|
# And a solution to set_directory_properties is found.
|
||||||
|
if(POLICY CMP0043)
|
||||||
|
cmake_policy(SET CMP0043 OLD)
|
||||||
|
endif()
|
||||||
|
|
||||||
project(MaNGOS)
|
project(MaNGOS)
|
||||||
set(MANGOS_VERSION 0.20)
|
set(MANGOS_VERSION 0.20)
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH
|
set(CMAKE_MODULE_PATH
|
||||||
${CMAKE_MODULE_PATH}
|
"${CMAKE_MODULE_PATH}"
|
||||||
${CMAKE_SOURCE_DIR}/cmake
|
"${CMAKE_SOURCE_DIR}/cmake"
|
||||||
)
|
)
|
||||||
|
|
||||||
# define all options here
|
# define all options here
|
||||||
option(ACE_USE_EXTERNAL "Use external ACE" OFF)
|
if(WIN32)
|
||||||
if(PCHSupport_FOUND AND WIN32) # TODO: why only enable it on windows by default?
|
set(CONF_DIR "" CACHE STRING "Config path. Can be absolute or relative")
|
||||||
option(PCH "Use precompiled headers" ON)
|
|
||||||
else()
|
else()
|
||||||
option(PCH "Use precompiled headers" OFF)
|
set(CONF_DIR "etc/" CACHE STRING "Config path. Can be absolute or relative")
|
||||||
endif()
|
endif()
|
||||||
|
option(DEBUG "Debug mode" OFF)
|
||||||
|
option(USE_STD_MALLOC "Use standard malloc instead of TBB" ON)
|
||||||
|
option(ACE_USE_EXTERNAL "Use external ACE" OFF)
|
||||||
|
option(POSTGRESQL "Use PostgreSQL" OFF)
|
||||||
|
option(BUILD_TOOLS "Build tools (map/vmap/mmap extractors)" ON)
|
||||||
|
#option(SCRIPT_LIB_ELUNA "Use Eluna as the scripting engine" ON) Temporarily disabled till more sync work is done
|
||||||
|
option(SCRIPT_LIB_SD2 "Use ScriptDev2 as the scripting engine" ON)
|
||||||
|
option(SOAP "Enable access via SOAP or not" OFF)
|
||||||
|
option(PCH "Use precompiled headers" ON)
|
||||||
# TODO: options that should be checked/created:
|
# TODO: options that should be checked/created:
|
||||||
#option(CLI "With CLI" ON)
|
#option(CLI "With CLI" ON)
|
||||||
#option(RA "With Remote Access" OFF)
|
#option(RA "With Remote Access" OFF)
|
||||||
#option(SQL "Copy SQL files" OFF)
|
#option(SQL "Copy SQL files" OFF)
|
||||||
#option(TOOLS "Build tools" OFF)
|
|
||||||
|
|
||||||
# Output description of this script
|
# Output description of this script
|
||||||
message("")
|
message("")
|
||||||
message(
|
message(
|
||||||
"This script builds the MaNGOS server.
|
"This script builds the MaNGOS server.
|
||||||
Options that can be used in order to configure the process:
|
Options that can be used in order to configure the process:
|
||||||
|
General:
|
||||||
CMAKE_INSTALL_PREFIX Path where the server should be installed to
|
CMAKE_INSTALL_PREFIX Path where the server should be installed to
|
||||||
CMAKE_BUILD_TYPE Sets build type (Release;Debug;...)
|
CONF_DIR Path to the configs, can be absolute or relative.
|
||||||
PCH Use precompiled headers
|
PCH Use precompiled headers
|
||||||
INCLUDE_BINDINGS_DIR Include a script library in src/bindings/ with the
|
DEBUG Debug mode
|
||||||
defined name. the name must corespond to the name of
|
USE_STD_MALLOC Use standard malloc instead of TBB
|
||||||
the folder and the folder must contain a valid
|
|
||||||
CMakeLists.txt
|
|
||||||
ACE_USE_EXTERNAL Use external ACE
|
ACE_USE_EXTERNAL Use external ACE
|
||||||
|
BUILD_TOOLS Build map/vmap/mmap extractors
|
||||||
|
SOAP Enable remote access via SOAP
|
||||||
|
Scripting engines:
|
||||||
|
SCRIPT_LIB_ELUNA Compile with support for Eluna scripts
|
||||||
|
SCRIPT_LIB_SD2 Compile with support for ScriptDev2 scripts
|
||||||
|
|
||||||
To set an option simply type -D<OPTION>=<VALUE> after 'cmake <srcs>'.
|
To set an option simply type -D<OPTION>=<VALUE> after 'cmake <srcs>'.
|
||||||
Also, you can specify the generator with -G. see 'cmake --help' for more details
|
Also, you can specify the generator with -G. see 'cmake --help' for more details
|
||||||
For example: cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/opt/mangos"
|
For example: cmake .. -DDEBUG=1 -DCMAKE_INSTALL_PREFIX=/opt/mangos"
|
||||||
|
|
||||||
|
Note: On UNIX systems, CONF_DIR is relative to the bin folder.
|
||||||
)
|
)
|
||||||
message("")
|
message("")
|
||||||
|
|
||||||
|
|
@ -89,6 +109,11 @@ if(WIN32)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Used by map-extractor for now
|
||||||
|
if(UNIX)
|
||||||
|
find_package(BZip2 REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
# find Git: used to get the revision number
|
# find Git: used to get the revision number
|
||||||
find_package(Git)
|
find_package(Git)
|
||||||
|
|
||||||
|
|
@ -109,8 +134,15 @@ if(CMAKE_CONFIGURATION_TYPES)
|
||||||
FORCE)
|
FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(BIN_DIR ${CMAKE_INSTALL_PREFIX}/bin)
|
#Force set the default install path if it is default
|
||||||
set(CONF_DIR ${CMAKE_INSTALL_PREFIX}/etc)
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||||
|
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/bin/" CACHE PATH "MaNGOS default install prefix" FORCE)
|
||||||
|
endif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||||
|
|
||||||
|
#Output the compiled exes and DLLs to build/bin directory on windows by default
|
||||||
|
if(WIN32)
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
endif()
|
||||||
|
|
||||||
#Set config install path correctly from given path
|
#Set config install path correctly from given path
|
||||||
string(FIND "${CONF_DIR}" ":" CONF_DIR_ABSOLUTE)
|
string(FIND "${CONF_DIR}" ":" CONF_DIR_ABSOLUTE)
|
||||||
|
|
@ -118,26 +150,33 @@ if(${CONF_DIR_ABSOLUTE} EQUAL -1)
|
||||||
#Path was not absolute
|
#Path was not absolute
|
||||||
set(CONF_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CONF_DIR}")
|
set(CONF_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CONF_DIR}")
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
set(CONF_COPY_DIR "${CMAKE_BINARY_DIR}/bin/$(ConfigurationName)/${CONF_DIR}")
|
set(CONF_COPY_DIR "${CMAKE_BINARY_DIR}/bin/$(ConfigurationName)/${CONF_DIR}")
|
||||||
else()
|
endif()
|
||||||
#Path was absolute
|
else()
|
||||||
set(CONF_INSTALL_DIR "${CONF_DIR}")
|
#Path was absolute
|
||||||
if(MSVC)
|
set(CONF_INSTALL_DIR "${CONF_DIR}")
|
||||||
set(CONF_COPY_DIR "${CONF_DIR}")
|
if(MSVC)
|
||||||
endif()
|
set(CONF_COPY_DIR "${CONF_DIR}")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# If win32 put it in the bin dir not lib
|
# If win32 put binaries in root folder, else bin.
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(LIBS_DIR ${CMAKE_INSTALL_PREFIX}/bin)
|
set(BIN_DIR ${CMAKE_INSTALL_PREFIX}/)
|
||||||
|
else()
|
||||||
|
set(BIN_DIR ${CMAKE_INSTALL_PREFIX}/bin)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# If win32 put libs in root folder, else bin.
|
||||||
|
if(WIN32)
|
||||||
|
set(LIBS_DIR ${CMAKE_INSTALL_PREFIX}/)
|
||||||
else()
|
else()
|
||||||
set(LIBS_DIR ${CMAKE_INSTALL_PREFIX}/lib)
|
set(LIBS_DIR ${CMAKE_INSTALL_PREFIX}/lib)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# For Unix systems set the rpath so that libraries are found
|
# For Unix systems set the rpath so that libraries are found
|
||||||
set(CMAKE_INSTALL_RPATH ${LIBS_DIR})
|
set(CMAKE_INSTALL_RPATH "${LIBS_DIR}")
|
||||||
set(CMAKE_INSTALL_NAME_DIR ${LIBS_DIR})
|
set(CMAKE_INSTALL_NAME_DIR "${LIBS_DIR}")
|
||||||
# Run out of build tree
|
# Run out of build tree
|
||||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH OFF)
|
set(CMAKE_BUILD_WITH_INSTALL_RPATH OFF)
|
||||||
|
|
||||||
|
|
@ -164,18 +203,28 @@ endif()
|
||||||
|
|
||||||
# Win32 delivered packages
|
# Win32 delivered packages
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(MYSQL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/dep/include/mysql)
|
set(MYSQL_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/dep/include/mysql")
|
||||||
set(MYSQL_LIBRARY ${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_release/libmySQL.lib)
|
set(MYSQL_LIBRARY "${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libmySQL.lib")
|
||||||
set(MYSQL_DEBUG_LIBRARY ${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_debug/libmySQL.lib)
|
set(MYSQL_DEBUG_LIBRARY "${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libmySQL.lib")
|
||||||
set(OPENSSL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/dep/include/openssl)
|
set(OPENSSL_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/dep/include/openssl")
|
||||||
set(OPENSSL_LIBRARIES ${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_release/libeay32.lib)
|
set(OPENSSL_LIBRARIES "${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libeay32.lib")
|
||||||
set(OPENSSL_DEBUG_LIBRARIES ${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_debug/libeay32.lib)
|
set(OPENSSL_DEBUG_LIBRARIES "${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libeay32.lib")
|
||||||
# zlib is build
|
# zlib is build
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# *nix-specific packages
|
# *nix-specific packages
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
find_package(MySQL REQUIRED)
|
if(POSTGRESQL)
|
||||||
|
find_package(PostgreSQL REQUIRED)
|
||||||
|
|
||||||
|
if(POSTGRESQL_FOUND)
|
||||||
|
include_directories(${POSTGRESQL_INCLUDE_DIRS})
|
||||||
|
endif(POSTGRESQL_FOUND)
|
||||||
|
|
||||||
|
else()
|
||||||
|
find_package(MySQL REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -210,19 +259,11 @@ endif()
|
||||||
|
|
||||||
message(STATUS "MaNGOS-Core revision : ${GIT_REVISION}")
|
message(STATUS "MaNGOS-Core revision : ${GIT_REVISION}")
|
||||||
message(STATUS "Install server to : ${CMAKE_INSTALL_PREFIX}")
|
message(STATUS "Install server to : ${CMAKE_INSTALL_PREFIX}")
|
||||||
|
message(STATUS "Install configs to : ${CONF_INSTALL_DIR}")
|
||||||
if(DEFINED INCLUDE_BINDINGS_DIR AND INCLUDE_BINDINGS_DIR)
|
if("${CONF_DIR}" STREQUAL "")
|
||||||
# check if the directory exists
|
message(STATUS "Search configs from : binary directory (default)")
|
||||||
if(NOT IS_DIRECTORY ${CMAKE_SOURCE_DIR}/src/bindings/${INCLUDE_BINDINGS_DIR})
|
|
||||||
message(FATAL_ERROR "Could not find the script library which was supposed to be: " ${CMAKE_SOURCE_DIR}/src/bindings/${INCLUDE_BINDINGS_DIR})
|
|
||||||
endif()
|
|
||||||
# check if it really contains a CMakeLists.txt
|
|
||||||
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/src/bindings/${INCLUDE_BINDINGS_DIR}/CMakeLists.txt)
|
|
||||||
message(FATAL_ERROR "The script library does not contain a CMakeLists.txt!")
|
|
||||||
endif()
|
|
||||||
message(STATUS "Build script library : Yes (using ${INCLUDE_BINDINGS_DIR})")
|
|
||||||
else()
|
else()
|
||||||
message(STATUS "Build script library : No")
|
message(STATUS "Search configs from : ${CONF_DIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# if(CLI)
|
# if(CLI)
|
||||||
|
|
@ -239,42 +280,67 @@ endif()
|
||||||
# message(STATUS "* Build with RA : No (default)")
|
# message(STATUS "* Build with RA : No (default)")
|
||||||
# endif(RA)
|
# endif(RA)
|
||||||
|
|
||||||
if(PCH AND NOT PCHSupport_FOUND)
|
if(SOAP)
|
||||||
set(PCH 0 CACHE BOOL
|
message(STATUS "Support for SOAP : Yes")
|
||||||
"Use precompiled headers"
|
set(DEFINITIONS ${DEFINITIONS} -DENABLE_SOAP)
|
||||||
FORCE)
|
else()
|
||||||
message(WARNING "No PCH for your system possible but PCH was set to 1. Resetting it."
|
message(STATUS "Support for SOAP : No (default)")
|
||||||
)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
#Eluna temporarily disabled till more sync work can be done
|
||||||
|
|
||||||
|
#if(SCRIPT_LIB_ELUNA)
|
||||||
|
# message(STATUS "Script engine Eluna : Yes (default)")
|
||||||
|
# add_definitions(-DENABLE_ELUNA)
|
||||||
|
#else()
|
||||||
|
# message(STATUS "Script engine Eluna : No")
|
||||||
|
#endif()
|
||||||
|
|
||||||
|
if(SCRIPT_LIB_SD2)
|
||||||
|
message(STATUS "Script engine SD2 : Yes (default)")
|
||||||
|
add_definitions(-DENABLE_SD2)
|
||||||
|
else()
|
||||||
|
message(STATUS "Script engine SD2 : No")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_TOOLS)
|
||||||
|
message(STATUS "Build tools : Yes (default)")
|
||||||
|
else()
|
||||||
|
message(STATUS "Build tools : No")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(PCH)
|
if(PCH)
|
||||||
message(STATUS "Use PCH : Yes")
|
message(STATUS "Use PCH : Yes (default)")
|
||||||
else()
|
else()
|
||||||
message(STATUS "Use PCH : No")
|
message(STATUS "Use PCH : No")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(DEBUG)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
message(STATUS "Build in debug-mode : Yes")
|
||||||
|
else()
|
||||||
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
|
message(STATUS "Build in debug-mode : No (default)")
|
||||||
|
endif()
|
||||||
# Handle debugmode compiles (this will require further work for proper WIN32-setups)
|
# Handle debugmode compiles (this will require further work for proper WIN32-setups)
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libstdc++")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g")
|
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Set warning levels for different builds
|
# Set warning levels for different builds
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} --no-warnings ")
|
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} --no-warnings")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} --no-warnings")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} --no-warnings")
|
||||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Wfatal-errors -Wextra")
|
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Wfatal-errors -Wextra -Winvalid-pch")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wfatal-errors -Wextra")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wfatal-errors -Wextra -Winvalid-pch")
|
||||||
elseif(WIN32)
|
elseif(WIN32)
|
||||||
# Disable warnings in Visual Studio 8 and above and add /MP
|
# Disable warnings in Visual Studio 8 and above and add /MP
|
||||||
if(MSVC AND NOT CMAKE_GENERATOR MATCHES "Visual Studio 7")
|
if(MSVC AND NOT CMAKE_GENERATOR MATCHES "Visual Studio 7")
|
||||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267 /MP")
|
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267 /MP")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /wd4996 /wd4355 /wd4244 /wd4267 /MP")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /wd4996 /wd4355 /wd4244 /wd4267 /MP")
|
||||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267 /W3 /MP")
|
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267 /MP")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267 /W3 /MP")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267 /MP")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
@ -284,12 +350,6 @@ endif()
|
||||||
# message(STATUS "Install SQL-files : No (default)")
|
# message(STATUS "Install SQL-files : No (default)")
|
||||||
# endif()
|
# endif()
|
||||||
|
|
||||||
# if(TOOLS)
|
|
||||||
# message(STATUS "Build map/vmap tools : Yes")
|
|
||||||
# else()
|
|
||||||
# message(STATUS "Build map/vmap tools : No (default)")
|
|
||||||
# endif()
|
|
||||||
|
|
||||||
# Some small tweaks for Visual Studio 7 and above.
|
# Some small tweaks for Visual Studio 7 and above.
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
# Mark 32 bit executables large address aware so they can use > 2GB address space
|
# Mark 32 bit executables large address aware so they can use > 2GB address space
|
||||||
|
|
@ -300,7 +360,7 @@ endif()
|
||||||
|
|
||||||
set(CMAKE_SKIP_BUILD_RPATH FALSE)
|
set(CMAKE_SKIP_BUILD_RPATH FALSE)
|
||||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||||
set(CMAKE_INSTALL_RPATH ${LIBS_DIR})
|
set(CMAKE_INSTALL_RPATH "${LIBS_DIR}")
|
||||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||||
|
|
||||||
# Generate revision-extractor
|
# Generate revision-extractor
|
||||||
|
|
@ -312,43 +372,23 @@ add_executable(genrev
|
||||||
${GENREV_SRC}
|
${GENREV_SRC}
|
||||||
)
|
)
|
||||||
|
|
||||||
get_target_property(GENERATE_EXE genrev LOCATION)
|
|
||||||
add_custom_target("revision.h" ALL
|
|
||||||
COMMAND ${GENERATE_EXE} ${CMAKE_SOURCE_DIR}
|
|
||||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
|
||||||
DEPENDS genrev
|
|
||||||
)
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
install(
|
install(
|
||||||
FILES
|
FILES
|
||||||
${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_release/libeay32.dll
|
"${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libeay32.dll"
|
||||||
${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_release/libmySQL.dll
|
"${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/ssleay32.dll"
|
||||||
|
"${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libmySQL.dll"
|
||||||
DESTINATION ${LIBS_DIR}
|
DESTINATION ${LIBS_DIR}
|
||||||
CONFIGURATIONS Release
|
CONFIGURATIONS Release
|
||||||
)
|
)
|
||||||
install(
|
install(
|
||||||
FILES
|
FILES
|
||||||
${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_debug/libeay32.dll
|
"${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libeay32.dll"
|
||||||
${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_debug/libmySQL.dll
|
"${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/ssleay32.dll"
|
||||||
|
"${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libmySQL.dll"
|
||||||
DESTINATION ${LIBS_DIR}
|
DESTINATION ${LIBS_DIR}
|
||||||
CONFIGURATIONS Debug
|
CONFIGURATIONS Debug
|
||||||
)
|
)
|
||||||
if(PLATFORM MATCHES X86)
|
|
||||||
# Copy dll's Windows needs
|
|
||||||
install(
|
|
||||||
FILES
|
|
||||||
${CMAKE_SOURCE_DIR}/dep/lib/win32_release/dbghelp.dll
|
|
||||||
DESTINATION ${LIBS_DIR}
|
|
||||||
CONFIGURATIONS Release
|
|
||||||
)
|
|
||||||
install(
|
|
||||||
FILES
|
|
||||||
${CMAKE_SOURCE_DIR}/dep/lib/win32_debug/dbghelp.dll
|
|
||||||
DESTINATION ${LIBS_DIR}
|
|
||||||
CONFIGURATIONS Debug
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(XCODE)
|
if(XCODE)
|
||||||
|
|
@ -359,30 +399,73 @@ if(XCODE)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
# This is used by config.h.cmake
|
||||||
|
if (SOAP)
|
||||||
|
set(ENABLE_SOAP ON)
|
||||||
|
else()
|
||||||
|
set(ENABLE_SOAP OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(dep) # TODO: add vmap extractor build support
|
add_subdirectory(dep)
|
||||||
|
|
||||||
# Add definitions for all build types
|
# Add definitions for all build types
|
||||||
# Don't place this above 'dep' subdirectory! Because of defines build will crash.
|
# Don't place this above 'dep' subdirectory! Because of defines build will crash.
|
||||||
|
# Remember, this overwrites global preprocessors, thus SOAP, Eluna, SD2 etc. has to be defined twice.
|
||||||
|
# TODO: Clean this up?
|
||||||
|
|
||||||
set(DEFINITIONS
|
set(DEFINITIONS
|
||||||
DO_MYSQL
|
|
||||||
HAVE_CONFIG_H
|
HAVE_CONFIG_H
|
||||||
VERSION="${MANGOS_VERSION}"
|
MANGOS
|
||||||
SYSCONFDIR="${CONF_DIR}/"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(SOAP)
|
||||||
|
set(DEFINITIONS ${DEFINITIONS} ENABLE_SOAP)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#Temporarily disabled till more work can be done
|
||||||
|
|
||||||
|
#if(SCRIPT_LIB_ELUNA)
|
||||||
|
# set(DEFINITIONS ${DEFINITIONS} ENABLE_ELUNA)
|
||||||
|
#endif()
|
||||||
|
|
||||||
|
if(SCRIPT_LIB_SD2)
|
||||||
|
set(DEFINITIONS ${DEFINITIONS} ENABLE_SD2)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(POSTGRESQL)
|
||||||
|
set(DEFINITIONS ${DEFINITIONS} DO_POSTGRESQL)
|
||||||
|
else()
|
||||||
|
set(DEFINITIONS ${DEFINITIONS} DO_MYSQL)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(DEFINITIONS_RELEASE NDEBUG)
|
set(DEFINITIONS_RELEASE NDEBUG)
|
||||||
set(DEFINITIONS_DEBUG _DEBUG MANGOS_DEBUG)
|
set(DEFINITIONS_DEBUG) # _DEBUG MANGOS_DEBUG) was here..
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(DEFINITIONS ${DEFINITIONS} WIN32 _WIN32)
|
set(DEFINITIONS ${DEFINITIONS} WIN32 _WIN32)
|
||||||
set(DEFINITIONS_RELEASE ${DEFINITIONS_RELEASE} _CRT_SECURE_NO_WARNINGS)
|
set(DEFINITIONS_RELEASE ${DEFINITIONS_RELEASE} _CRT_SECURE_NO_WARNINGS)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(USE_STD_MALLOC)
|
||||||
|
set(DEFINITIONS ${DEFINITIONS} USE_STANDARD_MALLOC)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Specify the maximum PreCompiled Header memory allocation limit
|
||||||
|
# Fixes a compiler-problem when using PCH - the /Ym flag is adjusted by the compiler in MSVC2012, hence we need to set an upper limit with /Zm to avoid discrepancies)
|
||||||
|
# (And yes, this is a verified , unresolved bug with MSVC... *sigh*)
|
||||||
|
# Make it MSVC specific, otherwise g++ etc pukes
|
||||||
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||||
|
string(REGEX REPLACE "/Zm[0-9]+ *" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zm500" CACHE STRING "" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -stdlib=libc++")
|
||||||
|
endif()
|
||||||
|
|
||||||
set_directory_properties(PROPERTIES COMPILE_DEFINITIONS "${DEFINITIONS}")
|
set_directory_properties(PROPERTIES COMPILE_DEFINITIONS "${DEFINITIONS}")
|
||||||
|
#Something like this is what i've imagined with the new generator expressions in cmake 3.0 / lfx
|
||||||
|
#set_directory_properties(PROPERTIES COMPILE_DEFINITIONS "$<$<CONFIG:Release>:"${DEFINITIONS} ${DEFINITIONS_RELEASE}">$<$<CONFIG:Debug>:"${DEFINITIONS} ${DEFINITIONS_DEBUG}">")
|
||||||
set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_RELEASE "${DEFINITIONS_RELEASE}")
|
set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_RELEASE "${DEFINITIONS_RELEASE}")
|
||||||
set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG "${DEFINITIONS_DEBUG}")
|
set_directory_properties(PROPERTIES COMPILE_DEFINITIONS_DEBUG "${DEFINITIONS_DEBUG}")
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
# if(SQL)
|
|
||||||
# add_subdirectory(sql)
|
|
||||||
# endif()
|
|
||||||
|
|
|
||||||
172
dep/StormLib/.gitignore
vendored
172
dep/StormLib/.gitignore
vendored
|
|
@ -1,13 +1,163 @@
|
||||||
#
|
#################
|
||||||
# NOTE! Don't add files that are generated in specific
|
## Eclipse
|
||||||
# subdirectories here. Add them in the ".gitignore" file
|
#################
|
||||||
# in that subdirectory instead.
|
|
||||||
#
|
|
||||||
# NOTE! Please use 'git-ls-files -i --exclude-standard'
|
|
||||||
# command after changing this file, to see if there are
|
|
||||||
# any tracked files which get ignored after the change.
|
|
||||||
#
|
|
||||||
# MaNGOS generated files at Windows build
|
|
||||||
#
|
|
||||||
|
|
||||||
|
*.pydevproject
|
||||||
|
.project
|
||||||
|
.metadata
|
||||||
|
bin/
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.classpath
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
|
||||||
|
# External tool builders
|
||||||
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations"
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# CDT-specific
|
||||||
|
.cproject
|
||||||
|
|
||||||
|
# PDT-specific
|
||||||
|
.buildpath
|
||||||
|
|
||||||
|
|
||||||
|
#################
|
||||||
|
## Visual Studio
|
||||||
|
#################
|
||||||
|
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Rr]elease/
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.vspscc
|
||||||
|
.builds
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
## TODO: If you have NuGet Package Restore enabled, uncomment this
|
||||||
|
#packages/
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish
|
||||||
|
|
||||||
|
# Others
|
||||||
|
[Bb]in
|
||||||
|
[Oo]bj
|
||||||
|
sql
|
||||||
|
TestResults
|
||||||
|
*.Cache
|
||||||
|
ClientBin
|
||||||
|
stylecop.*
|
||||||
|
~$*
|
||||||
|
*.dbmdl
|
||||||
|
Generated_Code #added for RIA/Silverlight projects
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file to a newer
|
||||||
|
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
## Windows
|
||||||
|
############
|
||||||
|
|
||||||
|
# Windows image file caches
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
Desktop.ini
|
||||||
|
|
||||||
|
|
||||||
|
#############
|
||||||
|
## Python
|
||||||
|
#############
|
||||||
|
|
||||||
|
*.py[co]
|
||||||
|
|
||||||
|
# Packages
|
||||||
|
*.egg
|
||||||
|
*.egg-info
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
eggs
|
||||||
|
parts
|
||||||
bin
|
bin
|
||||||
|
var
|
||||||
|
sdist
|
||||||
|
develop-eggs
|
||||||
|
.installed.cfg
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
.coverage
|
||||||
|
.tox
|
||||||
|
|
||||||
|
#Translations
|
||||||
|
*.mo
|
||||||
|
|
||||||
|
#Mr Developer
|
||||||
|
.mr.developer.cfg
|
||||||
|
|
||||||
|
# Mac crap
|
||||||
|
.DS_Store
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ set(SRC_FILES
|
||||||
src/SBaseCommon.cpp
|
src/SBaseCommon.cpp
|
||||||
src/SBaseDumpData.cpp
|
src/SBaseDumpData.cpp
|
||||||
src/SBaseFileTable.cpp
|
src/SBaseFileTable.cpp
|
||||||
|
src/SBaseSubTypes.cpp
|
||||||
src/SCompression.cpp
|
src/SCompression.cpp
|
||||||
src/SFileAddFile.cpp
|
src/SFileAddFile.cpp
|
||||||
src/SFileAttributes.cpp
|
src/SFileAttributes.cpp
|
||||||
|
|
@ -22,12 +23,15 @@ set(SRC_FILES
|
||||||
src/SFileCreateArchive.cpp
|
src/SFileCreateArchive.cpp
|
||||||
src/SFileExtractFile.cpp
|
src/SFileExtractFile.cpp
|
||||||
src/SFileFindFile.cpp
|
src/SFileFindFile.cpp
|
||||||
|
src/SFileGetFileInfo.cpp
|
||||||
src/SFileListFile.cpp
|
src/SFileListFile.cpp
|
||||||
src/SFileOpenArchive.cpp
|
src/SFileOpenArchive.cpp
|
||||||
src/SFileOpenFileEx.cpp
|
src/SFileOpenFileEx.cpp
|
||||||
src/SFilePatchArchives.cpp
|
src/SFilePatchArchives.cpp
|
||||||
src/SFileReadFile.cpp
|
src/SFileReadFile.cpp
|
||||||
src/SFileVerify.cpp
|
src/SFileVerify.cpp
|
||||||
|
src/libtomcrypt/src/pk/rsa/rsa_verify_simple.c
|
||||||
|
src/libtomcrypt/src/misc/crypt_libc.c
|
||||||
)
|
)
|
||||||
|
|
||||||
set(TOMCRYPT_FILES
|
set(TOMCRYPT_FILES
|
||||||
|
|
@ -43,7 +47,6 @@ set(TOMCRYPT_FILES
|
||||||
src/libtomcrypt/src/misc/crypt_find_prng.c
|
src/libtomcrypt/src/misc/crypt_find_prng.c
|
||||||
src/libtomcrypt/src/misc/crypt_hash_descriptor.c
|
src/libtomcrypt/src/misc/crypt_hash_descriptor.c
|
||||||
src/libtomcrypt/src/misc/crypt_hash_is_valid.c
|
src/libtomcrypt/src/misc/crypt_hash_is_valid.c
|
||||||
src/libtomcrypt/src/misc/crypt_libc.c
|
|
||||||
src/libtomcrypt/src/misc/crypt_ltc_mp_descriptor.c
|
src/libtomcrypt/src/misc/crypt_ltc_mp_descriptor.c
|
||||||
src/libtomcrypt/src/misc/crypt_prng_descriptor.c
|
src/libtomcrypt/src/misc/crypt_prng_descriptor.c
|
||||||
src/libtomcrypt/src/misc/crypt_prng_is_valid.c
|
src/libtomcrypt/src/misc/crypt_prng_is_valid.c
|
||||||
|
|
@ -64,6 +67,20 @@ set(TOMCRYPT_FILES
|
||||||
src/libtomcrypt/src/pk/asn1/der_decode_short_integer.c
|
src/libtomcrypt/src/pk/asn1/der_decode_short_integer.c
|
||||||
src/libtomcrypt/src/pk/asn1/der_decode_utctime.c
|
src/libtomcrypt/src/pk/asn1/der_decode_utctime.c
|
||||||
src/libtomcrypt/src/pk/asn1/der_decode_utf8_string.c
|
src/libtomcrypt/src/pk/asn1/der_decode_utf8_string.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_bit_string.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_boolean.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_ia5_string.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_integer.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_object_identifier.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_octet_string.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_printable_string.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_sequence_ex.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_sequence_multi.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_set.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_setof.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_short_integer.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_utctime.c
|
||||||
|
src/libtomcrypt/src/pk/asn1/der_encode_utf8_string.c
|
||||||
src/libtomcrypt/src/pk/asn1/der_length_bit_string.c
|
src/libtomcrypt/src/pk/asn1/der_length_bit_string.c
|
||||||
src/libtomcrypt/src/pk/asn1/der_length_boolean.c
|
src/libtomcrypt/src/pk/asn1/der_length_boolean.c
|
||||||
src/libtomcrypt/src/pk/asn1/der_length_ia5_string.c
|
src/libtomcrypt/src/pk/asn1/der_length_ia5_string.c
|
||||||
|
|
@ -85,13 +102,15 @@ set(TOMCRYPT_FILES
|
||||||
src/libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c
|
src/libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c
|
||||||
src/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c
|
src/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c
|
||||||
src/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c
|
src/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c
|
||||||
|
src/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c
|
||||||
src/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c
|
src/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c
|
||||||
|
src/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c
|
||||||
src/libtomcrypt/src/pk/rsa/rsa_exptmod.c
|
src/libtomcrypt/src/pk/rsa/rsa_exptmod.c
|
||||||
src/libtomcrypt/src/pk/rsa/rsa_free.c
|
src/libtomcrypt/src/pk/rsa/rsa_free.c
|
||||||
src/libtomcrypt/src/pk/rsa/rsa_import.c
|
src/libtomcrypt/src/pk/rsa/rsa_import.c
|
||||||
src/libtomcrypt/src/pk/rsa/rsa_make_key.c
|
src/libtomcrypt/src/pk/rsa/rsa_make_key.c
|
||||||
|
src/libtomcrypt/src/pk/rsa/rsa_sign_hash.c
|
||||||
src/libtomcrypt/src/pk/rsa/rsa_verify_hash.c
|
src/libtomcrypt/src/pk/rsa/rsa_verify_hash.c
|
||||||
src/libtomcrypt/src/pk/rsa/rsa_verify_simple.c
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(TOMMATH_FILES
|
set(TOMMATH_FILES
|
||||||
|
|
@ -224,7 +243,7 @@ set(ZLIB_BZIP2_FILES
|
||||||
src/bzip2/huffman.c
|
src/bzip2/huffman.c
|
||||||
src/bzip2/randtable.c
|
src/bzip2/randtable.c
|
||||||
src/zlib/adler32.c
|
src/zlib/adler32.c
|
||||||
src/zlib/compress2.c
|
src/zlib/compress.c
|
||||||
src/zlib/crc32.c
|
src/zlib/crc32.c
|
||||||
src/zlib/deflate.c
|
src/zlib/deflate.c
|
||||||
src/zlib/inffast.c
|
src/zlib/inffast.c
|
||||||
|
|
@ -234,10 +253,6 @@ set(ZLIB_BZIP2_FILES
|
||||||
src/zlib/zutil.c
|
src/zlib/zutil.c
|
||||||
)
|
)
|
||||||
|
|
||||||
# set(TEST_SRC_FILES
|
|
||||||
# test/Test.cpp
|
|
||||||
# )
|
|
||||||
|
|
||||||
add_definitions(-D_7ZIP_ST -DBZ_STRICT_ANSI)
|
add_definitions(-D_7ZIP_ST -DBZ_STRICT_ANSI)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
|
@ -268,37 +283,21 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL Linux)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(storm SHARED ${SRC_FILES} ${SRC_ADDITIONAL_FILES})
|
add_library(StormLib STATIC ${SRC_FILES} ${SRC_ADDITIONAL_FILES})
|
||||||
target_link_libraries(storm ${LINK_LIBS})
|
target_link_libraries(StormLib ${LINK_LIBS})
|
||||||
|
|
||||||
# option(WITH_TEST "Compile Test application" OFF)
|
|
||||||
# if(WITH_TEST)
|
|
||||||
# add_executable(storm_test ${TEST_SRC_FILES})
|
|
||||||
# target_link_libraries(storm_test storm)
|
|
||||||
# endif()
|
|
||||||
|
|
||||||
# option(WITH_STATIC "Compile static linked library" OFF)
|
|
||||||
# if(WITH_STATIC)
|
|
||||||
# add_library(StormLib_static STATIC ${SRC_FILES} ${SRC_ADDITIONAL_FILES})
|
|
||||||
# target_link_libraries(StormLib_static ${LINK_LIBS})
|
|
||||||
# set_target_properties(StormLib_static PROPERTIES OUTPUT_NAME StormLib)
|
|
||||||
# endif()
|
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set_target_properties(storm PROPERTIES FRAMEWORK true)
|
set_target_properties(StormLib PROPERTIES FRAMEWORK true)
|
||||||
set_target_properties(storm PROPERTIES PUBLIC_HEADER "src/StormLib.h src/StormPort.h")
|
set_target_properties(StormLib PROPERTIES PUBLIC_HEADER "src/StormLib.h src/StormPort.h")
|
||||||
set_target_properties(storm PROPERTIES LINK_FLAGS "-framework Carbon")
|
set_target_properties(StormLib PROPERTIES LINK_FLAGS "-framework Carbon")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
set_target_properties(storm PROPERTIES VERSION 8.20.0)
|
set_target_properties(StormLib PROPERTIES VERSION 9.0.0)
|
||||||
set_target_properties(storm PROPERTIES SOVERSION 8.20)
|
set_target_properties(StormLib PROPERTIES SOVERSION 9)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# On Win32, build StormLib.dll since we don't want to clash with Storm.dll
|
# On Win32, build StormLib.dll since we don't want to clash with Storm.dll
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set_target_properties(storm PROPERTIES OUTPUT_NAME StormLib)
|
set_target_properties(StormLib PROPERTIES OUTPUT_NAME StormLib)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(TARGETS storm RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib FRAMEWORK DESTINATION /Library/Frameworks)
|
|
||||||
install(FILES src/StormLib.h src/StormPort.h DESTINATION include)
|
|
||||||
21
dep/StormLib/LICENSE
Normal file
21
dep/StormLib/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 1999-2013 Ladislav Zezula
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
1
dep/StormLib/README
Normal file
1
dep/StormLib/README
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
This is official repository for the StomLib library, an open-source project that can work with Blizzard MPQ archives.
|
||||||
|
|
@ -2,6 +2,22 @@
|
||||||
StormLib history
|
StormLib history
|
||||||
================
|
================
|
||||||
|
|
||||||
|
Version 9.11
|
||||||
|
|
||||||
|
- Fixed bug in processing HET table.
|
||||||
|
|
||||||
|
Version 9.10
|
||||||
|
|
||||||
|
- Support for weak-signing
|
||||||
|
- Anti-protector: New Spazzler
|
||||||
|
|
||||||
|
Version 9.00
|
||||||
|
|
||||||
|
- Support for streaming (master-mirror)
|
||||||
|
- Support for multi-file MPQs used by some WoW versions
|
||||||
|
- Opening maps protected by Spazzler protector
|
||||||
|
- Opening maps protected by BOBA protector
|
||||||
|
|
||||||
Version 8.02
|
Version 8.02
|
||||||
|
|
||||||
- Support for UNICODE encoding for on-disk files
|
- Support for UNICODE encoding for on-disk files
|
||||||
|
|
@ -18,13 +34,13 @@
|
||||||
- Updated support for protected maps from Warcraft III
|
- Updated support for protected maps from Warcraft III
|
||||||
|
|
||||||
Version 7.11
|
Version 7.11
|
||||||
|
|
||||||
- Support for MPQs v 3.0 (WOW-Cataclysm BETA)
|
- Support for MPQs v 3.0 (WOW-Cataclysm BETA)
|
||||||
- StormLib now deals properly with files that have MPQ_SECTOR_CHECKSUM missing,
|
- StormLib now deals properly with files that have MPQ_SECTOR_CHECKSUM missing,
|
||||||
but have sector checksum entry present in the sector offset table
|
but have sector checksum entry present in the sector offset table
|
||||||
|
|
||||||
Version 7.10
|
Version 7.10
|
||||||
|
|
||||||
- Support for partial MPQs ("interface.MPQ.part")
|
- Support for partial MPQs ("interface.MPQ.part")
|
||||||
- The only operation that is externally allowed to do with internal files
|
- The only operation that is externally allowed to do with internal files
|
||||||
("(listfile)", "(attributes)" and "(signature)") is reading. Attempt to modify any of the file
|
("(listfile)", "(attributes)" and "(signature)") is reading. Attempt to modify any of the file
|
||||||
|
|
@ -32,7 +48,7 @@
|
||||||
- Fixed memory leak that has occured when writing more than one sector to the file at once
|
- Fixed memory leak that has occured when writing more than one sector to the file at once
|
||||||
|
|
||||||
Version 7.01
|
Version 7.01
|
||||||
|
|
||||||
- Support for adding files from memory
|
- Support for adding files from memory
|
||||||
- Fixed improper validation of handles to MPQ file and MPQ archive
|
- Fixed improper validation of handles to MPQ file and MPQ archive
|
||||||
- Fixed bug where StormLib didn't save CRC32 of the file when added to archive
|
- Fixed bug where StormLib didn't save CRC32 of the file when added to archive
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ TABLE OF CONTENTS
|
||||||
1. INTRODUCTION TO THE MOPAQ FORMAT
|
1. INTRODUCTION TO THE MOPAQ FORMAT
|
||||||
The MoPaQ (or MPQ) format is an archive file format designed by Mike O'Brien (hence the name Mike O'brien PaCK) at Blizzard
|
The MoPaQ (or MPQ) format is an archive file format designed by Mike O'Brien (hence the name Mike O'brien PaCK) at Blizzard
|
||||||
Entertainment. The format has been used in all Blizzard games since (and including) Diablo. It is heavily optimized to be
|
Entertainment. The format has been used in all Blizzard games since (and including) Diablo. It is heavily optimized to be
|
||||||
a read-only game archive format, and excels at this role.
|
a read-only game archive format, and excels at this role.
|
||||||
|
|
||||||
The Blizzard MoPaQ-reading functions are contained in the Storm module, which my be either statically or dynamically linked.
|
The Blizzard MoPaQ-reading functions are contained in the Storm module, which my be either statically or dynamically linked.
|
||||||
The Blizzard MoPaQ-writing functions are contained in the MPQAPI module, which is always statically linked.
|
The Blizzard MoPaQ-writing functions are contained in the MPQAPI module, which is always statically linked.
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ TABLE OF CONTENTS
|
||||||
4. Revision History
|
4. Revision History
|
||||||
|
|
||||||
1. INTRODUCTION TO THE MOPAQ FORMAT
|
1. INTRODUCTION TO THE MOPAQ FORMAT
|
||||||
The MoPaQ (or MPQ) format is an archive file format designed by Mike O'Brien (hence the name Mike O'brien PaCK) at Blizzard Entertainment. The format has been used in all Blizzard games since (and including) Diablo. It is heavily optimized to be a read-only game archive format, and excels at this role.
|
The MoPaQ (or MPQ) format is an archive file format designed by Mike O'Brien (hence the name Mike O'brien PaCK) at Blizzard Entertainment. The format has been used in all Blizzard games since (and including) Diablo. It is heavily optimized to be a read-only game archive format, and excels at this role.
|
||||||
|
|
||||||
The Blizzard MoPaQ-reading functions are contained in the Storm module, which my be either statically or dynamically linked. The Blizzard MoPaQ-writing functions are contained in the MPQAPI module, which is always statically linked.
|
The Blizzard MoPaQ-reading functions are contained in the Storm module, which my be either statically or dynamically linked. The Blizzard MoPaQ-writing functions are contained in the MPQAPI module, which is always statically linked.
|
||||||
|
|
||||||
|
|
@ -102,7 +102,7 @@ The data for each file is composed of the following structure:
|
||||||
00h: int32(SectorsInFile + 1) SectorOffsetTable : Offsets to the start of each sector, relative to the beginning of the file data. The last entry contains the file size, making it possible to easily calculate the size of any given sector. This table is not present if this information can be calculated (see details below).
|
00h: int32(SectorsInFile + 1) SectorOffsetTable : Offsets to the start of each sector, relative to the beginning of the file data. The last entry contains the file size, making it possible to easily calculate the size of any given sector. This table is not present if this information can be calculated (see details below).
|
||||||
immediately following SectorOffsetTable: SECTOR Sectors(SectorsInFile) : Data of each sector in the file, packed end to end (see details below).
|
immediately following SectorOffsetTable: SECTOR Sectors(SectorsInFile) : Data of each sector in the file, packed end to end (see details below).
|
||||||
|
|
||||||
Normally, file data is split up into sectors, for simple streaming. All sectors, save for the last, will contain as many bytes of file data as specified in the archive header's SectorSizeShift; the last sector may contain less than this, depending on the size of the entire file's data. If the file is compressed or imploded, the sector will be smaller or the same size as the file data it contains. Individual sectors in a compressed or imploded file may be stored uncompressed; this occurs if and only if the file data the sector contains could not be compressed by the algorithm(s) used (if the compressed sector size was greater than or equal to the size of the file data), and is indicated by the sector's size in SectorOffsetTable being equal to the size of the file data in the sector (which may be calculated from the FileSize).
|
Normally, file data is split up into sectors, for simple streaming. All sectors, save for the last, will contain as many bytes of file data as specified in the archive header's SectorSizeShift; the last sector may contain less than this, depending on the size of the entire file's data. If the file is compressed or imploded, the sector will be smaller or the same size as the file data it contains. Individual sectors in a compressed or imploded file may be stored uncompressed; this occurs if and only if the file data the sector contains could not be compressed by the algorithm(s) used (if the compressed sector size was greater than or equal to the size of the file data), and is indicated by the sector's size in SectorOffsetTable being equal to the size of the file data in the sector (which may be calculated from the FileSize).
|
||||||
|
|
||||||
The format of each sector depends on the kind of sector it is. Uncompressed sectors are simply the the raw file data contained in the sector. Imploded sectors are the raw compressed data following compression with the implode algorithm (these sectors can only be in imploded files). Compressed sectors (only found in compressed - not imploded - files) are compressed with one or more compression algorithms, and have the following structure:
|
The format of each sector depends on the kind of sector it is. Uncompressed sectors are simply the the raw file data contained in the sector. Imploded sectors are the raw compressed data following compression with the implode algorithm (these sectors can only be in imploded files). Compressed sectors (only found in compressed - not imploded - files) are compressed with one or more compression algorithms, and have the following structure:
|
||||||
00h: byte CompressionMask : Mask of the compression types applied to this sector. If multiple compression types are used, they are applied in the order listed below, and decompression is performed in the opposite order. This byte counts towards the total sector size, meaning that the sector will be stored uncompressed if the data cannot be compressed by at least two bytes; as well, this byte is encrypted with the sector data, if applicable. The following compression types are defined (for implementations of these algorithms, see StormLib):
|
00h: byte CompressionMask : Mask of the compression types applied to this sector. If multiple compression types are used, they are applied in the order listed below, and decompression is performed in the opposite order. This byte counts towards the total sector size, meaning that the sector will be stored uncompressed if the data cannot be compressed by at least two bytes; as well, this byte is encrypted with the sector data, if applicable. The following compression types are defined (for implementations of these algorithms, see StormLib):
|
||||||
|
|
@ -257,7 +257,7 @@ unsigned long HashString(const char *lpszString, unsigned long dwHashType)
|
||||||
{
|
{
|
||||||
assert(lpszString);
|
assert(lpszString);
|
||||||
assert(dwHashType <= MPQ_HASH_FILE_KEY);
|
assert(dwHashType <= MPQ_HASH_FILE_KEY);
|
||||||
|
|
||||||
unsigned long seed1 = 0x7FED7FEDL;
|
unsigned long seed1 = 0x7FED7FEDL;
|
||||||
unsigned long seed2 = 0xEEEEEEEEL;
|
unsigned long seed2 = 0xEEEEEEEEL;
|
||||||
int ch;
|
int ch;
|
||||||
|
|
@ -277,21 +277,21 @@ unsigned long HashString(const char *lpszString, unsigned long dwHashType)
|
||||||
unsigned long ComputeFileKey(const char *lpszFilePath, const BlockTableEntry &blockEntry, unsigned long nArchiveOffset)
|
unsigned long ComputeFileKey(const char *lpszFilePath, const BlockTableEntry &blockEntry, unsigned long nArchiveOffset)
|
||||||
{
|
{
|
||||||
assert(lpszFilePath);
|
assert(lpszFilePath);
|
||||||
|
|
||||||
// Find the file name part of the path
|
// Find the file name part of the path
|
||||||
const char *lpszFileName = strrchr(lpszFilePath, '\\');
|
const char *lpszFileName = strrchr(lpszFilePath, '\\');
|
||||||
if (lpszFileName)
|
if (lpszFileName)
|
||||||
lpszFileName++; // Skip the \
|
lpszFileName++; // Skip the \
|
||||||
else
|
else
|
||||||
lpszFileName = lpszFilePath;
|
lpszFileName = lpszFilePath;
|
||||||
|
|
||||||
// Hash the name to get the base key
|
// Hash the name to get the base key
|
||||||
unsigned long nFileKey = HashString(lpszFileName, MPQ_HASH_FILE_KEY);
|
unsigned long nFileKey = HashString(lpszFileName, MPQ_HASH_FILE_KEY);
|
||||||
|
|
||||||
// Offset-adjust the key if necessary
|
// Offset-adjust the key if necessary
|
||||||
if (blockEntry.Flags & BLOCK_OFFSET_ADJUSTED_KEY)
|
if (blockEntry.Flags & BLOCK_OFFSET_ADJUSTED_KEY)
|
||||||
nFileKey = (nFileKey + blockEntry.BlockOffset) ^ blockEntry.FileSize;
|
nFileKey = (nFileKey + blockEntry.BlockOffset) ^ blockEntry.FileSize;
|
||||||
|
|
||||||
return nFileKey;
|
return nFileKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -305,38 +305,38 @@ bool FindFileInHashTable(const HashTableEntry *lpHashTable, unsigned long nHashT
|
||||||
assert(lpHashTable);
|
assert(lpHashTable);
|
||||||
assert(nHashTableSize);
|
assert(nHashTableSize);
|
||||||
assert(lpszFilePath);
|
assert(lpszFilePath);
|
||||||
|
|
||||||
// Find the home entry in the hash table for the file
|
// Find the home entry in the hash table for the file
|
||||||
unsigned long iInitEntry = HashString(lpszFilePath, MPQ_HASH_TABLE_OFFSET) & (nHashTableSize - 1);
|
unsigned long iInitEntry = HashString(lpszFilePath, MPQ_HASH_TABLE_OFFSET) & (nHashTableSize - 1);
|
||||||
|
|
||||||
// Is there anything there at all?
|
// Is there anything there at all?
|
||||||
if (lpHashTable[iInitEntry].FileBlockIndex == MPQ_HASH_ENTRY_EMPTY)
|
if (lpHashTable[iInitEntry].FileBlockIndex == MPQ_HASH_ENTRY_EMPTY)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Compute the hashes to compare the hash table entry against
|
// Compute the hashes to compare the hash table entry against
|
||||||
unsigned long nNameHashA = HashString(lpszFilePath, MPQ_HASH_NAME_A),
|
unsigned long nNameHashA = HashString(lpszFilePath, MPQ_HASH_NAME_A),
|
||||||
nNameHashB = HashString(lpszFilePath, MPQ_HASH_NAME_B),
|
nNameHashB = HashString(lpszFilePath, MPQ_HASH_NAME_B),
|
||||||
iCurEntry = iInitEntry;
|
iCurEntry = iInitEntry;
|
||||||
|
|
||||||
// Check each entry in the hash table till a termination point is reached
|
// Check each entry in the hash table till a termination point is reached
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (lpHashTable[iCurEntry].FileBlockIndex != MPQ_HASH_ENTRY_DELETED)
|
if (lpHashTable[iCurEntry].FileBlockIndex != MPQ_HASH_ENTRY_DELETED)
|
||||||
{
|
{
|
||||||
if (lpHashTable[iCurEntry].FilePathHashA == nNameHashA
|
if (lpHashTable[iCurEntry].FilePathHashA == nNameHashA
|
||||||
&& lpHashTable[iCurEntry].FilePathHashB == nNameHashB
|
&& lpHashTable[iCurEntry].FilePathHashB == nNameHashB
|
||||||
&& lpHashTable[iCurEntry].Language == nLang
|
&& lpHashTable[iCurEntry].Language == nLang
|
||||||
&& lpHashTable[iCurEntry].Platform == nPlatform)
|
&& lpHashTable[iCurEntry].Platform == nPlatform)
|
||||||
{
|
{
|
||||||
iFileHashEntry = iCurEntry;
|
iFileHashEntry = iCurEntry;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
iCurEntry = (iCurEntry + 1) & (nHashTableSize - 1);
|
iCurEntry = (iCurEntry + 1) & (nHashTableSize - 1);
|
||||||
} while (iCurEntry != iInitEntry && lpHashTable[iCurEntry].FileBlockIndex != MPQ_HASH_ENTRY_EMPTY);
|
} while (iCurEntry != iInitEntry && lpHashTable[iCurEntry].FileBlockIndex != MPQ_HASH_ENTRY_EMPTY);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -347,25 +347,25 @@ bool DeleteFile(HashTableEntry *lpHashTable, unsigned long nHashTableSize, Block
|
||||||
assert(lpHashTable);
|
assert(lpHashTable);
|
||||||
assert(nHashTableSize);
|
assert(nHashTableSize);
|
||||||
assert(lpBlockTable);
|
assert(lpBlockTable);
|
||||||
|
|
||||||
// Find the file in the hash table
|
// Find the file in the hash table
|
||||||
unsigned long iFileHashEntry;
|
unsigned long iFileHashEntry;
|
||||||
|
|
||||||
if (!FindFileInHashTable(lpHashTable, nHashTableSize, lpszFilePath, nLang, nPlatform, iFileHashEntry))
|
if (!FindFileInHashTable(lpHashTable, nHashTableSize, lpszFilePath, nLang, nPlatform, iFileHashEntry))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Get the block table index before we nuke the hash table entry
|
// Get the block table index before we nuke the hash table entry
|
||||||
unsigned long iFileBlockEntry = lpHashTable[iFileHashEntry].FileBlockIndex;
|
unsigned long iFileBlockEntry = lpHashTable[iFileHashEntry].FileBlockIndex;
|
||||||
|
|
||||||
// Delete the file's entry in the hash table
|
// Delete the file's entry in the hash table
|
||||||
memset(&lpHashTable[iFileHashEntry], 0xFF, sizeof(HashTableEntry));
|
memset(&lpHashTable[iFileHashEntry], 0xFF, sizeof(HashTableEntry));
|
||||||
|
|
||||||
// If the next entry is empty, mark this one as empty; otherwise, mark this as deleted.
|
// If the next entry is empty, mark this one as empty; otherwise, mark this as deleted.
|
||||||
if (lpHashTable[(iFileHashEntry + 1) & (nHashTableSize - 1)].FileBlockIndex == MPQ_HASH_ENTRY_EMPTY)
|
if (lpHashTable[(iFileHashEntry + 1) & (nHashTableSize - 1)].FileBlockIndex == MPQ_HASH_ENTRY_EMPTY)
|
||||||
lpHashTable[iFileHashEntry].FileBlockIndex = MPQ_HASH_ENTRY_EMPTY;
|
lpHashTable[iFileHashEntry].FileBlockIndex = MPQ_HASH_ENTRY_EMPTY;
|
||||||
else
|
else
|
||||||
lpHashTable[iFileHashEntry].FileBlockIndex = MPQ_HASH_ENTRY_DELETED;
|
lpHashTable[iFileHashEntry].FileBlockIndex = MPQ_HASH_ENTRY_DELETED;
|
||||||
|
|
||||||
// If the block occupies space, mark the block as free space; otherwise, clear the block table entry.
|
// If the block occupies space, mark the block as free space; otherwise, clear the block table entry.
|
||||||
if (lpBlockTable[iFileBlockEntry].BlockSize > 0)
|
if (lpBlockTable[iFileBlockEntry].BlockSize > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -374,7 +374,7 @@ bool DeleteFile(HashTableEntry *lpHashTable, unsigned long nHashTableSize, Block
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
memset(&lpBlockTable[iFileBlockEntry], 0, sizeof(BlockTableEntry);
|
memset(&lpBlockTable[iFileBlockEntry], 0, sizeof(BlockTableEntry);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
S48B6CDTN5XEQAKQDJNDLJBJ73FDFM3U
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Y45MD3CAK4KXSSXHYD9VY64Z8EKJ4XFX
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
G8MN8UDG6NA2ANGY6A3DNY82HRGF29ZH
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
3DH5RE5NVM5GTFD85LXGWT6FK859ETR5
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
8WLKUAXE94PFQU4Y249PAZ24N4R4XKTQ
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
A34DXX3VHGGXSQBRFE5UFFDXMF9G4G54
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ZG7J9K938HJEFWPQUA768MA2PFER6EAJ
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
NE7CUNNNTVAPXV7E3G2BSVBWGVMW8BL2
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
3V9E2FTMBM9QQWK7U6MAMWAZWQDB838F
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
2NSFB8MELULJ83U6YHA3UP6K4MQD48L6
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
QA2TZ9EWZ4CUU8BMB5WXCTY65F9CSW4E
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
VHB378W64BAT9SH7D68VV9NLQDK9YEGT
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
U3NFQJV4M6GC7KBN9XQJ3BRDN3PLD9NE
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -14,58 +14,77 @@
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Function prototypes
|
// Function prototypes
|
||||||
|
|
||||||
|
typedef void (*STREAM_INIT)(
|
||||||
|
struct TFileStream * pStream // Pointer to an unopened stream
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef bool (*STREAM_CREATE)(
|
||||||
|
struct TFileStream * pStream // Pointer to an unopened stream
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef bool (*STREAM_OPEN)(
|
||||||
|
struct TFileStream * pStream, // Pointer to an unopened stream
|
||||||
|
const TCHAR * szFileName, // Pointer to file name to be open
|
||||||
|
DWORD dwStreamFlags // Stream flags
|
||||||
|
);
|
||||||
|
|
||||||
typedef bool (*STREAM_READ)(
|
typedef bool (*STREAM_READ)(
|
||||||
struct TFileStream * pStream, // Pointer to an open stream
|
struct TFileStream * pStream, // Pointer to an open stream
|
||||||
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position
|
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position
|
||||||
void * pvBuffer, // Pointer to data to be read
|
void * pvBuffer, // Pointer to data to be read
|
||||||
DWORD dwBytesToRead // Number of bytes to read from the file
|
DWORD dwBytesToRead // Number of bytes to read from the file
|
||||||
);
|
);
|
||||||
|
|
||||||
typedef bool (*STREAM_WRITE)(
|
typedef bool (*STREAM_WRITE)(
|
||||||
struct TFileStream * pStream, // Pointer to an open stream
|
struct TFileStream * pStream, // Pointer to an open stream
|
||||||
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to the current position
|
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to the current position
|
||||||
const void * pvBuffer, // Pointer to data to be written
|
const void * pvBuffer, // Pointer to data to be written
|
||||||
DWORD dwBytesToWrite // Number of bytes to read from the file
|
DWORD dwBytesToWrite // Number of bytes to read from the file
|
||||||
);
|
);
|
||||||
|
|
||||||
typedef bool (*STREAM_GETPOS)(
|
typedef bool (*STREAM_RESIZE)(
|
||||||
struct TFileStream * pStream, // Pointer to an open stream
|
struct TFileStream * pStream, // Pointer to an open stream
|
||||||
ULONGLONG * pByteOffset // Pointer to store current file position
|
ULONGLONG FileSize // New size for the file, in bytes
|
||||||
);
|
);
|
||||||
|
|
||||||
typedef bool (*STREAM_GETSIZE)(
|
typedef bool (*STREAM_GETSIZE)(
|
||||||
struct TFileStream * pStream, // Pointer to an open stream
|
struct TFileStream * pStream, // Pointer to an open stream
|
||||||
ULONGLONG * pFileSize // Receives the file size, in bytes
|
ULONGLONG * pFileSize // Receives the file size, in bytes
|
||||||
);
|
);
|
||||||
|
|
||||||
typedef bool (*STREAM_SETSIZE)(
|
typedef bool (*STREAM_GETPOS)(
|
||||||
struct TFileStream * pStream, // Pointer to an open stream
|
struct TFileStream * pStream, // Pointer to an open stream
|
||||||
ULONGLONG FileSize // New size for the file, in bytes
|
ULONGLONG * pByteOffset // Pointer to store current file position
|
||||||
);
|
);
|
||||||
|
|
||||||
typedef bool (*STREAM_GETTIME)(
|
|
||||||
struct TFileStream * pStream,
|
|
||||||
ULONGLONG * pFT
|
|
||||||
);
|
|
||||||
|
|
||||||
typedef bool (*STREAM_SWITCH)(
|
|
||||||
struct TFileStream * pStream,
|
|
||||||
struct TFileStream * pNewStream
|
|
||||||
);
|
|
||||||
|
|
||||||
typedef bool (*STREAM_GETBMP)(
|
|
||||||
TFileStream * pStream,
|
|
||||||
TFileBitmap * pBitmap,
|
|
||||||
DWORD Length,
|
|
||||||
LPDWORD LengthNeeded
|
|
||||||
);
|
|
||||||
|
|
||||||
typedef void (*STREAM_CLOSE)(
|
typedef void (*STREAM_CLOSE)(
|
||||||
struct TFileStream * pStream
|
struct TFileStream * pStream // Pointer to an open stream
|
||||||
);
|
);
|
||||||
|
|
||||||
|
typedef bool (*BLOCK_READ)(
|
||||||
|
struct TFileStream * pStream, // Pointer to a block-oriented stream
|
||||||
|
ULONGLONG StartOffset, // Byte offset of start of the block array
|
||||||
|
ULONGLONG EndOffset, // End offset (either end of the block or end of the file)
|
||||||
|
LPBYTE BlockBuffer, // Pointer to block-aligned buffer
|
||||||
|
DWORD BytesNeeded, // Number of bytes that are really needed
|
||||||
|
bool bAvailable // true if the block is available
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef bool (*BLOCK_CHECK)(
|
||||||
|
struct TFileStream * pStream, // Pointer to a block-oriented stream
|
||||||
|
ULONGLONG BlockOffset // Offset of the file to check
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef void (*BLOCK_SAVEMAP)(
|
||||||
|
struct TFileStream * pStream // Pointer to a block-oriented stream
|
||||||
|
);
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Local structures - part file structure
|
// Local structures - partial file structure and bitmap footer
|
||||||
|
|
||||||
|
#define ID_FILE_BITMAP_FOOTER 0x33767470 // Signature of the file bitmap footer ('ptv3')
|
||||||
|
#define DEFAULT_BLOCK_SIZE 0x00004000 // Default size of the stream block
|
||||||
|
#define DEFAULT_BUILD_NUMBER 10958 // Build number for newly created partial MPQs
|
||||||
|
|
||||||
typedef struct _PART_FILE_HEADER
|
typedef struct _PART_FILE_HEADER
|
||||||
{
|
{
|
||||||
|
|
@ -89,99 +108,108 @@ typedef struct _PART_FILE_MAP_ENTRY
|
||||||
|
|
||||||
} PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY;
|
} PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY;
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
typedef struct _FILE_BITMAP_FOOTER
|
||||||
// Local structures
|
{
|
||||||
|
DWORD Signature; // 'ptv3' (ID_FILE_BITMAP_FOOTER)
|
||||||
|
DWORD Version; // Unknown, seems to always have value of 3 (version?)
|
||||||
|
DWORD BuildNumber; // Game build number for that MPQ
|
||||||
|
DWORD MapOffsetLo; // Low 32-bits of the offset of the bit map
|
||||||
|
DWORD MapOffsetHi; // High 32-bits of the offset of the bit map
|
||||||
|
DWORD BlockSize; // Size of one block (usually 0x4000 bytes)
|
||||||
|
|
||||||
union TBaseData
|
} FILE_BITMAP_FOOTER, *PFILE_BITMAP_FOOTER;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Structure for file stream
|
||||||
|
|
||||||
|
union TBaseProviderData
|
||||||
{
|
{
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
ULONGLONG FileSize; // Size of the file
|
ULONGLONG FileSize; // Size of the file
|
||||||
ULONGLONG FilePos; // Current file position
|
ULONGLONG FilePos; // Current file position
|
||||||
ULONGLONG FileTime; // Date/time of last modification of the file
|
ULONGLONG FileTime; // Last write time
|
||||||
HANDLE hFile; // File handle
|
HANDLE hFile; // File handle
|
||||||
} File;
|
} File;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
ULONGLONG FileSize; // Mapped file size
|
ULONGLONG FileSize; // Size of the file
|
||||||
ULONGLONG FilePos; // Current stream position
|
ULONGLONG FilePos; // Current file position
|
||||||
ULONGLONG FileTime; // Date/time of last modification of the file
|
ULONGLONG FileTime; // Last write time
|
||||||
LPBYTE pbFile; // Pointer to mapped view
|
LPBYTE pbFile; // Pointer to mapped view
|
||||||
} Map;
|
} Map;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
ULONGLONG FileSize; // Size of the internet file
|
ULONGLONG FileSize; // Size of the file
|
||||||
ULONGLONG FilePos; // Current position in the file
|
ULONGLONG FilePos; // Current file position
|
||||||
ULONGLONG FileTime; // Date/time of last modification of the file
|
ULONGLONG FileTime; // Last write time
|
||||||
HANDLE hInternet; // Internet handle
|
HANDLE hInternet; // Internet handle
|
||||||
HANDLE hConnect; // Connection to the internet server
|
HANDLE hConnect; // Connection to the internet server
|
||||||
} Http;
|
} Http;
|
||||||
};
|
};
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Structure for linear stream
|
|
||||||
|
|
||||||
struct TFileStream
|
struct TFileStream
|
||||||
{
|
{
|
||||||
// Stream provider functions
|
// Stream provider functions
|
||||||
STREAM_READ StreamRead; // Pointer to stream read function for this archive. Do not use directly.
|
STREAM_READ StreamRead; // Pointer to stream read function for this archive. Do not use directly.
|
||||||
STREAM_WRITE StreamWrite; // Pointer to stream write function for this archive. Do not use directly.
|
STREAM_WRITE StreamWrite; // Pointer to stream write function for this archive. Do not use directly.
|
||||||
STREAM_GETPOS StreamGetPos; // Pointer to function that returns current file position
|
STREAM_RESIZE StreamResize; // Pointer to function changing file size
|
||||||
STREAM_GETSIZE StreamGetSize; // Pointer to function returning file size
|
STREAM_GETSIZE StreamGetSize; // Pointer to function returning file size
|
||||||
STREAM_SETSIZE StreamSetSize; // Pointer to function changing file size
|
STREAM_GETPOS StreamGetPos; // Pointer to function that returns current file position
|
||||||
STREAM_GETTIME StreamGetTime; // Pointer to function retrieving the file time
|
|
||||||
STREAM_GETBMP StreamGetBmp; // Pointer to function that retrieves the file bitmap
|
|
||||||
STREAM_SWITCH StreamSwitch; // Pointer to function changing the stream to another file
|
|
||||||
STREAM_CLOSE StreamClose; // Pointer to function closing the stream
|
STREAM_CLOSE StreamClose; // Pointer to function closing the stream
|
||||||
|
|
||||||
// Stream provider data members
|
// Block-oriented functions
|
||||||
TCHAR szFileName[MAX_PATH]; // File name
|
BLOCK_READ BlockRead; // Pointer to function reading one or more blocks
|
||||||
DWORD dwFlags; // Stream flags
|
BLOCK_CHECK BlockCheck; // Pointer to function checking whether the block is present
|
||||||
|
|
||||||
// Base provider functions
|
// Base provider functions
|
||||||
STREAM_READ BaseRead;
|
STREAM_CREATE BaseCreate; // Pointer to base create function
|
||||||
STREAM_WRITE BaseWrite;
|
STREAM_OPEN BaseOpen; // Pointer to base open function
|
||||||
STREAM_GETPOS BaseGetPos; // Pointer to function that returns current file position
|
STREAM_READ BaseRead; // Read from the stream
|
||||||
|
STREAM_WRITE BaseWrite; // Write to the stream
|
||||||
|
STREAM_RESIZE BaseResize; // Pointer to function changing file size
|
||||||
STREAM_GETSIZE BaseGetSize; // Pointer to function returning file size
|
STREAM_GETSIZE BaseGetSize; // Pointer to function returning file size
|
||||||
STREAM_SETSIZE BaseSetSize; // Pointer to function changing file size
|
STREAM_GETPOS BaseGetPos; // Pointer to function that returns current file position
|
||||||
STREAM_GETTIME BaseGetTime; // Pointer to function retrieving the file time
|
|
||||||
STREAM_CLOSE BaseClose; // Pointer to function closing the stream
|
STREAM_CLOSE BaseClose; // Pointer to function closing the stream
|
||||||
|
|
||||||
// Base provider data members
|
// Base provider data (file size, file position)
|
||||||
TBaseData Base; // Base provider data
|
TBaseProviderData Base;
|
||||||
|
|
||||||
|
// Stream provider data
|
||||||
|
TFileStream * pMaster; // Master stream (e.g. MPQ on a web server)
|
||||||
|
TCHAR * szFileName; // File name (self-relative pointer)
|
||||||
|
|
||||||
|
ULONGLONG StreamSize; // Stream size (can be less than file size)
|
||||||
|
ULONGLONG StreamPos; // Stream position
|
||||||
|
DWORD BuildNumber; // Game build number
|
||||||
|
DWORD dwFlags; // Stream flags
|
||||||
|
|
||||||
// Followed by stream provider data, with variable length
|
// Followed by stream provider data, with variable length
|
||||||
};
|
};
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Structure for linear stream
|
// Structures for block-oriented stream
|
||||||
|
|
||||||
struct TLinearStream : public TFileStream
|
struct TBlockStream : public TFileStream
|
||||||
{
|
{
|
||||||
TFileBitmap * pBitmap; // Pointer to the stream bitmap
|
SFILE_DOWNLOAD_CALLBACK pfnCallback; // Callback for downloading
|
||||||
};
|
void * FileBitmap; // Array of bits for file blocks
|
||||||
|
void * UserData; // User data to be passed to the download callback
|
||||||
//-----------------------------------------------------------------------------
|
DWORD BitmapSize; // Size of the file bitmap (in bytes)
|
||||||
// Structure for partial stream
|
DWORD BlockSize; // Size of one block, in bytes
|
||||||
|
DWORD BlockCount; // Number of data blocks in the file
|
||||||
struct TPartialStream : public TFileStream
|
DWORD IsComplete; // If nonzero, no blocks are missing
|
||||||
{
|
DWORD IsModified; // nonzero if the bitmap has been modified
|
||||||
ULONGLONG VirtualSize; // Virtual size of the file
|
};
|
||||||
ULONGLONG VirtualPos; // Virtual position in the file
|
|
||||||
DWORD BlockCount; // Number of file blocks. Used by partial file stream
|
|
||||||
DWORD BlockSize; // Size of one block. Used by partial file stream
|
|
||||||
|
|
||||||
PPART_FILE_MAP_ENTRY PartMap; // File map, variable length
|
|
||||||
};
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Structure for encrypted stream
|
// Structure for encrypted stream
|
||||||
|
|
||||||
#define MPQE_CHUNK_SIZE 0x40 // Size of one chunk to be decrypted
|
#define MPQE_CHUNK_SIZE 0x40 // Size of one chunk to be decrypted
|
||||||
|
|
||||||
struct TEncryptedStream : public TFileStream
|
struct TEncryptedStream : public TBlockStream
|
||||||
{
|
{
|
||||||
BYTE Key[MPQE_CHUNK_SIZE]; // File key
|
BYTE Key[MPQE_CHUNK_SIZE]; // File key
|
||||||
};
|
};
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -49,14 +49,14 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
printf("== HET Header =================================\n");
|
printf("== HET Header =================================\n");
|
||||||
printf("ULONGLONG AndMask64 = %016llX\n", pHetTable->AndMask64);
|
printf("ULONGLONG AndMask64 = %016llX\n", pHetTable->AndMask64);
|
||||||
printf("ULONGLONG OrMask64 = %016llX\n", pHetTable->OrMask64);
|
printf("ULONGLONG OrMask64 = %016llX\n", pHetTable->OrMask64);
|
||||||
printf("DWORD dwIndexSizeTotal = %08X\n", pHetTable->dwIndexSizeTotal);
|
printf("DWORD dwIndexSizeTotal = %08X\n", pHetTable->dwIndexSizeTotal);
|
||||||
printf("DWORD dwIndexSizeExtra = %08X\n", pHetTable->dwIndexSizeExtra);
|
printf("DWORD dwIndexSizeExtra = %08X\n", pHetTable->dwIndexSizeExtra);
|
||||||
printf("DWORD dwIndexSize = %08X\n", pHetTable->dwIndexSize);
|
printf("DWORD dwIndexSize = %08X\n", pHetTable->dwIndexSize);
|
||||||
printf("DWORD dwMaxFileCount = %08X\n", pHetTable->dwMaxFileCount);
|
printf("DWORD dwMaxFileCount = %08X\n", pHetTable->dwMaxFileCount);
|
||||||
printf("DWORD dwHashTableSize = %08X\n", pHetTable->dwHashTableSize);
|
printf("DWORD dwHashTableSize = %08X\n", pHetTable->dwHashTableSize);
|
||||||
printf("DWORD dwHashBitSize = %08X\n", pHetTable->dwHashBitSize);
|
printf("DWORD dwHashBitSize = %08X\n", pHetTable->dwHashBitSize);
|
||||||
printf("-----------------------------------------------\n\n");
|
printf("-----------------------------------------------\n\n");
|
||||||
|
|
||||||
printf("== BET Header =================================\n");
|
printf("== BET Header =================================\n");
|
||||||
|
|
@ -69,7 +69,7 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable)
|
||||||
printf("DWORD dwBitCount_FilePos = %08X\n", pBetTable->dwBitCount_FilePos);
|
printf("DWORD dwBitCount_FilePos = %08X\n", pBetTable->dwBitCount_FilePos);
|
||||||
printf("DWORD dwBitCount_FileSize = %08X\n", pBetTable->dwBitCount_FileSize);
|
printf("DWORD dwBitCount_FileSize = %08X\n", pBetTable->dwBitCount_FileSize);
|
||||||
printf("DWORD dwBitCount_CmpSize = %08X\n", pBetTable->dwBitCount_CmpSize);
|
printf("DWORD dwBitCount_CmpSize = %08X\n", pBetTable->dwBitCount_CmpSize);
|
||||||
printf("DWORD dwBitCount_FlagIndex = %08X\n", pBetTable->dwBitCount_FlagIndex);
|
printf("DWORD dwBitCount_FlagIndex = %08X\n", pBetTable->dwBitCount_FlagIndex);
|
||||||
printf("DWORD dwBitCount_Unknown = %08X\n", pBetTable->dwBitCount_Unknown);
|
printf("DWORD dwBitCount_Unknown = %08X\n", pBetTable->dwBitCount_Unknown);
|
||||||
printf("DWORD dwBetHashSizeTotal = %08X\n", pBetTable->dwBetHashSizeTotal);
|
printf("DWORD dwBetHashSizeTotal = %08X\n", pBetTable->dwBetHashSizeTotal);
|
||||||
printf("DWORD dwBetHashSizeExtra = %08X\n", pBetTable->dwBetHashSizeExtra);
|
printf("DWORD dwBetHashSizeExtra = %08X\n", pBetTable->dwBetHashSizeExtra);
|
||||||
|
|
@ -93,50 +93,50 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable)
|
||||||
|
|
||||||
GetBits(pHetTable->pBetIndexes, i * pHetTable->dwIndexSizeTotal,
|
GetBits(pHetTable->pBetIndexes, i * pHetTable->dwIndexSizeTotal,
|
||||||
pHetTable->dwIndexSize,
|
pHetTable->dwIndexSize,
|
||||||
&dwBetIndex,
|
&dwBetIndex,
|
||||||
4);
|
4);
|
||||||
|
|
||||||
if(dwBetIndex < pHetTable->dwMaxFileCount)
|
if(dwBetIndex < pHetTable->dwMaxFileCount)
|
||||||
{
|
{
|
||||||
DWORD dwEntryIndex = pBetTable->dwTableEntrySize * dwBetIndex;
|
DWORD dwEntryIndex = pBetTable->dwTableEntrySize * dwBetIndex;
|
||||||
|
|
||||||
GetBits(pBetTable->pBetHashes, dwBetIndex * pBetTable->dwBetHashSizeTotal,
|
GetBits(pBetTable->pBetHashes, dwBetIndex * pBetTable->dwBetHashSizeTotal,
|
||||||
pBetTable->dwBetHashSize,
|
pBetTable->dwBetHashSize,
|
||||||
&BetHash,
|
&BetHash,
|
||||||
8);
|
8);
|
||||||
|
|
||||||
GetBits(dwEntryIndex + pBetTable->dwBitIndex_FilePos,
|
GetBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FilePos,
|
||||||
pBetTable->dwBitCount_FilePos,
|
pBetTable->dwBitCount_FilePos,
|
||||||
&ByteOffset,
|
&ByteOffset,
|
||||||
8);
|
8);
|
||||||
|
|
||||||
GetBits(dwEntryIndex + pBetTable->dwBitIndex_FileSize,
|
GetBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FileSize,
|
||||||
pBetTable->dwBitCount_FileSize,
|
pBetTable->dwBitCount_FileSize,
|
||||||
&dwFileSize,
|
&dwFileSize,
|
||||||
4);
|
4);
|
||||||
|
|
||||||
GetBits(dwEntryIndex + pBetTable->dwBitIndex_CmpSize,
|
GetBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_CmpSize,
|
||||||
pBetTable->dwBitCount_CmpSize,
|
pBetTable->dwBitCount_CmpSize,
|
||||||
&dwCmpSize,
|
&dwCmpSize,
|
||||||
4);
|
4);
|
||||||
|
|
||||||
GetBits(dwEntryIndex + pBetTable->dwBitIndex_FlagIndex,
|
GetBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FlagIndex,
|
||||||
pBetTable->dwBitCount_FlagIndex,
|
pBetTable->dwBitCount_FlagIndex,
|
||||||
&dwFlagIndex,
|
&dwFlagIndex,
|
||||||
4);
|
4);
|
||||||
|
|
||||||
dwFlags = pBetTable->pFileFlags[dwFlagIndex];
|
dwFlags = pBetTable->pFileFlags[dwFlagIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(" %04X %02lX %04X %016llX %016llX %08X %08X %04X %08X\n", i,
|
printf(" %04X %02lX %04X %016llX %016llX %08X %08X %04X %08X\n", i,
|
||||||
pHetTable->pHetHashes[i],
|
pHetTable->pHetHashes[i],
|
||||||
dwBetIndex,
|
dwBetIndex,
|
||||||
BetHash,
|
BetHash,
|
||||||
ByteOffset,
|
ByteOffset,
|
||||||
dwFileSize,
|
dwFileSize,
|
||||||
dwCmpSize,
|
dwCmpSize,
|
||||||
dwFlagIndex,
|
dwFlagIndex,
|
||||||
dwFlags);
|
dwFlags);
|
||||||
}
|
}
|
||||||
printf("-----------------------------------------------------------------------------------------\n");
|
printf("-----------------------------------------------------------------------------------------\n");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
620
dep/StormLib/src/SBaseSubTypes.cpp
Normal file
620
dep/StormLib/src/SBaseSubTypes.cpp
Normal file
|
|
@ -0,0 +1,620 @@
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* SBaseSubTypes.cpp Copyright (c) Ladislav Zezula 2013 */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Conversion routines for archive formats that are similar to MPQ format */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Date Ver Who Comment */
|
||||||
|
/* -------- ---- --- ------- */
|
||||||
|
/* 02.11.11 1.00 Lad The first version of SBaseSubTypes.cpp */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#define __STORMLIB_SELF__
|
||||||
|
#include "StormLib.h"
|
||||||
|
#include "StormCommon.h"
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Support for SQP file format (War of the Immortals) */
|
||||||
|
/* */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct _TSQPHeader
|
||||||
|
{
|
||||||
|
// The ID_MPQ ('MPQ\x1A') signature
|
||||||
|
DWORD dwID;
|
||||||
|
|
||||||
|
// Size of the archive header
|
||||||
|
DWORD dwHeaderSize;
|
||||||
|
|
||||||
|
// 32-bit size of MPQ archive
|
||||||
|
DWORD dwArchiveSize;
|
||||||
|
|
||||||
|
// Offset to the beginning of the hash table, relative to the beginning of the archive.
|
||||||
|
DWORD dwHashTablePos;
|
||||||
|
|
||||||
|
// Offset to the beginning of the block table, relative to the beginning of the archive.
|
||||||
|
DWORD dwBlockTablePos;
|
||||||
|
|
||||||
|
// Number of entries in the hash table. Must be a power of two, and must be less than 2^16 for
|
||||||
|
// the original MoPaQ format, or less than 2^20 for the Burning Crusade format.
|
||||||
|
DWORD dwHashTableSize;
|
||||||
|
|
||||||
|
// Number of entries in the block table
|
||||||
|
DWORD dwBlockTableSize;
|
||||||
|
|
||||||
|
// Must be zero for SQP files
|
||||||
|
USHORT wFormatVersion;
|
||||||
|
|
||||||
|
// Power of two exponent specifying the number of 512-byte disk sectors in each file sector
|
||||||
|
// in the archive. The size of each file sector in the archive is 512 * 2 ^ wSectorSize.
|
||||||
|
USHORT wSectorSize;
|
||||||
|
|
||||||
|
} TSQPHeader;
|
||||||
|
|
||||||
|
typedef struct _TSQPHash
|
||||||
|
{
|
||||||
|
// Most likely the lcLocale+wPlatform.
|
||||||
|
DWORD dwAlwaysZero;
|
||||||
|
|
||||||
|
// If the hash table entry is valid, this is the index into the block table of the file.
|
||||||
|
// Otherwise, one of the following two values:
|
||||||
|
// - FFFFFFFFh: Hash table entry is empty, and has always been empty.
|
||||||
|
// Terminates searches for a given file.
|
||||||
|
// - FFFFFFFEh: Hash table entry is empty, but was valid at some point (a deleted file).
|
||||||
|
// Does not terminate searches for a given file.
|
||||||
|
DWORD dwBlockIndex;
|
||||||
|
|
||||||
|
// The hash of the file path, using method A.
|
||||||
|
DWORD dwName1;
|
||||||
|
|
||||||
|
// The hash of the file path, using method B.
|
||||||
|
DWORD dwName2;
|
||||||
|
|
||||||
|
} TSQPHash;
|
||||||
|
|
||||||
|
typedef struct _TSQPBlock
|
||||||
|
{
|
||||||
|
// Offset of the beginning of the file, relative to the beginning of the archive.
|
||||||
|
DWORD dwFilePos;
|
||||||
|
|
||||||
|
// Flags for the file. See MPQ_FILE_XXXX constants
|
||||||
|
DWORD dwFlags;
|
||||||
|
|
||||||
|
// Compressed file size
|
||||||
|
DWORD dwCSize;
|
||||||
|
|
||||||
|
// Uncompressed file size
|
||||||
|
DWORD dwFSize;
|
||||||
|
|
||||||
|
} TSQPBlock;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Functions - SQP file format
|
||||||
|
|
||||||
|
// This function converts SQP file header into MPQ file header
|
||||||
|
int ConvertSqpHeaderToFormat4(
|
||||||
|
TMPQArchive * ha,
|
||||||
|
ULONGLONG FileSize,
|
||||||
|
DWORD dwFlags)
|
||||||
|
{
|
||||||
|
TSQPHeader * pSqpHeader = (TSQPHeader *)ha->HeaderData;
|
||||||
|
TMPQHeader Header;
|
||||||
|
|
||||||
|
// SQP files from War of the Immortal use MPQ file format with slightly
|
||||||
|
// modified structure. These fields have different position:
|
||||||
|
//
|
||||||
|
// Offset TMPQHeader TSQPHeader
|
||||||
|
// ------ ---------- -----------
|
||||||
|
// 000C wFormatVersion dwHashTablePos (lo)
|
||||||
|
// 000E wSectorSize dwHashTablePos (hi)
|
||||||
|
// 001C dwBlockTableSize (lo) wBlockSize
|
||||||
|
// 001E dwHashTableSize (hi) wFormatVersion
|
||||||
|
|
||||||
|
// Can't open the archive with certain flags
|
||||||
|
if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
|
||||||
|
return ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
|
// The file must not be greater than 4 GB
|
||||||
|
if((FileSize >> 0x20) != 0)
|
||||||
|
return ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
|
// Translate the SQP header into a MPQ header
|
||||||
|
memset(&Header, 0, sizeof(TMPQHeader));
|
||||||
|
Header.dwID = BSWAP_INT32_UNSIGNED(pSqpHeader->dwID);
|
||||||
|
Header.dwHeaderSize = BSWAP_INT32_UNSIGNED(pSqpHeader->dwHeaderSize);
|
||||||
|
Header.dwArchiveSize = BSWAP_INT32_UNSIGNED(pSqpHeader->dwArchiveSize);
|
||||||
|
Header.dwHashTablePos = BSWAP_INT32_UNSIGNED(pSqpHeader->dwHashTablePos);
|
||||||
|
Header.dwBlockTablePos = BSWAP_INT32_UNSIGNED(pSqpHeader->dwBlockTablePos);
|
||||||
|
Header.dwHashTableSize = BSWAP_INT32_UNSIGNED(pSqpHeader->dwHashTableSize);
|
||||||
|
Header.dwBlockTableSize = BSWAP_INT32_UNSIGNED(pSqpHeader->dwBlockTableSize);
|
||||||
|
Header.wFormatVersion = BSWAP_INT16_UNSIGNED(pSqpHeader->wFormatVersion);
|
||||||
|
Header.wSectorSize = BSWAP_INT16_UNSIGNED(pSqpHeader->wSectorSize);
|
||||||
|
|
||||||
|
// Verify the SQP header
|
||||||
|
if(Header.dwID == ID_MPQ && Header.dwHeaderSize == sizeof(TSQPHeader) && Header.dwArchiveSize == FileSize)
|
||||||
|
{
|
||||||
|
// Check for fixed values of version and sector size
|
||||||
|
if(Header.wFormatVersion == MPQ_FORMAT_VERSION_1 && Header.wSectorSize == 3)
|
||||||
|
{
|
||||||
|
// Initialize the fields of 3.0 header
|
||||||
|
Header.ArchiveSize64 = Header.dwArchiveSize;
|
||||||
|
Header.HashTableSize64 = Header.dwHashTableSize * sizeof(TMPQHash);
|
||||||
|
Header.BlockTableSize64 = Header.dwBlockTableSize * sizeof(TMPQBlock);
|
||||||
|
|
||||||
|
// Copy the converted MPQ header back
|
||||||
|
memcpy(ha->HeaderData, &Header, sizeof(TMPQHeader));
|
||||||
|
|
||||||
|
// Mark this file as SQP file
|
||||||
|
ha->pfnHashString = HashStringSlash;
|
||||||
|
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
|
||||||
|
ha->dwSubType = MPQ_SUBTYPE_SQP;
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_FILE_CORRUPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void * LoadSqpTable(TMPQArchive * ha, DWORD dwByteOffset, DWORD cbTableSize, DWORD dwKey)
|
||||||
|
{
|
||||||
|
ULONGLONG ByteOffset;
|
||||||
|
LPBYTE pbSqpTable;
|
||||||
|
|
||||||
|
// Allocate buffer for the table
|
||||||
|
pbSqpTable = STORM_ALLOC(BYTE, cbTableSize);
|
||||||
|
if(pbSqpTable != NULL)
|
||||||
|
{
|
||||||
|
// Load the table
|
||||||
|
ByteOffset = ha->MpqPos + dwByteOffset;
|
||||||
|
if(FileStream_Read(ha->pStream, &ByteOffset, pbSqpTable, cbTableSize))
|
||||||
|
{
|
||||||
|
// Decrypt the SQP table
|
||||||
|
DecryptMpqBlock(pbSqpTable, cbTableSize, dwKey);
|
||||||
|
return pbSqpTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the table
|
||||||
|
STORM_FREE(pbSqpTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
TMPQHash * LoadSqpHashTable(TMPQArchive * ha)
|
||||||
|
{
|
||||||
|
TMPQHeader * pHeader = ha->pHeader;
|
||||||
|
TSQPHash * pSqpHashTable;
|
||||||
|
TSQPHash * pSqpHashEnd;
|
||||||
|
TSQPHash * pSqpHash;
|
||||||
|
TMPQHash * pMpqHash;
|
||||||
|
DWORD dwBlockIndex;
|
||||||
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// Load the hash table
|
||||||
|
pSqpHashTable = (TSQPHash *)LoadSqpTable(ha, pHeader->dwHashTablePos, pHeader->dwHashTableSize * sizeof(TSQPHash), MPQ_KEY_HASH_TABLE);
|
||||||
|
if(pSqpHashTable != NULL)
|
||||||
|
{
|
||||||
|
// Parse the entire hash table and convert it to MPQ hash table
|
||||||
|
pSqpHashEnd = pSqpHashTable + pHeader->dwHashTableSize;
|
||||||
|
pMpqHash = (TMPQHash *)pSqpHashTable;
|
||||||
|
for(pSqpHash = pSqpHashTable; pSqpHash < pSqpHashEnd; pSqpHash++, pMpqHash++)
|
||||||
|
{
|
||||||
|
// Ignore free entries
|
||||||
|
if(pSqpHash->dwBlockIndex != HASH_ENTRY_FREE)
|
||||||
|
{
|
||||||
|
// Check block index against the size of the block table
|
||||||
|
dwBlockIndex = pSqpHash->dwBlockIndex;
|
||||||
|
if(pHeader->dwBlockTableSize <= dwBlockIndex && dwBlockIndex < HASH_ENTRY_DELETED)
|
||||||
|
nError = ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
|
// We do not support nonzero locale and platform ID
|
||||||
|
if(pSqpHash->dwAlwaysZero != 0 && pSqpHash->dwAlwaysZero != HASH_ENTRY_FREE)
|
||||||
|
nError = ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
|
// Store the file name hash
|
||||||
|
pMpqHash->dwName1 = pSqpHash->dwName1;
|
||||||
|
pMpqHash->dwName2 = pSqpHash->dwName2;
|
||||||
|
|
||||||
|
// Store the rest. Note that this must be done last,
|
||||||
|
// because pSqpHash->dwBlockIndex corresponds to pMpqHash->dwName2
|
||||||
|
pMpqHash->dwBlockIndex = dwBlockIndex;
|
||||||
|
pMpqHash->wPlatform = 0;
|
||||||
|
pMpqHash->lcLocale = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an error occured, we need to free the hash table
|
||||||
|
if(nError != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
STORM_FREE(pSqpHashTable);
|
||||||
|
pSqpHashTable = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the converted hash table (or NULL on failure)
|
||||||
|
return (TMPQHash *)pSqpHashTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads the SQP Block table and converts it to a MPQ block table
|
||||||
|
TMPQBlock * LoadSqpBlockTable(TMPQArchive * ha)
|
||||||
|
{
|
||||||
|
TMPQHeader * pHeader = ha->pHeader;
|
||||||
|
TSQPBlock * pSqpBlockTable;
|
||||||
|
TSQPBlock * pSqpBlockEnd;
|
||||||
|
TSQPBlock * pSqpBlock;
|
||||||
|
TMPQBlock * pMpqBlock;
|
||||||
|
DWORD dwFlags;
|
||||||
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// Load the hash table
|
||||||
|
pSqpBlockTable = (TSQPBlock *)LoadSqpTable(ha, pHeader->dwBlockTablePos, pHeader->dwBlockTableSize * sizeof(TSQPBlock), MPQ_KEY_BLOCK_TABLE);
|
||||||
|
if(pSqpBlockTable != NULL)
|
||||||
|
{
|
||||||
|
// Parse the entire hash table and convert it to MPQ hash table
|
||||||
|
pSqpBlockEnd = pSqpBlockTable + pHeader->dwBlockTableSize;
|
||||||
|
pMpqBlock = (TMPQBlock *)pSqpBlockTable;
|
||||||
|
for(pSqpBlock = pSqpBlockTable; pSqpBlock < pSqpBlockEnd; pSqpBlock++, pMpqBlock++)
|
||||||
|
{
|
||||||
|
// Check for valid flags
|
||||||
|
if(pSqpBlock->dwFlags & ~MPQ_FILE_VALID_FLAGS)
|
||||||
|
nError = ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
|
// Convert SQP block table entry to MPQ block table entry
|
||||||
|
dwFlags = pSqpBlock->dwFlags;
|
||||||
|
pMpqBlock->dwCSize = pSqpBlock->dwCSize;
|
||||||
|
pMpqBlock->dwFSize = pSqpBlock->dwFSize;
|
||||||
|
pMpqBlock->dwFlags = dwFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an error occured, we need to free the hash table
|
||||||
|
if(nError != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
STORM_FREE(pSqpBlockTable);
|
||||||
|
pSqpBlockTable = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the converted hash table (or NULL on failure)
|
||||||
|
return (TMPQBlock *)pSqpBlockTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* Support for MPK file format (Longwu Online) */
|
||||||
|
/* */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#define MPK_FILE_UNKNOWN_0001 0x00000001 // Seems to be always present
|
||||||
|
#define MPK_FILE_UNKNOWN_0010 0x00000010 // Seems to be always present
|
||||||
|
#define MPK_FILE_COMPRESSED 0x00000100 // Indicates a compressed file
|
||||||
|
#define MPK_FILE_UNKNOWN_2000 0x00002000 // Seems to be always present
|
||||||
|
#define MPK_FILE_EXISTS 0x01000000 // Seems to be always present
|
||||||
|
|
||||||
|
typedef struct _TMPKHeader
|
||||||
|
{
|
||||||
|
// The ID_MPK ('MPK\x1A') signature
|
||||||
|
DWORD dwID;
|
||||||
|
|
||||||
|
// Contains '2000'
|
||||||
|
DWORD dwVersion;
|
||||||
|
|
||||||
|
// 32-bit size of the archive
|
||||||
|
DWORD dwArchiveSize;
|
||||||
|
|
||||||
|
// Size of the archive header
|
||||||
|
DWORD dwHeaderSize;
|
||||||
|
|
||||||
|
DWORD dwHashTablePos;
|
||||||
|
DWORD dwHashTableSize;
|
||||||
|
DWORD dwBlockTablePos;
|
||||||
|
DWORD dwBlockTableSize;
|
||||||
|
DWORD dwUnknownPos;
|
||||||
|
DWORD dwUnknownSize;
|
||||||
|
} TMPKHeader;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _TMPKHash
|
||||||
|
{
|
||||||
|
// The hash of the file path, using method A.
|
||||||
|
DWORD dwName1;
|
||||||
|
|
||||||
|
// The hash of the file path, using method B.
|
||||||
|
DWORD dwName2;
|
||||||
|
|
||||||
|
// The hash of the file path, using method C.
|
||||||
|
DWORD dwName3;
|
||||||
|
|
||||||
|
// If the hash table entry is valid, this is the index into the block table of the file.
|
||||||
|
// Otherwise, one of the following two values:
|
||||||
|
// - FFFFFFFFh: Hash table entry is empty, and has always been empty.
|
||||||
|
// Terminates searches for a given file.
|
||||||
|
// - FFFFFFFEh: Hash table entry is empty, but was valid at some point (a deleted file).
|
||||||
|
// Does not terminate searches for a given file.
|
||||||
|
DWORD dwBlockIndex;
|
||||||
|
|
||||||
|
} TMPKHash;
|
||||||
|
|
||||||
|
typedef struct _TMPKBlock
|
||||||
|
{
|
||||||
|
DWORD dwFlags; // 0x1121 - Compressed , 0x1120 - Not compressed
|
||||||
|
DWORD dwFilePos; // Offset of the beginning of the file, relative to the beginning of the archive.
|
||||||
|
DWORD dwFSize; // Uncompressed file size
|
||||||
|
DWORD dwCSize; // Compressed file size
|
||||||
|
DWORD dwUnknown; // 0x86364E6D
|
||||||
|
} TMPKBlock;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Local variables - MPK file format
|
||||||
|
|
||||||
|
static const unsigned char MpkDecryptionKey[512] =
|
||||||
|
{
|
||||||
|
0x60, 0x20, 0x29, 0xE1, 0x01, 0xCE, 0xAA, 0xFE, 0xA3, 0xAB, 0x8E, 0x30, 0xAF, 0x02, 0xD1, 0x7D,
|
||||||
|
0x41, 0x24, 0x06, 0xBD, 0xAE, 0xBE, 0x43, 0xC3, 0xBA, 0xB7, 0x08, 0x13, 0x51, 0xCF, 0xF8, 0xF7,
|
||||||
|
0x25, 0x42, 0xA5, 0x4A, 0xDA, 0x0F, 0x52, 0x1C, 0x90, 0x3B, 0x63, 0x49, 0x36, 0xF6, 0xDD, 0x1B,
|
||||||
|
0xEA, 0x58, 0xD4, 0x40, 0x70, 0x61, 0x55, 0x09, 0xCD, 0x0B, 0xA2, 0x4B, 0x68, 0x2C, 0x8A, 0xF1,
|
||||||
|
0x3C, 0x3A, 0x65, 0xBB, 0xA1, 0xA8, 0x23, 0x97, 0xFD, 0x15, 0x00, 0x94, 0x88, 0x33, 0x59, 0xE9,
|
||||||
|
0xFB, 0x69, 0x21, 0xEF, 0x85, 0x5B, 0x57, 0x6C, 0xFA, 0xB5, 0xEE, 0xB8, 0x71, 0xDC, 0xB1, 0x38,
|
||||||
|
0x0C, 0x0A, 0x5C, 0x56, 0xC9, 0xB4, 0x84, 0x17, 0x1E, 0xE5, 0xD3, 0x5A, 0xCC, 0xFC, 0x11, 0x86,
|
||||||
|
0x7F, 0x45, 0x4F, 0x54, 0xC8, 0x8D, 0x73, 0x89, 0x79, 0x5D, 0xB3, 0xBF, 0xB9, 0xE3, 0x93, 0xE4,
|
||||||
|
0x6F, 0x35, 0x2D, 0x46, 0xF2, 0x76, 0xC5, 0x7E, 0xE2, 0xA4, 0xE6, 0xD9, 0x6E, 0x48, 0x34, 0x2B,
|
||||||
|
0xC6, 0x5F, 0xBC, 0xA0, 0x6D, 0x0D, 0x47, 0x6B, 0x95, 0x96, 0x92, 0x91, 0xB2, 0x27, 0xEB, 0x9E,
|
||||||
|
0xEC, 0x8F, 0xDF, 0x9C, 0x74, 0x99, 0x64, 0xF5, 0xFF, 0x28, 0xB6, 0x37, 0xF3, 0x7C, 0x81, 0x03,
|
||||||
|
0x44, 0x62, 0x1F, 0xDB, 0x04, 0x7B, 0xB0, 0x9B, 0x31, 0xA7, 0xDE, 0x78, 0x9F, 0xAD, 0x0E, 0x3F,
|
||||||
|
0x3E, 0x4D, 0xC7, 0xD7, 0x39, 0x19, 0x5E, 0xC2, 0xD0, 0xAC, 0xE8, 0x1A, 0x87, 0x8B, 0x07, 0x05,
|
||||||
|
0x22, 0xED, 0x72, 0x2E, 0x1D, 0xC1, 0xA9, 0xD6, 0xE0, 0x83, 0xD5, 0xD8, 0xCB, 0x80, 0xF0, 0x66,
|
||||||
|
0x7A, 0x9D, 0x50, 0xF9, 0x10, 0x4E, 0x16, 0x14, 0x77, 0x75, 0x6A, 0x67, 0xD2, 0xC0, 0xA6, 0xC4,
|
||||||
|
0x53, 0x8C, 0x32, 0xCA, 0x82, 0x2A, 0x18, 0x9A, 0xF4, 0x4C, 0x3D, 0x26, 0x12, 0xE7, 0x98, 0x2F,
|
||||||
|
0x4A, 0x04, 0x0D, 0xAF, 0xB4, 0xCF, 0x12, 0xCE, 0x1A, 0x37, 0x61, 0x39, 0x60, 0x95, 0xBE, 0x25,
|
||||||
|
0xE4, 0x6E, 0xFC, 0x1B, 0xE7, 0x49, 0xE6, 0x67, 0xF6, 0xC5, 0xCB, 0x2F, 0x27, 0xD4, 0x68, 0xB2,
|
||||||
|
0x01, 0x52, 0xD0, 0x46, 0x11, 0x20, 0xFB, 0x9D, 0xA9, 0x02, 0xF5, 0x8F, 0x3D, 0x82, 0xD3, 0xFF,
|
||||||
|
0x0B, 0xB8, 0xF2, 0x4D, 0x8E, 0x81, 0x2C, 0xAB, 0x5F, 0xC4, 0x41, 0x29, 0x40, 0xFA, 0xC0, 0xBF,
|
||||||
|
0x33, 0x10, 0x21, 0x16, 0xB0, 0x71, 0x83, 0x96, 0x8D, 0x2B, 0x23, 0x3B, 0xF9, 0xC1, 0xE5, 0x72,
|
||||||
|
0xE2, 0x1C, 0x26, 0xF0, 0x73, 0x36, 0x63, 0x56, 0x31, 0x4E, 0x6B, 0x55, 0x62, 0x79, 0xC6, 0x91,
|
||||||
|
0x00, 0x35, 0xB1, 0x2A, 0xA6, 0x42, 0xDF, 0xEB, 0x3C, 0x51, 0xEA, 0x97, 0x57, 0x94, 0x8C, 0x80,
|
||||||
|
0x34, 0x5C, 0xD2, 0x76, 0xA4, 0xE9, 0x85, 0xE8, 0xBB, 0x78, 0xE0, 0xB5, 0xAD, 0x0F, 0x87, 0x70,
|
||||||
|
0xDD, 0xAE, 0xF4, 0xD9, 0x66, 0x54, 0x6F, 0xCC, 0x4C, 0x77, 0x3E, 0xCD, 0xF1, 0x75, 0x0A, 0xA1,
|
||||||
|
0x28, 0x9B, 0x9A, 0x7E, 0x4B, 0x98, 0x99, 0x47, 0xFE, 0xA5, 0xF7, 0xB7, 0xA3, 0xE1, 0x9F, 0xBC,
|
||||||
|
0x93, 0x44, 0x3A, 0x08, 0x89, 0x22, 0xEE, 0xB9, 0x45, 0xD6, 0x06, 0x09, 0xC9, 0xBD, 0x14, 0x0C,
|
||||||
|
0xB6, 0x5E, 0x9C, 0x7A, 0x65, 0x59, 0xAA, 0x19, 0x5B, 0x7C, 0x18, 0x43, 0x92, 0x13, 0x15, 0x7B,
|
||||||
|
0xED, 0xD5, 0xC7, 0x17, 0xEF, 0x86, 0x90, 0xC2, 0x74, 0x64, 0xF3, 0xDC, 0x6C, 0x38, 0x05, 0x1D,
|
||||||
|
0xC8, 0x0E, 0xEC, 0x6A, 0x32, 0xDA, 0xD7, 0xC3, 0xDB, 0x8B, 0x24, 0xB3, 0x5D, 0x2E, 0xBA, 0xA2,
|
||||||
|
0xD8, 0x03, 0x88, 0x7D, 0x7F, 0x69, 0x8A, 0xFD, 0xCA, 0x4F, 0x30, 0x9E, 0xA0, 0xD1, 0x5A, 0x53,
|
||||||
|
0xDE, 0x3F, 0x84, 0xAC, 0xF8, 0xA7, 0x2D, 0x1F, 0x1E, 0xE3, 0x58, 0x50, 0x6D, 0x48, 0x07, 0xA8
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Functions - MPK file format
|
||||||
|
|
||||||
|
// This function converts MPK file header into MPQ file header
|
||||||
|
int ConvertMpkHeaderToFormat4(
|
||||||
|
TMPQArchive * ha,
|
||||||
|
ULONGLONG FileSize,
|
||||||
|
DWORD dwFlags)
|
||||||
|
{
|
||||||
|
TMPKHeader * pMpkHeader = (TMPKHeader *)ha->HeaderData;
|
||||||
|
TMPQHeader Header;
|
||||||
|
|
||||||
|
// Can't open the archive with certain flags
|
||||||
|
if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
|
||||||
|
return ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
|
// Translate the MPK header into a MPQ header
|
||||||
|
// Note: Hash table size and block table size are in bytes, not in entries
|
||||||
|
memset(&Header, 0, sizeof(TMPQHeader));
|
||||||
|
Header.dwID = BSWAP_INT32_UNSIGNED(pMpkHeader->dwID);
|
||||||
|
Header.dwArchiveSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwArchiveSize);
|
||||||
|
Header.dwHeaderSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwHeaderSize);
|
||||||
|
Header.dwHashTablePos = BSWAP_INT32_UNSIGNED(pMpkHeader->dwHashTablePos);
|
||||||
|
Header.dwHashTableSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwHashTableSize) / sizeof(TMPKHash);
|
||||||
|
Header.dwBlockTablePos = BSWAP_INT32_UNSIGNED(pMpkHeader->dwBlockTablePos);
|
||||||
|
Header.dwBlockTableSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwBlockTableSize) / sizeof(TMPKBlock);
|
||||||
|
// Header.dwUnknownPos = BSWAP_INT32_UNSIGNED(pMpkHeader->dwUnknownPos);
|
||||||
|
// Header.dwUnknownSize = BSWAP_INT32_UNSIGNED(pMpkHeader->dwUnknownSize);
|
||||||
|
assert(Header.dwHeaderSize == sizeof(TMPKHeader));
|
||||||
|
|
||||||
|
// Verify the MPK header
|
||||||
|
if(Header.dwID == ID_MPK && Header.dwHeaderSize == sizeof(TMPKHeader) && Header.dwArchiveSize == (DWORD)FileSize)
|
||||||
|
{
|
||||||
|
// The header ID must be ID_MPQ
|
||||||
|
Header.dwID = ID_MPQ;
|
||||||
|
Header.wFormatVersion = MPQ_FORMAT_VERSION_1;
|
||||||
|
Header.wSectorSize = 3;
|
||||||
|
|
||||||
|
// Initialize the fields of 3.0 header
|
||||||
|
Header.ArchiveSize64 = Header.dwArchiveSize;
|
||||||
|
Header.HashTableSize64 = Header.dwHashTableSize * sizeof(TMPQHash);
|
||||||
|
Header.BlockTableSize64 = Header.dwBlockTableSize * sizeof(TMPQBlock);
|
||||||
|
|
||||||
|
// Copy the converted MPQ header back
|
||||||
|
memcpy(ha->HeaderData, &Header, sizeof(TMPQHeader));
|
||||||
|
|
||||||
|
// Mark this file as MPK file
|
||||||
|
ha->pfnHashString = HashStringLower;
|
||||||
|
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
|
||||||
|
ha->dwSubType = MPQ_SUBTYPE_MPK;
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
return ERROR_FILE_CORRUPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempts to search a free hash entry in the hash table being converted.
|
||||||
|
// The created hash table must always be of nonzero size,
|
||||||
|
// should have no duplicated items and no deleted entries
|
||||||
|
TMPQHash * FindFreeHashEntry(TMPQHash * pHashTable, DWORD dwHashTableSize, DWORD dwStartIndex)
|
||||||
|
{
|
||||||
|
TMPQHash * pHash;
|
||||||
|
DWORD dwIndex;
|
||||||
|
|
||||||
|
// Set the initial index
|
||||||
|
dwStartIndex = dwIndex = (dwStartIndex & (dwHashTableSize - 1));
|
||||||
|
assert(dwHashTableSize != 0);
|
||||||
|
|
||||||
|
// Search the hash table and return the found entries in the following priority:
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
// We are not expecting to find matching entry in the hash table being built
|
||||||
|
// We are not expecting to find deleted entry either
|
||||||
|
pHash = pHashTable + dwIndex;
|
||||||
|
|
||||||
|
// If we found a free entry, we need to stop searching
|
||||||
|
if(pHash->dwBlockIndex == HASH_ENTRY_FREE)
|
||||||
|
return pHash;
|
||||||
|
|
||||||
|
// Move to the next hash entry.
|
||||||
|
// If we reached the starting entry, it's failure.
|
||||||
|
dwIndex = (dwIndex + 1) & (dwHashTableSize - 1);
|
||||||
|
if(dwIndex == dwStartIndex)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We haven't found anything
|
||||||
|
assert(false);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DecryptMpkTable(void * pvMpkTable, size_t cbSize)
|
||||||
|
{
|
||||||
|
LPBYTE pbMpkTable = (LPBYTE)pvMpkTable;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < cbSize; i++)
|
||||||
|
pbMpkTable[i] = MpkDecryptionKey[pbMpkTable[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
void * LoadMpkTable(TMPQArchive * ha, DWORD dwByteOffset, DWORD cbTableSize)
|
||||||
|
{
|
||||||
|
ULONGLONG ByteOffset;
|
||||||
|
LPBYTE pbMpkTable = NULL;
|
||||||
|
|
||||||
|
// Allocate space for the table
|
||||||
|
pbMpkTable = STORM_ALLOC(BYTE, cbTableSize);
|
||||||
|
if(pbMpkTable != NULL)
|
||||||
|
{
|
||||||
|
// Load and the MPK hash table
|
||||||
|
ByteOffset = ha->MpqPos + dwByteOffset;
|
||||||
|
if(FileStream_Read(ha->pStream, &ByteOffset, pbMpkTable, cbTableSize))
|
||||||
|
{
|
||||||
|
// Decrypt the table
|
||||||
|
DecryptMpkTable(pbMpkTable, cbTableSize);
|
||||||
|
return pbMpkTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the MPK table
|
||||||
|
STORM_FREE(pbMpkTable);
|
||||||
|
pbMpkTable = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the table
|
||||||
|
return pbMpkTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
TMPQHash * LoadMpkHashTable(TMPQArchive * ha)
|
||||||
|
{
|
||||||
|
TMPQHeader * pHeader = ha->pHeader;
|
||||||
|
TMPQHash * pHashTable = NULL;
|
||||||
|
TMPKHash * pMpkHash;
|
||||||
|
TMPQHash * pHash = NULL;
|
||||||
|
DWORD dwHashTableSize = pHeader->dwHashTableSize;
|
||||||
|
|
||||||
|
// MPKs use different hash table searching.
|
||||||
|
// Instead of using MPQ_HASH_TABLE_INDEX hash as index,
|
||||||
|
// they store the value directly in the hash table.
|
||||||
|
// Also for faster searching, the hash table is sorted ascending by the value
|
||||||
|
|
||||||
|
// Load and decrypt the MPK hash table.
|
||||||
|
pMpkHash = (TMPKHash *)LoadMpkTable(ha, pHeader->dwHashTablePos, pHeader->dwHashTableSize * sizeof(TMPKHash));
|
||||||
|
if(pMpkHash != NULL)
|
||||||
|
{
|
||||||
|
// Calculate the hash table size as if it was real MPQ hash table
|
||||||
|
pHeader->dwHashTableSize = GetHashTableSizeForFileCount(dwHashTableSize);
|
||||||
|
pHeader->HashTableSize64 = pHeader->dwHashTableSize * sizeof(TMPQHash);
|
||||||
|
|
||||||
|
// Now allocate table that will serve like a true MPQ hash table,
|
||||||
|
// so we translate the MPK hash table to MPQ hash table
|
||||||
|
pHashTable = STORM_ALLOC(TMPQHash, pHeader->dwHashTableSize);
|
||||||
|
if(pHashTable != NULL)
|
||||||
|
{
|
||||||
|
// Set the entire hash table to free
|
||||||
|
memset(pHashTable, 0xFF, (size_t)pHeader->HashTableSize64);
|
||||||
|
|
||||||
|
// Copy the MPK hash table into MPQ hash table
|
||||||
|
for(DWORD i = 0; i < dwHashTableSize; i++)
|
||||||
|
{
|
||||||
|
// Finds the free hash entry in the hash table
|
||||||
|
// We don't expect any errors here, because we are putting files to empty hash table
|
||||||
|
pHash = FindFreeHashEntry(pHashTable, pHeader->dwHashTableSize, pMpkHash[i].dwName1);
|
||||||
|
assert(pHash->dwBlockIndex == HASH_ENTRY_FREE);
|
||||||
|
|
||||||
|
// Copy the MPK hash entry to the hash table
|
||||||
|
pHash->dwBlockIndex = pMpkHash[i].dwBlockIndex;
|
||||||
|
pHash->wPlatform = 0;
|
||||||
|
pHash->lcLocale = 0;
|
||||||
|
pHash->dwName1 = pMpkHash[i].dwName2;
|
||||||
|
pHash->dwName2 = pMpkHash[i].dwName3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the temporary hash table
|
||||||
|
STORM_FREE(pMpkHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pHashTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD ConvertMpkFlagsToMpqFlags(DWORD dwMpkFlags)
|
||||||
|
{
|
||||||
|
DWORD dwMpqFlags = MPQ_FILE_EXISTS;
|
||||||
|
|
||||||
|
// Check for flags that are always present
|
||||||
|
assert((dwMpkFlags & MPK_FILE_UNKNOWN_0001) != 0);
|
||||||
|
assert((dwMpkFlags & MPK_FILE_UNKNOWN_0010) != 0);
|
||||||
|
assert((dwMpkFlags & MPK_FILE_UNKNOWN_2000) != 0);
|
||||||
|
assert((dwMpkFlags & MPK_FILE_EXISTS) != 0);
|
||||||
|
|
||||||
|
// Append the compressed flag
|
||||||
|
dwMpqFlags |= (dwMpkFlags & MPK_FILE_COMPRESSED) ? MPQ_FILE_COMPRESS : 0;
|
||||||
|
|
||||||
|
// All files in the MPQ seem to be single unit files
|
||||||
|
dwMpqFlags |= MPQ_FILE_ENCRYPTED | MPQ_FILE_SINGLE_UNIT;
|
||||||
|
|
||||||
|
return dwMpqFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
TMPQBlock * LoadMpkBlockTable(TMPQArchive * ha)
|
||||||
|
{
|
||||||
|
TMPQHeader * pHeader = ha->pHeader;
|
||||||
|
TMPKBlock * pMpkBlockTable;
|
||||||
|
TMPKBlock * pMpkBlockEnd;
|
||||||
|
TMPQBlock * pBlockTable = NULL;
|
||||||
|
TMPKBlock * pMpkBlock;
|
||||||
|
TMPQBlock * pMpqBlock;
|
||||||
|
|
||||||
|
// Load and decrypt the MPK block table
|
||||||
|
pMpkBlockTable = pMpkBlock = (TMPKBlock *)LoadMpkTable(ha, pHeader->dwBlockTablePos, pHeader->dwBlockTableSize * sizeof(TMPKBlock));
|
||||||
|
if(pMpkBlockTable != NULL)
|
||||||
|
{
|
||||||
|
// Allocate buffer for MPQ-like block table
|
||||||
|
pBlockTable = pMpqBlock = STORM_ALLOC(TMPQBlock, pHeader->dwBlockTableSize);
|
||||||
|
if(pBlockTable != NULL)
|
||||||
|
{
|
||||||
|
// Convert the MPK block table to MPQ block table
|
||||||
|
pMpkBlockEnd = pMpkBlockTable + pHeader->dwBlockTableSize;
|
||||||
|
while(pMpkBlock < pMpkBlockEnd)
|
||||||
|
{
|
||||||
|
// Translate the MPK block table entry to MPQ block table entry
|
||||||
|
pMpqBlock->dwFilePos = pMpkBlock->dwFilePos;
|
||||||
|
pMpqBlock->dwCSize = pMpkBlock->dwCSize;
|
||||||
|
pMpqBlock->dwFSize = pMpkBlock->dwFSize;
|
||||||
|
pMpqBlock->dwFlags = ConvertMpkFlagsToMpqFlags(pMpkBlock->dwFlags);
|
||||||
|
|
||||||
|
// Move both
|
||||||
|
pMpkBlock++;
|
||||||
|
pMpqBlock++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the MPK block table
|
||||||
|
STORM_FREE(pMpkBlockTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pBlockTable;
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
/* SCompression.cpp Copyright (c) Ladislav Zezula 2003 */
|
/* SCompression.cpp Copyright (c) Ladislav Zezula 2003 */
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/* This module serves as a bridge between StormLib code and (de)compression */
|
/* This module serves as a bridge between StormLib code and (de)compression */
|
||||||
/* functions. All (de)compression calls go (and should only go) through this */
|
/* functions. All (de)compression calls go (and should only go) through this */
|
||||||
/* module. No system headers should be included in this module to prevent */
|
/* module. No system headers should be included in this module to prevent */
|
||||||
/* compile-time problems. */
|
/* compile-time problems. */
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
@ -22,35 +22,34 @@
|
||||||
// Information about the input and output buffers for pklib
|
// Information about the input and output buffers for pklib
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char * pbInBuff; // Pointer to input data buffer
|
unsigned char * pbInBuff; // Pointer to input data buffer
|
||||||
char * pbInBuffEnd; // End of the input buffer
|
unsigned char * pbInBuffEnd; // End of the input buffer
|
||||||
char * pbOutBuff; // Pointer to output data buffer
|
unsigned char * pbOutBuff; // Pointer to output data buffer
|
||||||
char * pbOutBuffEnd; // Pointer to output data buffer
|
unsigned char * pbOutBuffEnd; // Pointer to output data buffer
|
||||||
} TDataInfo;
|
} TDataInfo;
|
||||||
|
|
||||||
// Prototype of the compression function
|
// Prototype of the compression function
|
||||||
// Function doesn't return an error. A success means that the size of compressed buffer
|
// Function doesn't return an error. A success means that the size of compressed buffer
|
||||||
// is lower than size of uncompressed buffer.
|
// is lower than size of uncompressed buffer.
|
||||||
typedef void (*COMPRESS)(
|
typedef void (*COMPRESS)(
|
||||||
char * pbOutBuffer, // [out] Pointer to the buffer where the compressed data will be stored
|
void * pvOutBuffer, // [out] Pointer to the buffer where the compressed data will be stored
|
||||||
int * pcbOutBuffer, // [in] Pointer to length of the buffer pointed by pbOutBuffer
|
int * pcbOutBuffer, // [in] Pointer to length of the buffer pointed by pvOutBuffer
|
||||||
// [out] Contains length of the compressed data
|
void * pvInBuffer, // [in] Pointer to the buffer with data to compress
|
||||||
char * pbInBuffer, // [in] Pointer to the buffer with data to compress
|
int cbInBuffer, // [in] Length of the buffer pointer by pvInBuffer
|
||||||
int cbInBuffer, // [in] Length of the buffer pointer by pbInBuffer
|
int * pCmpType, // [in] Compression-method specific value. ADPCM Setups this for the following Huffman compression
|
||||||
int * pCmpType, // [in] Compression-method specific value. ADPCM Setups this for the following Huffman compression
|
int nCmpLevel); // [in] Compression specific value. ADPCM uses this. Should be set to zero.
|
||||||
int nCmpLevel); // [in] Compression specific value. ADPCM uses this. Should be set to zero.
|
|
||||||
|
|
||||||
// Prototype of the decompression function
|
// Prototype of the decompression function
|
||||||
// Returns 1 if success, 0 if failure
|
// Returns 1 if success, 0 if failure
|
||||||
typedef int (*DECOMPRESS)(
|
typedef int (*DECOMPRESS)(
|
||||||
char * pbOutBuffer, // [out] Pointer to the buffer where to store decompressed data
|
void * pvOutBuffer, // [out] Pointer to the buffer where to store decompressed data
|
||||||
int * pcbOutBuffer, // [in] Pointer to total size of the buffer pointed by pbOutBuffer
|
int * pcbOutBuffer, // [in] Pointer to total size of the buffer pointed by pvOutBuffer
|
||||||
// [out] Contains length of the decompressed data
|
// [out] Contains length of the decompressed data
|
||||||
char * pbInBuffer, // [in] Pointer to data to be decompressed
|
void * pvInBuffer, // [in] Pointer to data to be decompressed
|
||||||
int cbInBuffer); // [in] Length of the data to be decompressed
|
int cbInBuffer); // [in] Length of the data to be decompressed
|
||||||
|
|
||||||
// Table of compression functions
|
// Table of compression functions
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
unsigned long uMask; // Compression mask
|
unsigned long uMask; // Compression mask
|
||||||
COMPRESS Compress; // Compression function
|
COMPRESS Compress; // Compression function
|
||||||
|
|
@ -70,59 +69,22 @@ typedef struct
|
||||||
/* */
|
/* */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
// 1500F4C0
|
void Compress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||||
void Compress_huff(
|
|
||||||
char * pbOutBuffer,
|
|
||||||
int * pcbOutBuffer,
|
|
||||||
char * pbInBuffer,
|
|
||||||
int cbInBuffer,
|
|
||||||
int * pCmpType,
|
|
||||||
int /* nCmpLevel */)
|
|
||||||
{
|
{
|
||||||
THuffmannTree ht; // Huffmann tree for compression
|
THuffmannTree ht(true);
|
||||||
TOutputStream os; // Output stream
|
TOutputStream os(pvOutBuffer, *pcbOutBuffer);
|
||||||
|
|
||||||
// Initialize output stream
|
STORMLIB_UNUSED(nCmpLevel);
|
||||||
os.pbOutBuffer = (unsigned char *)pbOutBuffer;
|
*pcbOutBuffer = ht.Compress(&os, pvInBuffer, cbInBuffer, *pCmpType);
|
||||||
os.cbOutSize = *pcbOutBuffer;
|
}
|
||||||
os.pbOutPos = (unsigned char *)pbOutBuffer;
|
|
||||||
os.dwBitBuff = 0;
|
|
||||||
os.nBits = 0;
|
|
||||||
|
|
||||||
// Initialize the Huffmann tree for compression
|
int Decompress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
ht.InitTree(true);
|
|
||||||
|
|
||||||
*pcbOutBuffer = ht.DoCompression(&os, (unsigned char *)pbInBuffer, cbInBuffer, *pCmpType);
|
|
||||||
|
|
||||||
// The following code is not necessary to run, because it has no
|
|
||||||
// effect on the output data. It only clears the huffmann tree, but when
|
|
||||||
// the tree is on the stack, who cares ?
|
|
||||||
// ht.UninitTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1500F5F0
|
|
||||||
int Decompress_huff(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer)
|
|
||||||
{
|
{
|
||||||
THuffmannTree ht;
|
THuffmannTree ht(false);
|
||||||
TInputStream is;
|
TInputStream is(pvInBuffer, cbInBuffer);
|
||||||
|
|
||||||
// Initialize input stream
|
*pcbOutBuffer = ht.Decompress(pvOutBuffer, *pcbOutBuffer, &is);
|
||||||
is.pbInBufferEnd = (unsigned char *)pbInBuffer + cbInBuffer;
|
return (*pcbOutBuffer == 0) ? 0 : 1;
|
||||||
is.pbInBuffer = (unsigned char *)pbInBuffer;
|
|
||||||
is.BitBuffer = 0;
|
|
||||||
is.BitCount = 0;
|
|
||||||
|
|
||||||
// Initialize the Huffmann tree for compression
|
|
||||||
ht.InitTree(false);
|
|
||||||
*pcbOutBuffer = ht.DoDecompression((unsigned char *)pbOutBuffer, *pcbOutBuffer, &is);
|
|
||||||
if(*pcbOutBuffer == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// The following code is not necessary to run, because it has no
|
|
||||||
// effect on the output data. It only clears the huffmann tree, but when
|
|
||||||
// the tree is on the stack, who cares ?
|
|
||||||
// ht.UninitTree();
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
@ -131,23 +93,21 @@ int Decompress_huff(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, i
|
||||||
/* */
|
/* */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
void Compress_ZLIB(
|
void Compress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||||
char * pbOutBuffer,
|
|
||||||
int * pcbOutBuffer,
|
|
||||||
char * pbInBuffer,
|
|
||||||
int cbInBuffer,
|
|
||||||
int * /* pCmpType */,
|
|
||||||
int /* nCmpLevel */)
|
|
||||||
{
|
{
|
||||||
z_stream z; // Stream information for zlib
|
z_stream z; // Stream information for zlib
|
||||||
int windowBits;
|
int windowBits;
|
||||||
int nResult;
|
int nResult;
|
||||||
|
|
||||||
|
// Keep compilers happy
|
||||||
|
STORMLIB_UNUSED(pCmpType);
|
||||||
|
STORMLIB_UNUSED(nCmpLevel);
|
||||||
|
|
||||||
// Fill the stream structure for zlib
|
// Fill the stream structure for zlib
|
||||||
z.next_in = (Bytef *)pbInBuffer;
|
z.next_in = (Bytef *)pvInBuffer;
|
||||||
z.avail_in = (uInt)cbInBuffer;
|
z.avail_in = (uInt)cbInBuffer;
|
||||||
z.total_in = cbInBuffer;
|
z.total_in = cbInBuffer;
|
||||||
z.next_out = (Bytef *)pbOutBuffer;
|
z.next_out = (Bytef *)pvOutBuffer;
|
||||||
z.avail_out = *pcbOutBuffer;
|
z.avail_out = *pcbOutBuffer;
|
||||||
z.total_out = 0;
|
z.total_out = 0;
|
||||||
z.zalloc = NULL;
|
z.zalloc = NULL;
|
||||||
|
|
@ -175,16 +135,16 @@ void Compress_ZLIB(
|
||||||
// Storm.dll uses zlib version 1.1.3
|
// Storm.dll uses zlib version 1.1.3
|
||||||
// Wow.exe uses zlib version 1.2.3
|
// Wow.exe uses zlib version 1.2.3
|
||||||
nResult = deflateInit2(&z,
|
nResult = deflateInit2(&z,
|
||||||
6, // Compression level used by WoW MPQs
|
6, // Compression level used by WoW MPQs
|
||||||
Z_DEFLATED,
|
Z_DEFLATED,
|
||||||
windowBits,
|
windowBits,
|
||||||
8,
|
8,
|
||||||
Z_DEFAULT_STRATEGY);
|
Z_DEFAULT_STRATEGY);
|
||||||
if(nResult == Z_OK)
|
if(nResult == Z_OK)
|
||||||
{
|
{
|
||||||
// Call zlib to compress the data
|
// Call zlib to compress the data
|
||||||
nResult = deflate(&z, Z_FINISH);
|
nResult = deflate(&z, Z_FINISH);
|
||||||
|
|
||||||
if(nResult == Z_OK || nResult == Z_STREAM_END)
|
if(nResult == Z_OK || nResult == Z_STREAM_END)
|
||||||
*pcbOutBuffer = z.total_out;
|
*pcbOutBuffer = z.total_out;
|
||||||
|
|
||||||
|
|
@ -192,16 +152,16 @@ void Compress_ZLIB(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Decompress_ZLIB(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer)
|
int Decompress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
{
|
{
|
||||||
z_stream z; // Stream information for zlib
|
z_stream z; // Stream information for zlib
|
||||||
int nResult;
|
int nResult;
|
||||||
|
|
||||||
// Fill the stream structure for zlib
|
// Fill the stream structure for zlib
|
||||||
z.next_in = (Bytef *)pbInBuffer;
|
z.next_in = (Bytef *)pvInBuffer;
|
||||||
z.avail_in = (uInt)cbInBuffer;
|
z.avail_in = (uInt)cbInBuffer;
|
||||||
z.total_in = cbInBuffer;
|
z.total_in = cbInBuffer;
|
||||||
z.next_out = (Bytef *)pbOutBuffer;
|
z.next_out = (Bytef *)pvOutBuffer;
|
||||||
z.avail_out = *pcbOutBuffer;
|
z.avail_out = *pcbOutBuffer;
|
||||||
z.total_out = 0;
|
z.total_out = 0;
|
||||||
z.zalloc = NULL;
|
z.zalloc = NULL;
|
||||||
|
|
@ -227,7 +187,7 @@ int Decompress_ZLIB(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, i
|
||||||
// Function loads data from the input buffer. Used by Pklib's "implode"
|
// Function loads data from the input buffer. Used by Pklib's "implode"
|
||||||
// and "explode" function as user-defined callback
|
// and "explode" function as user-defined callback
|
||||||
// Returns number of bytes loaded
|
// Returns number of bytes loaded
|
||||||
//
|
//
|
||||||
// char * buf - Pointer to a buffer where to store loaded data
|
// char * buf - Pointer to a buffer where to store loaded data
|
||||||
// unsigned int * size - Max. number of bytes to read
|
// unsigned int * size - Max. number of bytes to read
|
||||||
// void * param - Custom pointer, parameter of implode/explode
|
// void * param - Custom pointer, parameter of implode/explode
|
||||||
|
|
@ -241,7 +201,7 @@ static unsigned int ReadInputData(char * buf, unsigned int * size, void * param)
|
||||||
// Check the case when not enough data available
|
// Check the case when not enough data available
|
||||||
if(nToRead > nMaxAvail)
|
if(nToRead > nMaxAvail)
|
||||||
nToRead = nMaxAvail;
|
nToRead = nMaxAvail;
|
||||||
|
|
||||||
// Load data and increment offsets
|
// Load data and increment offsets
|
||||||
memcpy(buf, pInfo->pbInBuff, nToRead);
|
memcpy(buf, pInfo->pbInBuff, nToRead);
|
||||||
pInfo->pbInBuff += nToRead;
|
pInfo->pbInBuff += nToRead;
|
||||||
|
|
@ -251,7 +211,7 @@ static unsigned int ReadInputData(char * buf, unsigned int * size, void * param)
|
||||||
|
|
||||||
// Function for store output data. Used by Pklib's "implode" and "explode"
|
// Function for store output data. Used by Pklib's "implode" and "explode"
|
||||||
// as user-defined callback
|
// as user-defined callback
|
||||||
//
|
//
|
||||||
// char * buf - Pointer to data to be written
|
// char * buf - Pointer to data to be written
|
||||||
// unsigned int * size - Number of bytes to write
|
// unsigned int * size - Number of bytes to write
|
||||||
// void * param - Custom pointer, parameter of implode/explode
|
// void * param - Custom pointer, parameter of implode/explode
|
||||||
|
|
@ -272,68 +232,74 @@ static void WriteOutputData(char * buf, unsigned int * size, void * param)
|
||||||
assert(pInfo->pbOutBuff <= pInfo->pbOutBuffEnd);
|
assert(pInfo->pbOutBuff <= pInfo->pbOutBuffEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Compress_PKLIB(
|
static void Compress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||||
char * pbOutBuffer,
|
|
||||||
int * pcbOutBuffer,
|
|
||||||
char * pbInBuffer,
|
|
||||||
int cbInBuffer,
|
|
||||||
int * /* pCmpType */,
|
|
||||||
int /* nCmpLevel */)
|
|
||||||
{
|
{
|
||||||
TDataInfo Info; // Data information
|
TDataInfo Info; // Data information
|
||||||
char * work_buf = STORM_ALLOC(char, CMP_BUFFER_SIZE);// Pklib's work buffer
|
char * work_buf = STORM_ALLOC(char, CMP_BUFFER_SIZE);// Pklib's work buffer
|
||||||
unsigned int dict_size; // Dictionary size
|
unsigned int dict_size; // Dictionary size
|
||||||
unsigned int ctype = CMP_BINARY; // Compression type
|
unsigned int ctype = CMP_BINARY; // Compression type
|
||||||
|
|
||||||
// Fill data information structure
|
// Keep compilers happy
|
||||||
memset(work_buf, 0, CMP_BUFFER_SIZE);
|
STORMLIB_UNUSED(pCmpType);
|
||||||
Info.pbInBuff = pbInBuffer;
|
STORMLIB_UNUSED(nCmpLevel);
|
||||||
Info.pbInBuffEnd = pbInBuffer + cbInBuffer;
|
|
||||||
Info.pbOutBuff = pbOutBuffer;
|
|
||||||
Info.pbOutBuffEnd = pbOutBuffer + *pcbOutBuffer;
|
|
||||||
|
|
||||||
//
|
// Handle no-memory condition
|
||||||
// Set the dictionary size
|
if(work_buf != NULL)
|
||||||
//
|
{
|
||||||
// Diablo I ues fixed dictionary size of CMP_IMPLODE_DICT_SIZE3
|
// Fill data information structure
|
||||||
// Starcraft uses the variable dictionary size based on algorithm below
|
memset(work_buf, 0, CMP_BUFFER_SIZE);
|
||||||
//
|
Info.pbInBuff = (unsigned char *)pvInBuffer;
|
||||||
|
Info.pbInBuffEnd = (unsigned char *)pvInBuffer + cbInBuffer;
|
||||||
|
Info.pbOutBuff = (unsigned char *)pvOutBuffer;
|
||||||
|
Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer;
|
||||||
|
|
||||||
if (cbInBuffer < 0x600)
|
//
|
||||||
dict_size = CMP_IMPLODE_DICT_SIZE1;
|
// Set the dictionary size
|
||||||
else if(0x600 <= cbInBuffer && cbInBuffer < 0xC00)
|
//
|
||||||
dict_size = CMP_IMPLODE_DICT_SIZE2;
|
// Diablo I uses fixed dictionary size of CMP_IMPLODE_DICT_SIZE3
|
||||||
else
|
// Starcraft I uses the variable dictionary size based on algorithm below
|
||||||
dict_size = CMP_IMPLODE_DICT_SIZE3;
|
//
|
||||||
|
|
||||||
// Do the compression
|
if (cbInBuffer < 0x600)
|
||||||
if(implode(ReadInputData, WriteOutputData, work_buf, &Info, &ctype, &dict_size) == CMP_NO_ERROR)
|
dict_size = CMP_IMPLODE_DICT_SIZE1;
|
||||||
*pcbOutBuffer = (int)(Info.pbOutBuff - pbOutBuffer);
|
else if(0x600 <= cbInBuffer && cbInBuffer < 0xC00)
|
||||||
|
dict_size = CMP_IMPLODE_DICT_SIZE2;
|
||||||
|
else
|
||||||
|
dict_size = CMP_IMPLODE_DICT_SIZE3;
|
||||||
|
|
||||||
STORM_FREE(work_buf);
|
// Do the compression
|
||||||
|
if(implode(ReadInputData, WriteOutputData, work_buf, &Info, &ctype, &dict_size) == CMP_NO_ERROR)
|
||||||
|
*pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer);
|
||||||
|
|
||||||
|
STORM_FREE(work_buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Decompress_PKLIB(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer)
|
static int Decompress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
{
|
{
|
||||||
TDataInfo Info; // Data information
|
TDataInfo Info; // Data information
|
||||||
char * work_buf = STORM_ALLOC(char, EXP_BUFFER_SIZE);// Pklib's work buffer
|
char * work_buf = STORM_ALLOC(char, EXP_BUFFER_SIZE);// Pklib's work buffer
|
||||||
|
|
||||||
|
// Handle no-memory condition
|
||||||
|
if(work_buf == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
// Fill data information structure
|
// Fill data information structure
|
||||||
memset(work_buf, 0, EXP_BUFFER_SIZE);
|
memset(work_buf, 0, EXP_BUFFER_SIZE);
|
||||||
Info.pbInBuff = pbInBuffer;
|
Info.pbInBuff = (unsigned char *)pvInBuffer;
|
||||||
Info.pbInBuffEnd = pbInBuffer + cbInBuffer;
|
Info.pbInBuffEnd = (unsigned char *)pvInBuffer + cbInBuffer;
|
||||||
Info.pbOutBuff = pbOutBuffer;
|
Info.pbOutBuff = (unsigned char *)pvOutBuffer;
|
||||||
Info.pbOutBuffEnd = pbOutBuffer + *pcbOutBuffer;
|
Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer;
|
||||||
|
|
||||||
// Do the decompression
|
// Do the decompression
|
||||||
explode(ReadInputData, WriteOutputData, work_buf, &Info);
|
explode(ReadInputData, WriteOutputData, work_buf, &Info);
|
||||||
|
|
||||||
// If PKLIB is unable to decompress the data, return 0;
|
// If PKLIB is unable to decompress the data, return 0;
|
||||||
if(Info.pbOutBuff == pbOutBuffer)
|
if(Info.pbOutBuff == pvOutBuffer)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Give away the number of decompressed bytes
|
// Give away the number of decompressed bytes
|
||||||
*pcbOutBuffer = (int)(Info.pbOutBuff - pbOutBuffer);
|
*pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer);
|
||||||
STORM_FREE(work_buf);
|
STORM_FREE(work_buf);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -344,19 +310,17 @@ static int Decompress_PKLIB(char * pbOutBuffer, int * pcbOutBuffer, char * pbInB
|
||||||
/* */
|
/* */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
static void Compress_BZIP2(
|
static void Compress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||||
char * pbOutBuffer,
|
|
||||||
int * pcbOutBuffer,
|
|
||||||
char * pbInBuffer,
|
|
||||||
int cbInBuffer,
|
|
||||||
int * /* pCmpType */,
|
|
||||||
int /* nCmpLevel */)
|
|
||||||
{
|
{
|
||||||
bz_stream strm;
|
bz_stream strm;
|
||||||
int blockSize100k = 9;
|
int blockSize100k = 9;
|
||||||
int workFactor = 30;
|
int workFactor = 30;
|
||||||
int bzError;
|
int bzError;
|
||||||
|
|
||||||
|
// Keep compilers happy
|
||||||
|
STORMLIB_UNUSED(pCmpType);
|
||||||
|
STORMLIB_UNUSED(nCmpLevel);
|
||||||
|
|
||||||
// Initialize the BZIP2 compression
|
// Initialize the BZIP2 compression
|
||||||
strm.bzalloc = NULL;
|
strm.bzalloc = NULL;
|
||||||
strm.bzfree = NULL;
|
strm.bzfree = NULL;
|
||||||
|
|
@ -365,9 +329,9 @@ static void Compress_BZIP2(
|
||||||
// Last checked on Starcraft II
|
// Last checked on Starcraft II
|
||||||
if(BZ2_bzCompressInit(&strm, blockSize100k, 0, workFactor) == BZ_OK)
|
if(BZ2_bzCompressInit(&strm, blockSize100k, 0, workFactor) == BZ_OK)
|
||||||
{
|
{
|
||||||
strm.next_in = pbInBuffer;
|
strm.next_in = (char *)pvInBuffer;
|
||||||
strm.avail_in = cbInBuffer;
|
strm.avail_in = cbInBuffer;
|
||||||
strm.next_out = pbOutBuffer;
|
strm.next_out = (char *)pvOutBuffer;
|
||||||
strm.avail_out = *pcbOutBuffer;
|
strm.avail_out = *pcbOutBuffer;
|
||||||
|
|
||||||
// Perform the compression
|
// Perform the compression
|
||||||
|
|
@ -386,7 +350,7 @@ static void Compress_BZIP2(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Decompress_BZIP2(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer)
|
static int Decompress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
{
|
{
|
||||||
bz_stream strm;
|
bz_stream strm;
|
||||||
int nResult = BZ_OK;
|
int nResult = BZ_OK;
|
||||||
|
|
@ -396,17 +360,17 @@ static int Decompress_BZIP2(char * pbOutBuffer, int * pcbOutBuffer, char * pbInB
|
||||||
strm.bzfree = NULL;
|
strm.bzfree = NULL;
|
||||||
if(BZ2_bzDecompressInit(&strm, 0, 0) == BZ_OK)
|
if(BZ2_bzDecompressInit(&strm, 0, 0) == BZ_OK)
|
||||||
{
|
{
|
||||||
strm.next_in = pbInBuffer;
|
strm.next_in = (char *)pvInBuffer;
|
||||||
strm.avail_in = cbInBuffer;
|
strm.avail_in = cbInBuffer;
|
||||||
strm.next_out = pbOutBuffer;
|
strm.next_out = (char *)pvOutBuffer;
|
||||||
strm.avail_out = *pcbOutBuffer;
|
strm.avail_out = *pcbOutBuffer;
|
||||||
|
|
||||||
// Perform the decompression
|
// Perform the decompression
|
||||||
while(nResult != BZ_STREAM_END)
|
while(nResult != BZ_STREAM_END)
|
||||||
{
|
{
|
||||||
nResult = BZ2_bzDecompress(&strm);
|
nResult = BZ2_bzDecompress(&strm);
|
||||||
|
|
||||||
// If any error there, break the loop
|
// If any error there, break the loop
|
||||||
if(nResult < BZ_OK)
|
if(nResult < BZ_OK)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -461,17 +425,12 @@ static void LZMA_Callback_Free(void *p, void *address)
|
||||||
// the data compressed by StormLib.
|
// the data compressed by StormLib.
|
||||||
//
|
//
|
||||||
|
|
||||||
/*static */ void Compress_LZMA(
|
static void Compress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||||
char * pbOutBuffer,
|
|
||||||
int * pcbOutBuffer,
|
|
||||||
char * pbInBuffer,
|
|
||||||
int cbInBuffer,
|
|
||||||
int * /* pCmpType */,
|
|
||||||
int /* nCmpLevel */)
|
|
||||||
{
|
{
|
||||||
ICompressProgress Progress;
|
ICompressProgress Progress;
|
||||||
CLzmaEncProps props;
|
CLzmaEncProps props;
|
||||||
ISzAlloc SzAlloc;
|
ISzAlloc SzAlloc;
|
||||||
|
Byte * pbOutBuffer = (Byte *)pvOutBuffer;
|
||||||
Byte * destBuffer;
|
Byte * destBuffer;
|
||||||
SizeT destLen = *pcbOutBuffer;
|
SizeT destLen = *pcbOutBuffer;
|
||||||
SizeT srcLen = cbInBuffer;
|
SizeT srcLen = cbInBuffer;
|
||||||
|
|
@ -479,6 +438,10 @@ static void LZMA_Callback_Free(void *p, void *address)
|
||||||
size_t encodedPropsSize = LZMA_PROPS_SIZE;
|
size_t encodedPropsSize = LZMA_PROPS_SIZE;
|
||||||
SRes nResult;
|
SRes nResult;
|
||||||
|
|
||||||
|
// Keep compilers happy
|
||||||
|
STORMLIB_UNUSED(pCmpType);
|
||||||
|
STORMLIB_UNUSED(nCmpLevel);
|
||||||
|
|
||||||
// Fill the callbacks in structures
|
// Fill the callbacks in structures
|
||||||
Progress.Progress = LZMA_Callback_Progress;
|
Progress.Progress = LZMA_Callback_Progress;
|
||||||
SzAlloc.Alloc = LZMA_Callback_Alloc;
|
SzAlloc.Alloc = LZMA_Callback_Alloc;
|
||||||
|
|
@ -488,19 +451,19 @@ static void LZMA_Callback_Free(void *p, void *address)
|
||||||
LzmaEncProps_Init(&props);
|
LzmaEncProps_Init(&props);
|
||||||
|
|
||||||
// Perform compression
|
// Perform compression
|
||||||
destBuffer = (Byte *)pbOutBuffer + LZMA_HEADER_SIZE;
|
destBuffer = (Byte *)pvOutBuffer + LZMA_HEADER_SIZE;
|
||||||
destLen = *pcbOutBuffer - LZMA_HEADER_SIZE;
|
destLen = *pcbOutBuffer - LZMA_HEADER_SIZE;
|
||||||
nResult = LzmaEncode(destBuffer,
|
nResult = LzmaEncode(destBuffer,
|
||||||
&destLen,
|
&destLen,
|
||||||
(Byte *)pbInBuffer,
|
(Byte *)pvInBuffer,
|
||||||
srcLen,
|
srcLen,
|
||||||
&props,
|
&props,
|
||||||
encodedProps,
|
encodedProps,
|
||||||
&encodedPropsSize,
|
&encodedPropsSize,
|
||||||
0,
|
0,
|
||||||
&Progress,
|
&Progress,
|
||||||
&SzAlloc,
|
&SzAlloc,
|
||||||
&SzAlloc);
|
&SzAlloc);
|
||||||
if(nResult != SZ_OK)
|
if(nResult != SZ_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -512,7 +475,7 @@ static void LZMA_Callback_Free(void *p, void *address)
|
||||||
*pbOutBuffer++ = 0;
|
*pbOutBuffer++ = 0;
|
||||||
|
|
||||||
// Copy the encoded properties to the output buffer
|
// Copy the encoded properties to the output buffer
|
||||||
memcpy(pbOutBuffer, encodedProps, encodedPropsSize);
|
memcpy(pvOutBuffer, encodedProps, encodedPropsSize);
|
||||||
pbOutBuffer += encodedPropsSize;
|
pbOutBuffer += encodedPropsSize;
|
||||||
|
|
||||||
// Copy the size of the data
|
// Copy the size of the data
|
||||||
|
|
@ -529,18 +492,18 @@ static void LZMA_Callback_Free(void *p, void *address)
|
||||||
*pcbOutBuffer = (unsigned int)(destLen + LZMA_HEADER_SIZE);
|
*pcbOutBuffer = (unsigned int)(destLen + LZMA_HEADER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Decompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer)
|
static int Decompress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
{
|
{
|
||||||
ELzmaStatus LzmaStatus;
|
ELzmaStatus LzmaStatus;
|
||||||
ISzAlloc SzAlloc;
|
ISzAlloc SzAlloc;
|
||||||
Byte * destBuffer = (Byte *)pbOutBuffer;
|
Byte * destBuffer = (Byte *)pvOutBuffer;
|
||||||
Byte * srcBuffer = (Byte *)pbInBuffer;
|
Byte * srcBuffer = (Byte *)pvInBuffer;
|
||||||
SizeT destLen = *pcbOutBuffer;
|
SizeT destLen = *pcbOutBuffer;
|
||||||
SizeT srcLen = cbInBuffer;
|
SizeT srcLen = cbInBuffer;
|
||||||
SRes nResult;
|
SRes nResult;
|
||||||
|
|
||||||
// There must be at least 0x0E bytes in the buffer
|
// There must be at least 0x0E bytes in the buffer
|
||||||
if(srcLen <= LZMA_HEADER_SIZE)
|
if(srcLen <= LZMA_HEADER_SIZE)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// We only accept blocks that have no filter used
|
// We only accept blocks that have no filter used
|
||||||
|
|
@ -554,14 +517,55 @@ static int Decompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBu
|
||||||
// Perform compression
|
// Perform compression
|
||||||
srcLen = cbInBuffer - LZMA_HEADER_SIZE;
|
srcLen = cbInBuffer - LZMA_HEADER_SIZE;
|
||||||
nResult = LzmaDecode(destBuffer,
|
nResult = LzmaDecode(destBuffer,
|
||||||
&destLen,
|
&destLen,
|
||||||
srcBuffer + LZMA_HEADER_SIZE,
|
srcBuffer + LZMA_HEADER_SIZE,
|
||||||
&srcLen,
|
&srcLen,
|
||||||
srcBuffer + 1,
|
srcBuffer + 1,
|
||||||
LZMA_PROPS_SIZE,
|
LZMA_PROPS_SIZE,
|
||||||
LZMA_FINISH_END,
|
LZMA_FINISH_END,
|
||||||
&LzmaStatus,
|
&LzmaStatus,
|
||||||
&SzAlloc);
|
&SzAlloc);
|
||||||
|
if(nResult != SZ_OK)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*pcbOutBuffer = (unsigned int)destLen;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Decompress_LZMA_MPK(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
|
{
|
||||||
|
ELzmaStatus LzmaStatus;
|
||||||
|
ISzAlloc SzAlloc;
|
||||||
|
Byte * destBuffer = (Byte *)pvOutBuffer;
|
||||||
|
Byte * srcBuffer = (Byte *)pvInBuffer;
|
||||||
|
SizeT destLen = *pcbOutBuffer;
|
||||||
|
SizeT srcLen = cbInBuffer;
|
||||||
|
SRes nResult;
|
||||||
|
BYTE LZMA_Props[] = {0x5D, 0x00, 0x00, 0x00, 0x01};
|
||||||
|
|
||||||
|
// There must be at least 0x0E bytes in the buffer
|
||||||
|
if(srcLen <= sizeof(LZMA_Props))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Verify the props header
|
||||||
|
if(memcmp(pvInBuffer, LZMA_Props, sizeof(LZMA_Props)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Fill the callbacks in structures
|
||||||
|
SzAlloc.Alloc = LZMA_Callback_Alloc;
|
||||||
|
SzAlloc.Free = LZMA_Callback_Free;
|
||||||
|
|
||||||
|
// Perform compression
|
||||||
|
srcLen = cbInBuffer - sizeof(LZMA_Props);
|
||||||
|
nResult = LzmaDecode(destBuffer,
|
||||||
|
&destLen,
|
||||||
|
srcBuffer + sizeof(LZMA_Props),
|
||||||
|
&srcLen,
|
||||||
|
srcBuffer,
|
||||||
|
sizeof(LZMA_Props),
|
||||||
|
LZMA_FINISH_END,
|
||||||
|
&LzmaStatus,
|
||||||
|
&SzAlloc);
|
||||||
if(nResult != SZ_OK)
|
if(nResult != SZ_OK)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -575,20 +579,18 @@ static int Decompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBu
|
||||||
/* */
|
/* */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
void Compress_SPARSE(
|
void Compress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||||
char * pbOutBuffer,
|
|
||||||
int * pcbOutBuffer,
|
|
||||||
char * pbInBuffer,
|
|
||||||
int cbInBuffer,
|
|
||||||
int * /* pCmpType */,
|
|
||||||
int /* nCmpLevel */)
|
|
||||||
{
|
{
|
||||||
CompressSparse((unsigned char *)pbOutBuffer, pcbOutBuffer, (unsigned char *)pbInBuffer, cbInBuffer);
|
// Keep compilers happy
|
||||||
|
STORMLIB_UNUSED(pCmpType);
|
||||||
|
STORMLIB_UNUSED(nCmpLevel);
|
||||||
|
|
||||||
|
CompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Decompress_SPARSE(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer)
|
int Decompress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
{
|
{
|
||||||
return DecompressSparse((unsigned char *)pbOutBuffer, pcbOutBuffer, (unsigned char *)pbInBuffer, cbInBuffer);
|
return DecompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
@ -597,13 +599,7 @@ int Decompress_SPARSE(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer,
|
||||||
/* */
|
/* */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
static void Compress_ADPCM_mono(
|
static void Compress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||||
char * pbOutBuffer,
|
|
||||||
int * pcbOutBuffer,
|
|
||||||
char * pbInBuffer,
|
|
||||||
int cbInBuffer,
|
|
||||||
int * pCmpType,
|
|
||||||
int nCmpLevel)
|
|
||||||
{
|
{
|
||||||
// Prepare the compression level for Huffmann compression,
|
// Prepare the compression level for Huffmann compression,
|
||||||
// which will be called as next step
|
// which will be called as next step
|
||||||
|
|
@ -622,12 +618,12 @@ static void Compress_ADPCM_mono(
|
||||||
nCmpLevel = 5;
|
nCmpLevel = 5;
|
||||||
*pCmpType = 7;
|
*pCmpType = 7;
|
||||||
}
|
}
|
||||||
*pcbOutBuffer = CompressADPCM((unsigned char *)pbOutBuffer, *pcbOutBuffer, (short *)pbInBuffer, cbInBuffer, 1, nCmpLevel);
|
*pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1, nCmpLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Decompress_ADPCM_mono(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer)
|
static int Decompress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
{
|
{
|
||||||
*pcbOutBuffer = DecompressADPCM((unsigned char *)pbOutBuffer, *pcbOutBuffer, (unsigned char *)pbInBuffer, cbInBuffer, 1);
|
*pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -637,13 +633,7 @@ static int Decompress_ADPCM_mono(char * pbOutBuffer, int * pcbOutBuffer, char *
|
||||||
/* */
|
/* */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
static void Compress_ADPCM_stereo(
|
static void Compress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||||
char * pbOutBuffer,
|
|
||||||
int * pcbOutBuffer,
|
|
||||||
char * pbInBuffer,
|
|
||||||
int cbInBuffer,
|
|
||||||
int * pCmpType,
|
|
||||||
int nCmpLevel)
|
|
||||||
{
|
{
|
||||||
// Prepare the compression level for Huffmann compression,
|
// Prepare the compression level for Huffmann compression,
|
||||||
// which will be called as next step
|
// which will be called as next step
|
||||||
|
|
@ -662,12 +652,12 @@ static void Compress_ADPCM_stereo(
|
||||||
nCmpLevel = 5;
|
nCmpLevel = 5;
|
||||||
*pCmpType = 7;
|
*pCmpType = 7;
|
||||||
}
|
}
|
||||||
*pcbOutBuffer = CompressADPCM((unsigned char *)pbOutBuffer, *pcbOutBuffer, (short *)pbInBuffer, cbInBuffer, 2, nCmpLevel);
|
*pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2, nCmpLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int Decompress_ADPCM_stereo(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer)
|
static int Decompress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
{
|
{
|
||||||
*pcbOutBuffer = DecompressADPCM((unsigned char *)pbOutBuffer, *pcbOutBuffer, (unsigned char *)pbInBuffer, cbInBuffer, 2);
|
*pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -677,24 +667,25 @@ static int Decompress_ADPCM_stereo(char * pbOutBuffer, int * pcbOutBuffer, char
|
||||||
/* */
|
/* */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
int WINAPI SCompImplode(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer)
|
int WINAPI SCompImplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
{
|
{
|
||||||
int cbOutBuffer = *pcbOutBuffer;
|
int cbOutBuffer;
|
||||||
|
|
||||||
// Check for valid parameters
|
// Check for valid parameters
|
||||||
if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pbOutBuffer || !pbInBuffer)
|
if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer)
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform the compression
|
// Perform the compression
|
||||||
Compress_PKLIB(pbOutBuffer, &cbOutBuffer, pbInBuffer, cbInBuffer, NULL, 0);
|
cbOutBuffer = *pcbOutBuffer;
|
||||||
|
Compress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer, NULL, 0);
|
||||||
|
|
||||||
// If the compression was unsuccessful, copy the data as-is
|
// If the compression was unsuccessful, copy the data as-is
|
||||||
if(cbOutBuffer >= *pcbOutBuffer)
|
if(cbOutBuffer >= *pcbOutBuffer)
|
||||||
{
|
{
|
||||||
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
|
memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
|
||||||
cbOutBuffer = *pcbOutBuffer;
|
cbOutBuffer = *pcbOutBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -708,33 +699,34 @@ int WINAPI SCompImplode(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffe
|
||||||
/* */
|
/* */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
int WINAPI SCompExplode(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer)
|
int WINAPI SCompExplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
{
|
{
|
||||||
int cbOutBuffer = *pcbOutBuffer;
|
int cbOutBuffer;
|
||||||
|
|
||||||
// Check for valid parameters
|
// Check for valid parameters
|
||||||
if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pbOutBuffer || !pbInBuffer)
|
if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer)
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the input length is the same as output length, do nothing.
|
// If the input length is the same as output length, do nothing.
|
||||||
|
cbOutBuffer = *pcbOutBuffer;
|
||||||
if(cbInBuffer == cbOutBuffer)
|
if(cbInBuffer == cbOutBuffer)
|
||||||
{
|
{
|
||||||
// If the buffers are equal, don't copy anything.
|
// If the buffers are equal, don't copy anything.
|
||||||
if(pbInBuffer == pbOutBuffer)
|
if(pvInBuffer == pvOutBuffer)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
|
memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform decompression
|
// Perform decompression
|
||||||
if(!Decompress_PKLIB(pbOutBuffer, &cbOutBuffer, pbInBuffer, cbInBuffer))
|
if(!Decompress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer))
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_FILE_CORRUPT);
|
SetLastError(ERROR_FILE_CORRUPT);
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
*pcbOutBuffer = cbOutBuffer;
|
*pcbOutBuffer = cbOutBuffer;
|
||||||
|
|
@ -758,29 +750,23 @@ int WINAPI SCompExplode(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffe
|
||||||
|
|
||||||
static TCompressTable cmp_table[] =
|
static TCompressTable cmp_table[] =
|
||||||
{
|
{
|
||||||
{MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse compression
|
{MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse compression
|
||||||
{MPQ_COMPRESSION_ADPCM_MONO, Compress_ADPCM_mono}, // IMA ADPCM mono compression
|
{MPQ_COMPRESSION_ADPCM_MONO, Compress_ADPCM_mono}, // IMA ADPCM mono compression
|
||||||
{MPQ_COMPRESSION_ADPCM_STEREO, Compress_ADPCM_stereo}, // IMA ADPCM stereo compression
|
{MPQ_COMPRESSION_ADPCM_STEREO, Compress_ADPCM_stereo}, // IMA ADPCM stereo compression
|
||||||
{MPQ_COMPRESSION_HUFFMANN, Compress_huff}, // Huffmann compression
|
{MPQ_COMPRESSION_HUFFMANN, Compress_huff}, // Huffmann compression
|
||||||
{MPQ_COMPRESSION_ZLIB, Compress_ZLIB}, // Compression with the "zlib" library
|
{MPQ_COMPRESSION_ZLIB, Compress_ZLIB}, // Compression with the "zlib" library
|
||||||
{MPQ_COMPRESSION_PKWARE, Compress_PKLIB}, // Compression with Pkware DCL
|
{MPQ_COMPRESSION_PKWARE, Compress_PKLIB}, // Compression with Pkware DCL
|
||||||
{MPQ_COMPRESSION_BZIP2, Compress_BZIP2} // Compression Bzip2 library
|
{MPQ_COMPRESSION_BZIP2, Compress_BZIP2} // Compression Bzip2 library
|
||||||
};
|
};
|
||||||
|
|
||||||
int WINAPI SCompCompress(
|
int WINAPI SCompCompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel)
|
||||||
char * pbOutBuffer,
|
|
||||||
int * pcbOutBuffer,
|
|
||||||
char * pbInBuffer,
|
|
||||||
int cbInBuffer,
|
|
||||||
unsigned uCompressionMask,
|
|
||||||
int nCmpType,
|
|
||||||
int nCmpLevel)
|
|
||||||
{
|
{
|
||||||
COMPRESS CompressFuncArray[0x10]; // Array of compression functions, applied sequentially
|
COMPRESS CompressFuncArray[0x10]; // Array of compression functions, applied sequentially
|
||||||
unsigned char CompressByte[0x10]; // CompressByte for each method in the CompressFuncArray array
|
unsigned char CompressByte[0x10]; // CompressByte for each method in the CompressFuncArray array
|
||||||
char * pbWorkBuffer = NULL; // Temporary storage for decompressed data
|
unsigned char * pbWorkBuffer = NULL; // Temporary storage for decompressed data
|
||||||
char * pbOutput = pbOutBuffer; // Current output buffer
|
unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer;
|
||||||
char * pbInput = pbInBuffer; // Current input buffer
|
unsigned char * pbOutput = (unsigned char *)pvOutBuffer;// Current output buffer
|
||||||
|
unsigned char * pbInput = (unsigned char *)pvInBuffer; // Current input buffer
|
||||||
int nCompressCount = 0;
|
int nCompressCount = 0;
|
||||||
int nCompressIndex = 0;
|
int nCompressIndex = 0;
|
||||||
int nAtLeastOneCompressionDone = 0;
|
int nAtLeastOneCompressionDone = 0;
|
||||||
|
|
@ -789,7 +775,7 @@ int WINAPI SCompCompress(
|
||||||
int nResult = 1;
|
int nResult = 1;
|
||||||
|
|
||||||
// Check for valid parameters
|
// Check for valid parameters
|
||||||
if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pbOutBuffer || !pbInBuffer)
|
if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer)
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -838,7 +824,7 @@ int WINAPI SCompCompress(
|
||||||
// If we need to do more than 1 compression, allocate intermediate buffer
|
// If we need to do more than 1 compression, allocate intermediate buffer
|
||||||
if(nCompressCount > 1)
|
if(nCompressCount > 1)
|
||||||
{
|
{
|
||||||
pbWorkBuffer = STORM_ALLOC(char, *pcbOutBuffer);
|
pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer);
|
||||||
if(pbWorkBuffer == NULL)
|
if(pbWorkBuffer == NULL)
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||||
|
|
@ -884,12 +870,12 @@ int WINAPI SCompCompress(
|
||||||
// If at least one compression succeeded, put the compression
|
// If at least one compression succeeded, put the compression
|
||||||
// mask to the begin of the output buffer
|
// mask to the begin of the output buffer
|
||||||
if(nAtLeastOneCompressionDone)
|
if(nAtLeastOneCompressionDone)
|
||||||
*pbOutBuffer = (char)uCompressionMask;
|
*pbOutBuffer = (unsigned char)uCompressionMask;
|
||||||
*pcbOutBuffer = cbOutBuffer + nAtLeastOneCompressionDone;
|
*pcbOutBuffer = cbOutBuffer + nAtLeastOneCompressionDone;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
|
memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
|
||||||
*pcbOutBuffer = cbInBuffer;
|
*pcbOutBuffer = cbInBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -919,15 +905,13 @@ static TDecompressTable dcmp_table[] =
|
||||||
{MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression
|
{MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression
|
||||||
};
|
};
|
||||||
|
|
||||||
int WINAPI SCompDecompress(
|
int WINAPI SCompDecompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
char * pbOutBuffer,
|
|
||||||
int * pcbOutBuffer,
|
|
||||||
char * pbInBuffer,
|
|
||||||
int cbInBuffer)
|
|
||||||
{
|
{
|
||||||
char * pbWorkBuffer = NULL; // Temporary storage for decompressed data
|
unsigned char * pbWorkBuffer = NULL;
|
||||||
char * pbOutput = pbOutBuffer; // Where to store decompressed data
|
unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer;
|
||||||
char * pbInput; // Where to store decompressed data
|
unsigned char * pbInBuffer = (unsigned char *)pvInBuffer;
|
||||||
|
unsigned char * pbOutput = (unsigned char *)pvOutBuffer;
|
||||||
|
unsigned char * pbInput;
|
||||||
unsigned uCompressionMask; // Decompressions applied to the data
|
unsigned uCompressionMask; // Decompressions applied to the data
|
||||||
unsigned uCompressionCopy; // Decompressions applied to the data
|
unsigned uCompressionCopy; // Decompressions applied to the data
|
||||||
int cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer
|
int cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer
|
||||||
|
|
@ -944,8 +928,8 @@ int WINAPI SCompDecompress(
|
||||||
if(cbOutBuffer == cbInBuffer)
|
if(cbOutBuffer == cbInBuffer)
|
||||||
{
|
{
|
||||||
// If the buffers are equal, don't copy anything.
|
// If the buffers are equal, don't copy anything.
|
||||||
if(pbInBuffer != pbOutBuffer)
|
if(pvInBuffer != pvOutBuffer)
|
||||||
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
|
memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -981,7 +965,7 @@ int WINAPI SCompDecompress(
|
||||||
// If there is more than one compression, we have to allocate extra buffer
|
// If there is more than one compression, we have to allocate extra buffer
|
||||||
if(nCompressCount > 1)
|
if(nCompressCount > 1)
|
||||||
{
|
{
|
||||||
pbWorkBuffer = STORM_ALLOC(char, cbOutBuffer);
|
pbWorkBuffer = STORM_ALLOC(unsigned char, cbOutBuffer);
|
||||||
if(pbWorkBuffer == NULL)
|
if(pbWorkBuffer == NULL)
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||||
|
|
@ -1001,7 +985,7 @@ int WINAPI SCompDecompress(
|
||||||
// Get the correct output buffer
|
// Get the correct output buffer
|
||||||
pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer;
|
pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer;
|
||||||
nCompressIndex--;
|
nCompressIndex--;
|
||||||
|
|
||||||
// Perform the decompression
|
// Perform the decompression
|
||||||
cbOutBuffer = *pcbOutBuffer;
|
cbOutBuffer = *pcbOutBuffer;
|
||||||
nResult = dcmp_table[i].Decompress(pbOutput, &cbOutBuffer, pbInput, cbInLength);
|
nResult = dcmp_table[i].Decompress(pbOutput, &cbOutBuffer, pbInput, cbInLength);
|
||||||
|
|
@ -1027,15 +1011,12 @@ int WINAPI SCompDecompress(
|
||||||
return nResult;
|
return nResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WINAPI SCompDecompress2(
|
int WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
char * pbOutBuffer,
|
|
||||||
int * pcbOutBuffer,
|
|
||||||
char * pbInBuffer,
|
|
||||||
int cbInBuffer)
|
|
||||||
{
|
{
|
||||||
DECOMPRESS pfnDecompress1 = NULL;
|
DECOMPRESS pfnDecompress1 = NULL;
|
||||||
DECOMPRESS pfnDecompress2 = NULL;
|
DECOMPRESS pfnDecompress2 = NULL;
|
||||||
char * pbWorkBuffer = pbOutBuffer;
|
unsigned char * pbWorkBuffer = (unsigned char *)pvOutBuffer;
|
||||||
|
unsigned char * pbInBuffer = (unsigned char *)pvInBuffer;
|
||||||
int cbWorkBuffer = *pcbOutBuffer;
|
int cbWorkBuffer = *pcbOutBuffer;
|
||||||
int nResult;
|
int nResult;
|
||||||
char CompressionMethod;
|
char CompressionMethod;
|
||||||
|
|
@ -1047,8 +1028,8 @@ int WINAPI SCompDecompress2(
|
||||||
// If the outputbuffer is as big as input buffer, just copy the block
|
// If the outputbuffer is as big as input buffer, just copy the block
|
||||||
if(*pcbOutBuffer == cbInBuffer)
|
if(*pcbOutBuffer == cbInBuffer)
|
||||||
{
|
{
|
||||||
if(pbOutBuffer != pbInBuffer)
|
if(pvOutBuffer != pvInBuffer)
|
||||||
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
|
memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1059,35 +1040,35 @@ int WINAPI SCompDecompress2(
|
||||||
// We only recognize a fixed set of compression methods
|
// We only recognize a fixed set of compression methods
|
||||||
switch((unsigned char)CompressionMethod)
|
switch((unsigned char)CompressionMethod)
|
||||||
{
|
{
|
||||||
case MPQ_COMPRESSION_ZLIB:
|
case MPQ_COMPRESSION_ZLIB:
|
||||||
pfnDecompress1 = Decompress_ZLIB;
|
pfnDecompress1 = Decompress_ZLIB;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPQ_COMPRESSION_PKWARE:
|
case MPQ_COMPRESSION_PKWARE:
|
||||||
pfnDecompress1 = Decompress_PKLIB;
|
pfnDecompress1 = Decompress_PKLIB;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPQ_COMPRESSION_BZIP2:
|
case MPQ_COMPRESSION_BZIP2:
|
||||||
pfnDecompress1 = Decompress_BZIP2;
|
pfnDecompress1 = Decompress_BZIP2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPQ_COMPRESSION_LZMA:
|
case MPQ_COMPRESSION_LZMA:
|
||||||
pfnDecompress1 = Decompress_LZMA;
|
pfnDecompress1 = Decompress_LZMA;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPQ_COMPRESSION_SPARSE:
|
case MPQ_COMPRESSION_SPARSE:
|
||||||
pfnDecompress1 = Decompress_SPARSE;
|
pfnDecompress1 = Decompress_SPARSE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB):
|
case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB):
|
||||||
pfnDecompress1 = Decompress_ZLIB;
|
pfnDecompress1 = Decompress_ZLIB;
|
||||||
pfnDecompress2 = Decompress_SPARSE;
|
pfnDecompress2 = Decompress_SPARSE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_BZIP2):
|
case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_BZIP2):
|
||||||
pfnDecompress1 = Decompress_BZIP2;
|
pfnDecompress1 = Decompress_BZIP2;
|
||||||
pfnDecompress2 = Decompress_SPARSE;
|
pfnDecompress2 = Decompress_SPARSE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Note: Any combination including MPQ_COMPRESSION_ADPCM_MONO,
|
// Note: Any combination including MPQ_COMPRESSION_ADPCM_MONO,
|
||||||
|
|
@ -1095,15 +1076,25 @@ int WINAPI SCompDecompress2(
|
||||||
// is not supported by newer MPQs.
|
// is not supported by newer MPQs.
|
||||||
//
|
//
|
||||||
|
|
||||||
default:
|
case (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_HUFFMANN):
|
||||||
SetLastError(ERROR_FILE_CORRUPT);
|
pfnDecompress1 = Decompress_huff;
|
||||||
return 0;
|
pfnDecompress2 = Decompress_ADPCM_mono;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN):
|
||||||
|
pfnDecompress1 = Decompress_huff;
|
||||||
|
pfnDecompress2 = Decompress_ADPCM_stereo;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
SetLastError(ERROR_FILE_CORRUPT);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have to use two decompressions, allocate temporary buffer
|
// If we have to use two decompressions, allocate temporary buffer
|
||||||
if(pfnDecompress2 != NULL)
|
if(pfnDecompress2 != NULL)
|
||||||
{
|
{
|
||||||
pbWorkBuffer = STORM_ALLOC(char, *pcbOutBuffer);
|
pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer);
|
||||||
if(pbWorkBuffer == NULL)
|
if(pbWorkBuffer == NULL)
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||||
|
|
@ -1119,17 +1110,29 @@ int WINAPI SCompDecompress2(
|
||||||
{
|
{
|
||||||
cbInBuffer = cbWorkBuffer;
|
cbInBuffer = cbWorkBuffer;
|
||||||
cbWorkBuffer = *pcbOutBuffer;
|
cbWorkBuffer = *pcbOutBuffer;
|
||||||
nResult = pfnDecompress2(pbOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer);
|
nResult = pfnDecompress2(pvOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Supply the output buffer size
|
// Supply the output buffer size
|
||||||
*pcbOutBuffer = cbWorkBuffer;
|
*pcbOutBuffer = cbWorkBuffer;
|
||||||
|
|
||||||
// Free temporary buffer
|
// Free temporary buffer
|
||||||
if(pbWorkBuffer != pbOutBuffer)
|
if(pbWorkBuffer != pvOutBuffer)
|
||||||
STORM_FREE(pbWorkBuffer);
|
STORM_FREE(pbWorkBuffer);
|
||||||
|
|
||||||
if(nResult == 0)
|
if(nResult == 0)
|
||||||
SetLastError(ERROR_FILE_CORRUPT);
|
SetLastError(ERROR_FILE_CORRUPT);
|
||||||
return nResult;
|
return nResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* */
|
||||||
|
/* File decompression for MPK archives */
|
||||||
|
/* */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
int SCompDecompressMpk(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||||
|
{
|
||||||
|
return Decompress_LZMA_MPK(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
/* Date Ver Who Comment */
|
/* Date Ver Who Comment */
|
||||||
/* -------- ---- --- ------- */
|
/* -------- ---- --- ------- */
|
||||||
/* 27.03.10 1.00 Lad Splitted from SFileCreateArchiveEx.cpp */
|
/* 27.03.10 1.00 Lad Splitted from SFileCreateArchiveEx.cpp */
|
||||||
|
/* 21.04.13 1.01 Dea AddFile callback now part of TMPQArchive */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
#define __STORMLIB_SELF__
|
#define __STORMLIB_SELF__
|
||||||
|
|
@ -13,7 +14,17 @@
|
||||||
#include "StormCommon.h"
|
#include "StormCommon.h"
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Local structures
|
// Local variables
|
||||||
|
|
||||||
|
// Mask for lossy compressions
|
||||||
|
#define MPQ_LOSSY_COMPRESSION_MASK (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN)
|
||||||
|
|
||||||
|
// Data compression for SFileAddFile
|
||||||
|
// Kept here for compatibility with code that was created with StormLib version < 6.50
|
||||||
|
static DWORD DefaultDataCompression = MPQ_COMPRESSION_PKWARE;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// WAVE verification
|
||||||
|
|
||||||
#define FILE_SIGNATURE_RIFF 0x46464952
|
#define FILE_SIGNATURE_RIFF 0x46464952
|
||||||
#define FILE_SIGNATURE_WAVE 0x45564157
|
#define FILE_SIGNATURE_WAVE 0x45564157
|
||||||
|
|
@ -25,8 +36,8 @@ typedef struct _WAVE_FILE_HEADER
|
||||||
DWORD dwChunkId; // 0x52494646 ("RIFF")
|
DWORD dwChunkId; // 0x52494646 ("RIFF")
|
||||||
DWORD dwChunkSize; // Size of that chunk, in bytes
|
DWORD dwChunkSize; // Size of that chunk, in bytes
|
||||||
DWORD dwFormat; // Must be 0x57415645 ("WAVE")
|
DWORD dwFormat; // Must be 0x57415645 ("WAVE")
|
||||||
|
|
||||||
// Format sub-chunk
|
// Format sub-chunk
|
||||||
DWORD dwSubChunk1Id; // 0x666d7420 ("fmt ")
|
DWORD dwSubChunk1Id; // 0x666d7420 ("fmt ")
|
||||||
DWORD dwSubChunk1Size; // 0x16 for PCM
|
DWORD dwSubChunk1Size; // 0x16 for PCM
|
||||||
USHORT wAudioFormat; // 1 = PCM. Other value means some sort of compression
|
USHORT wAudioFormat; // 1 = PCM. Other value means some sort of compression
|
||||||
|
|
@ -39,36 +50,29 @@ typedef struct _WAVE_FILE_HEADER
|
||||||
// Followed by "data" sub-chunk (we don't care)
|
// Followed by "data" sub-chunk (we don't care)
|
||||||
} WAVE_FILE_HEADER, *PWAVE_FILE_HEADER;
|
} WAVE_FILE_HEADER, *PWAVE_FILE_HEADER;
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
static bool IsWaveFile_16BitsPerAdpcmSample(
|
||||||
// Local variables
|
LPBYTE pbFileData,
|
||||||
|
DWORD cbFileData,
|
||||||
// Data compression for SFileAddFile
|
LPDWORD pdwChannels)
|
||||||
// Kept here for compatibility with code that was created with StormLib version < 6.50
|
|
||||||
static DWORD DefaultDataCompression = MPQ_COMPRESSION_PKWARE;
|
|
||||||
|
|
||||||
static SFILE_ADDFILE_CALLBACK AddFileCB = NULL;
|
|
||||||
static void * pvUserData = NULL;
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// MPQ write data functions
|
|
||||||
|
|
||||||
#define LOSSY_COMPRESSION_MASK (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN)
|
|
||||||
|
|
||||||
static int IsWaveFile(
|
|
||||||
LPBYTE pbFileData,
|
|
||||||
DWORD cbFileData,
|
|
||||||
LPDWORD pdwChannels)
|
|
||||||
{
|
{
|
||||||
PWAVE_FILE_HEADER pWaveHdr = (PWAVE_FILE_HEADER)pbFileData;
|
PWAVE_FILE_HEADER pWaveHdr = (PWAVE_FILE_HEADER)pbFileData;
|
||||||
|
|
||||||
|
// The amount of file data must be at least size of WAVE header
|
||||||
if(cbFileData > sizeof(WAVE_FILE_HEADER))
|
if(cbFileData > sizeof(WAVE_FILE_HEADER))
|
||||||
{
|
{
|
||||||
|
// Check for the RIFF header
|
||||||
if(pWaveHdr->dwChunkId == FILE_SIGNATURE_RIFF && pWaveHdr->dwFormat == FILE_SIGNATURE_WAVE)
|
if(pWaveHdr->dwChunkId == FILE_SIGNATURE_RIFF && pWaveHdr->dwFormat == FILE_SIGNATURE_WAVE)
|
||||||
{
|
{
|
||||||
|
// Check for ADPCM format
|
||||||
if(pWaveHdr->dwSubChunk1Id == FILE_SIGNATURE_FMT && pWaveHdr->wAudioFormat == AUDIO_FORMAT_PCM)
|
if(pWaveHdr->dwSubChunk1Id == FILE_SIGNATURE_FMT && pWaveHdr->wAudioFormat == AUDIO_FORMAT_PCM)
|
||||||
{
|
{
|
||||||
*pdwChannels = pWaveHdr->wChannels;
|
// Now the number of bits per sample must be at least 16.
|
||||||
return true;
|
// If not, the WAVE file gets corrupted by the ADPCM compression
|
||||||
|
if(pWaveHdr->wBitsPerSample >= 0x10)
|
||||||
|
{
|
||||||
|
*pdwChannels = pWaveHdr->wChannels;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -76,33 +80,29 @@ static int IsWaveFile(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// MPQ write data functions
|
||||||
|
|
||||||
static int WriteDataToMpqFile(
|
static int WriteDataToMpqFile(
|
||||||
TMPQArchive * ha,
|
TMPQArchive * ha,
|
||||||
TMPQFile * hf,
|
TMPQFile * hf,
|
||||||
LPBYTE pbFileData,
|
LPBYTE pbFileData,
|
||||||
DWORD dwDataSize,
|
DWORD dwDataSize,
|
||||||
DWORD dwCompression)
|
DWORD dwCompression)
|
||||||
{
|
{
|
||||||
TFileEntry * pFileEntry = hf->pFileEntry;
|
TFileEntry * pFileEntry = hf->pFileEntry;
|
||||||
ULONGLONG ByteOffset;
|
ULONGLONG ByteOffset;
|
||||||
LPBYTE pbCompressed = NULL; // Compressed (target) data
|
LPBYTE pbCompressed = NULL; // Compressed (target) data
|
||||||
LPBYTE pbToWrite = NULL; // Data to write to the file
|
LPBYTE pbToWrite = hf->pbFileSector; // Data to write to the file
|
||||||
int nCompressionLevel = -1; // ADPCM compression level (only used for wave files)
|
int nCompressionLevel; // ADPCM compression level (only used for wave files)
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// If the caller wants ADPCM compression, we will set wave compression level to 4,
|
|
||||||
// which corresponds to medium quality
|
|
||||||
if(dwCompression & LOSSY_COMPRESSION_MASK)
|
|
||||||
nCompressionLevel = 4;
|
|
||||||
|
|
||||||
// Make sure that the caller won't overrun the previously initiated file size
|
// Make sure that the caller won't overrun the previously initiated file size
|
||||||
assert(hf->dwFilePos + dwDataSize <= pFileEntry->dwFileSize);
|
assert(hf->dwFilePos + dwDataSize <= pFileEntry->dwFileSize);
|
||||||
assert(hf->dwSectorCount != 0);
|
assert(hf->dwSectorCount != 0);
|
||||||
assert(hf->pbFileSector != NULL);
|
assert(hf->pbFileSector != NULL);
|
||||||
if((hf->dwFilePos + dwDataSize) > pFileEntry->dwFileSize)
|
if((hf->dwFilePos + dwDataSize) > pFileEntry->dwFileSize)
|
||||||
return ERROR_DISK_FULL;
|
return ERROR_DISK_FULL;
|
||||||
pbToWrite = hf->pbFileSector;
|
|
||||||
|
|
||||||
// Now write all data to the file sector buffer
|
// Now write all data to the file sector buffer
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
|
|
@ -111,11 +111,11 @@ static int WriteDataToMpqFile(
|
||||||
DWORD dwSectorIndex = hf->dwFilePos / hf->dwSectorSize;
|
DWORD dwSectorIndex = hf->dwFilePos / hf->dwSectorSize;
|
||||||
DWORD dwBytesToCopy;
|
DWORD dwBytesToCopy;
|
||||||
|
|
||||||
// Process all data.
|
// Process all data.
|
||||||
while(dwDataSize != 0)
|
while(dwDataSize != 0)
|
||||||
{
|
{
|
||||||
dwBytesToCopy = dwDataSize;
|
dwBytesToCopy = dwDataSize;
|
||||||
|
|
||||||
// Check for sector overflow
|
// Check for sector overflow
|
||||||
if(dwBytesToCopy > (hf->dwSectorSize - dwBytesInSector))
|
if(dwBytesToCopy > (hf->dwSectorSize - dwBytesInSector))
|
||||||
dwBytesToCopy = (hf->dwSectorSize - dwBytesInSector);
|
dwBytesToCopy = (hf->dwSectorSize - dwBytesInSector);
|
||||||
|
|
@ -141,7 +141,7 @@ static int WriteDataToMpqFile(
|
||||||
hf->dwCrc32 = crc32(hf->dwCrc32, hf->pbFileSector, dwBytesInSector);
|
hf->dwCrc32 = crc32(hf->dwCrc32, hf->pbFileSector, dwBytesInSector);
|
||||||
|
|
||||||
// Compress the file sector, if needed
|
// Compress the file sector, if needed
|
||||||
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED)
|
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK)
|
||||||
{
|
{
|
||||||
int nOutBuffer = (int)dwBytesInSector;
|
int nOutBuffer = (int)dwBytesInSector;
|
||||||
int nInBuffer = (int)dwBytesInSector;
|
int nInBuffer = (int)dwBytesInSector;
|
||||||
|
|
@ -160,27 +160,33 @@ static int WriteDataToMpqFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Note that both SCompImplode and SCompCompress give original buffer,
|
// Note that both SCompImplode and SCompCompress copy data as-is,
|
||||||
// if they are unable to comperss the data.
|
// if they are unable to compress the data.
|
||||||
//
|
//
|
||||||
|
|
||||||
if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
|
if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
|
||||||
{
|
{
|
||||||
SCompImplode((char *)pbCompressed,
|
SCompImplode(pbCompressed, &nOutBuffer, hf->pbFileSector, nInBuffer);
|
||||||
&nOutBuffer,
|
|
||||||
(char *)hf->pbFileSector,
|
|
||||||
nInBuffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
|
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
|
||||||
{
|
{
|
||||||
SCompCompress((char *)pbCompressed,
|
// If this is the first sector, we need to override the given compression
|
||||||
&nOutBuffer,
|
// by the first sector compression. This is because the entire sector must
|
||||||
(char *)hf->pbFileSector,
|
// be compressed by the same compression.
|
||||||
nInBuffer,
|
//
|
||||||
(unsigned)dwCompression,
|
// Test case:
|
||||||
0,
|
//
|
||||||
nCompressionLevel);
|
// WRITE_FILE(hFile, pvBuffer, 0x10, MPQ_COMPRESSION_PKWARE) // Write 0x10 bytes (sector 0)
|
||||||
|
// WRITE_FILE(hFile, pvBuffer, 0x10, MPQ_COMPRESSION_ADPCM_MONO) // Write 0x10 bytes (still sector 0)
|
||||||
|
// WRITE_FILE(hFile, pvBuffer, 0x10, MPQ_COMPRESSION_ADPCM_MONO) // Write 0x10 bytes (still sector 0)
|
||||||
|
// WRITE_FILE(hFile, pvBuffer, 0x10, MPQ_COMPRESSION_ADPCM_MONO) // Write 0x10 bytes (still sector 0)
|
||||||
|
dwCompression = (dwSectorIndex == 0) ? hf->dwCompression0 : dwCompression;
|
||||||
|
|
||||||
|
// If the caller wants ADPCM compression, we will set wave compression level to 4,
|
||||||
|
// which corresponds to medium quality
|
||||||
|
nCompressionLevel = (dwCompression & MPQ_LOSSY_COMPRESSION_MASK) ? 4 : -1;
|
||||||
|
SCompCompress(pbCompressed, &nOutBuffer, hf->pbFileSector, nInBuffer, (unsigned)dwCompression, 0, nCompressionLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update sector positions
|
// Update sector positions
|
||||||
|
|
@ -191,7 +197,7 @@ static int WriteDataToMpqFile(
|
||||||
// We have to calculate sector CRC, if enabled
|
// We have to calculate sector CRC, if enabled
|
||||||
if(hf->SectorChksums != NULL)
|
if(hf->SectorChksums != NULL)
|
||||||
hf->SectorChksums[dwSectorIndex] = adler32(0, pbCompressed, nOutBuffer);
|
hf->SectorChksums[dwSectorIndex] = adler32(0, pbCompressed, nOutBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt the sector, if necessary
|
// Encrypt the sector, if necessary
|
||||||
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
|
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
|
||||||
|
|
@ -209,8 +215,8 @@ static int WriteDataToMpqFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the compact callback, if any
|
// Call the compact callback, if any
|
||||||
if(AddFileCB != NULL)
|
if(ha->pfnAddFileCB != NULL)
|
||||||
AddFileCB(pvUserData, hf->dwFilePos, hf->dwDataSize, false);
|
ha->pfnAddFileCB(ha->pvAddFileUserData, hf->dwFilePos, hf->dwDataSize, false);
|
||||||
|
|
||||||
// Update the compressed file size
|
// Update the compressed file size
|
||||||
pFileEntry->dwCmpSize += dwBytesInSector;
|
pFileEntry->dwCmpSize += dwBytesInSector;
|
||||||
|
|
@ -230,10 +236,10 @@ static int WriteDataToMpqFile(
|
||||||
// Recrypts file data for file renaming
|
// Recrypts file data for file renaming
|
||||||
|
|
||||||
static int RecryptFileData(
|
static int RecryptFileData(
|
||||||
TMPQArchive * ha,
|
TMPQArchive * ha,
|
||||||
TMPQFile * hf,
|
TMPQFile * hf,
|
||||||
const char * szFileName,
|
const char * szFileName,
|
||||||
const char * szNewFileName)
|
const char * szNewFileName)
|
||||||
{
|
{
|
||||||
ULONGLONG RawFilePos;
|
ULONGLONG RawFilePos;
|
||||||
TFileEntry * pFileEntry = hf->pFileEntry;
|
TFileEntry * pFileEntry = hf->pFileEntry;
|
||||||
|
|
@ -246,8 +252,8 @@ static int RecryptFileData(
|
||||||
assert(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED);
|
assert(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED);
|
||||||
|
|
||||||
// File decryption key is calculated from the plain name
|
// File decryption key is calculated from the plain name
|
||||||
szNewFileName = GetPlainFileNameA(szNewFileName);
|
szNewFileName = GetPlainFileName(szNewFileName);
|
||||||
szFileName = GetPlainFileNameA(szFileName);
|
szFileName = GetPlainFileName(szFileName);
|
||||||
|
|
||||||
// Calculate both file keys
|
// Calculate both file keys
|
||||||
dwOldKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags);
|
dwOldKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags);
|
||||||
|
|
@ -277,7 +283,7 @@ static int RecryptFileData(
|
||||||
if(hf->SectorOffsets != NULL)
|
if(hf->SectorOffsets != NULL)
|
||||||
{
|
{
|
||||||
// Allocate secondary buffer for sectors copy
|
// Allocate secondary buffer for sectors copy
|
||||||
DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]);
|
DWORD * SectorOffsetsCopy = STORM_ALLOC(DWORD, hf->SectorOffsets[0] / sizeof(DWORD));
|
||||||
DWORD dwSectorOffsLen = hf->SectorOffsets[0];
|
DWORD dwSectorOffsLen = hf->SectorOffsets[0];
|
||||||
|
|
||||||
if(SectorOffsetsCopy == NULL)
|
if(SectorOffsetsCopy == NULL)
|
||||||
|
|
@ -325,7 +331,7 @@ static int RecryptFileData(
|
||||||
}
|
}
|
||||||
|
|
||||||
// If necessary, re-encrypt the sector
|
// If necessary, re-encrypt the sector
|
||||||
// Note: Recompression is not necessary here. Unlike encryption,
|
// Note: Recompression is not necessary here. Unlike encryption,
|
||||||
// the compression does not depend on the position of the file in MPQ.
|
// the compression does not depend on the position of the file in MPQ.
|
||||||
BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector);
|
BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector);
|
||||||
DecryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwOldKey + dwSector);
|
DecryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwOldKey + dwSector);
|
||||||
|
|
@ -351,13 +357,13 @@ static int RecryptFileData(
|
||||||
// Support functions for adding files to the MPQ
|
// Support functions for adding files to the MPQ
|
||||||
|
|
||||||
int SFileAddFile_Init(
|
int SFileAddFile_Init(
|
||||||
TMPQArchive * ha,
|
TMPQArchive * ha,
|
||||||
const char * szFileName,
|
const char * szFileName,
|
||||||
ULONGLONG FileTime,
|
ULONGLONG FileTime,
|
||||||
DWORD dwFileSize,
|
DWORD dwFileSize,
|
||||||
LCID lcLocale,
|
LCID lcLocale,
|
||||||
DWORD dwFlags,
|
DWORD dwFlags,
|
||||||
TMPQFile ** phf)
|
TMPQFile ** phf)
|
||||||
{
|
{
|
||||||
TFileEntry * pFileEntry = NULL;
|
TFileEntry * pFileEntry = NULL;
|
||||||
ULONGLONG TempPos; // For various file offset calculations
|
ULONGLONG TempPos; // For various file offset calculations
|
||||||
|
|
@ -375,9 +381,9 @@ int SFileAddFile_Init(
|
||||||
dwFlags &= ~MPQ_FILE_SECTOR_CRC;
|
dwFlags &= ~MPQ_FILE_SECTOR_CRC;
|
||||||
|
|
||||||
// Sector CRC is not allowed if the file is not compressed
|
// Sector CRC is not allowed if the file is not compressed
|
||||||
if(!(dwFlags & MPQ_FILE_COMPRESSED))
|
if(!(dwFlags & MPQ_FILE_COMPRESS_MASK))
|
||||||
dwFlags &= ~MPQ_FILE_SECTOR_CRC;
|
dwFlags &= ~MPQ_FILE_SECTOR_CRC;
|
||||||
|
|
||||||
// Fix Key is not allowed if the file is not enrypted
|
// Fix Key is not allowed if the file is not enrypted
|
||||||
if(!(dwFlags & MPQ_FILE_ENCRYPTED))
|
if(!(dwFlags & MPQ_FILE_ENCRYPTED))
|
||||||
dwFlags &= ~MPQ_FILE_FIX_KEY;
|
dwFlags &= ~MPQ_FILE_FIX_KEY;
|
||||||
|
|
@ -388,21 +394,18 @@ int SFileAddFile_Init(
|
||||||
lcLocale = 0;
|
lcLocale = 0;
|
||||||
|
|
||||||
// Allocate the TMPQFile entry for newly added file
|
// Allocate the TMPQFile entry for newly added file
|
||||||
hf = CreateMpqFile(ha);
|
hf = CreateFileHandle(ha, NULL);
|
||||||
if(hf == NULL)
|
if(hf == NULL)
|
||||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
|
||||||
// Find a free space in the MPQ, as well as free block table entry
|
// Find a free space in the MPQ and verify if it's not over 4 GB on MPQs v1
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// Find the position where the file will be stored
|
// Find the position where the file will be stored
|
||||||
FindFreeMpqSpace(ha, &hf->MpqFilePos);
|
hf->MpqFilePos = FindFreeMpqSpace(ha);
|
||||||
hf->RawFilePos = ha->MpqPos + hf->MpqFilePos;
|
hf->RawFilePos = ha->MpqPos + hf->MpqFilePos;
|
||||||
hf->bIsWriteHandle = true;
|
hf->bIsWriteHandle = true;
|
||||||
|
|
||||||
// Sanity check: The MPQ must be marked as changed at this point
|
|
||||||
assert((ha->dwFlags & MPQ_FLAG_CHANGED) != 0);
|
|
||||||
|
|
||||||
// When format V1, the size of the archive cannot exceed 4 GB
|
// When format V1, the size of the archive cannot exceed 4 GB
|
||||||
if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1)
|
if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1)
|
||||||
{
|
{
|
||||||
|
|
@ -428,34 +431,31 @@ int SFileAddFile_Init(
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If the file exists and "replace existing" is not set, fail it
|
// If the caller didn't set MPQ_FILE_REPLACEEXISTING, fail it
|
||||||
if((dwFlags & MPQ_FILE_REPLACEEXISTING) == 0)
|
if((dwFlags & MPQ_FILE_REPLACEEXISTING) == 0)
|
||||||
nError = ERROR_ALREADY_EXISTS;
|
nError = ERROR_ALREADY_EXISTS;
|
||||||
|
|
||||||
// If the file entry already contains a file
|
// When replacing an existing file,
|
||||||
// and it is a pseudo-name, replace it
|
// we still need to invalidate the (attributes) file
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
InvalidateInternalFiles(ha);
|
||||||
AllocateFileName(pFileEntry, szFileName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// Fill the file entry and TMPQFile structure
|
||||||
// At this point, the file name in file entry must be non-NULL
|
|
||||||
//
|
|
||||||
|
|
||||||
// Create key for file encryption
|
|
||||||
if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_ENCRYPTED))
|
|
||||||
{
|
|
||||||
hf->dwFileKey = DecryptFileKey(szFileName, hf->MpqFilePos, dwFileSize, dwFlags);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
|
// At this point, the file name in the file entry must be set
|
||||||
|
assert(pFileEntry->szFileName != NULL);
|
||||||
|
assert(_stricmp(pFileEntry->szFileName, szFileName) == 0);
|
||||||
|
|
||||||
// Initialize the hash entry for the file
|
// Initialize the hash entry for the file
|
||||||
hf->pFileEntry = pFileEntry;
|
hf->pFileEntry = pFileEntry;
|
||||||
hf->dwDataSize = dwFileSize;
|
hf->dwDataSize = dwFileSize;
|
||||||
|
|
||||||
|
// Decrypt the file key
|
||||||
|
if(dwFlags & MPQ_FILE_ENCRYPTED)
|
||||||
|
hf->dwFileKey = DecryptFileKey(szFileName, hf->MpqFilePos, dwFileSize, dwFlags);
|
||||||
|
|
||||||
// Initialize the block table entry for the file
|
// Initialize the block table entry for the file
|
||||||
pFileEntry->ByteOffset = hf->MpqFilePos;
|
pFileEntry->ByteOffset = hf->MpqFilePos;
|
||||||
|
|
@ -473,14 +473,20 @@ int SFileAddFile_Init(
|
||||||
// If the caller gave us a file time, use it.
|
// If the caller gave us a file time, use it.
|
||||||
pFileEntry->FileTime = FileTime;
|
pFileEntry->FileTime = FileTime;
|
||||||
|
|
||||||
|
// Mark the archive as modified
|
||||||
|
ha->dwFlags |= MPQ_FLAG_CHANGED;
|
||||||
|
|
||||||
// Call the callback, if needed
|
// Call the callback, if needed
|
||||||
if(AddFileCB != NULL)
|
if(ha->pfnAddFileCB != NULL)
|
||||||
AddFileCB(pvUserData, 0, hf->dwDataSize, false);
|
ha->pfnAddFileCB(ha->pvAddFileUserData, 0, hf->dwDataSize, false);
|
||||||
|
hf->nAddFileError = ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If an error occured, remember it
|
// Fre the file handle if failed
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS && hf != NULL)
|
||||||
hf->bErrorOccured = true;
|
FreeFileHandle(hf);
|
||||||
|
|
||||||
|
// Give the handle to the caller
|
||||||
*phf = hf;
|
*phf = hf;
|
||||||
return nError;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
@ -505,12 +511,9 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d
|
||||||
ULONGLONG RawFilePos = hf->RawFilePos;
|
ULONGLONG RawFilePos = hf->RawFilePos;
|
||||||
|
|
||||||
// Allocate buffer for file sector
|
// Allocate buffer for file sector
|
||||||
nError = AllocateSectorBuffer(hf);
|
hf->nAddFileError = nError = AllocateSectorBuffer(hf);
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
{
|
|
||||||
hf->bErrorOccured = true;
|
|
||||||
return nError;
|
return nError;
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate patch info, if the data is patch
|
// Allocate patch info, if the data is patch
|
||||||
if(hf->pPatchInfo == NULL && IsIncrementalPatchFile(pvData, dwSize, &hf->dwPatchedFileSize))
|
if(hf->pPatchInfo == NULL && IsIncrementalPatchFile(pvData, dwSize, &hf->dwPatchedFileSize))
|
||||||
|
|
@ -519,34 +522,25 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d
|
||||||
hf->pFileEntry->dwFlags |= MPQ_FILE_PATCH_FILE;
|
hf->pFileEntry->dwFlags |= MPQ_FILE_PATCH_FILE;
|
||||||
|
|
||||||
// Allocate the patch info
|
// Allocate the patch info
|
||||||
nError = AllocatePatchInfo(hf, false);
|
hf->nAddFileError = nError = AllocatePatchInfo(hf, false);
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
{
|
|
||||||
hf->bErrorOccured = true;
|
|
||||||
return nError;
|
return nError;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate sector offsets
|
// Allocate sector offsets
|
||||||
if(hf->SectorOffsets == NULL)
|
if(hf->SectorOffsets == NULL)
|
||||||
{
|
{
|
||||||
nError = AllocateSectorOffsets(hf, false);
|
hf->nAddFileError = nError = AllocateSectorOffsets(hf, false);
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
{
|
|
||||||
hf->bErrorOccured = true;
|
|
||||||
return nError;
|
return nError;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create array of sector checksums
|
// Create array of sector checksums
|
||||||
if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC))
|
if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC))
|
||||||
{
|
{
|
||||||
nError = AllocateSectorChecksums(hf, false);
|
hf->nAddFileError = nError = AllocateSectorChecksums(hf, false);
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
{
|
|
||||||
hf->bErrorOccured = true;
|
|
||||||
return nError;
|
return nError;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-save the patch info, if any
|
// Pre-save the patch info, if any
|
||||||
|
|
@ -554,7 +548,7 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d
|
||||||
{
|
{
|
||||||
if(!FileStream_Write(ha->pStream, &RawFilePos, hf->pPatchInfo, hf->pPatchInfo->dwLength))
|
if(!FileStream_Write(ha->pStream, &RawFilePos, hf->pPatchInfo, hf->pPatchInfo->dwLength))
|
||||||
nError = GetLastError();
|
nError = GetLastError();
|
||||||
|
|
||||||
pFileEntry->dwCmpSize += hf->pPatchInfo->dwLength;
|
pFileEntry->dwCmpSize += hf->pPatchInfo->dwLength;
|
||||||
RawFilePos += hf->pPatchInfo->dwLength;
|
RawFilePos += hf->pPatchInfo->dwLength;
|
||||||
}
|
}
|
||||||
|
|
@ -574,7 +568,16 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d
|
||||||
|
|
||||||
// Write the MPQ data to the file
|
// Write the MPQ data to the file
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
// Save the first sector compression to the file structure
|
||||||
|
// Note that the entire first file sector will be compressed
|
||||||
|
// by compression that was passed to the first call of SFileAddFile_Write
|
||||||
|
if(hf->dwFilePos == 0)
|
||||||
|
hf->dwCompression0 = dwCompression;
|
||||||
|
|
||||||
|
// Write the data to the MPQ
|
||||||
nError = WriteDataToMpqFile(ha, hf, (LPBYTE)pvData, dwSize, dwCompression);
|
nError = WriteDataToMpqFile(ha, hf, (LPBYTE)pvData, dwSize, dwCompression);
|
||||||
|
}
|
||||||
|
|
||||||
// If it succeeded and we wrote all the file data,
|
// If it succeeded and we wrote all the file data,
|
||||||
// we need to re-save sector offset table
|
// we need to re-save sector offset table
|
||||||
|
|
@ -592,8 +595,6 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d
|
||||||
if(hf->SectorChksums != NULL)
|
if(hf->SectorChksums != NULL)
|
||||||
{
|
{
|
||||||
nError = WriteSectorChecksums(hf);
|
nError = WriteSectorChecksums(hf);
|
||||||
if(nError != ERROR_SUCCESS)
|
|
||||||
hf->bErrorOccured = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now write patch info
|
// Now write patch info
|
||||||
|
|
@ -603,16 +604,12 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d
|
||||||
hf->pPatchInfo->dwDataSize = hf->pFileEntry->dwFileSize;
|
hf->pPatchInfo->dwDataSize = hf->pFileEntry->dwFileSize;
|
||||||
hf->pFileEntry->dwFileSize = hf->dwPatchedFileSize;
|
hf->pFileEntry->dwFileSize = hf->dwPatchedFileSize;
|
||||||
nError = WritePatchInfo(hf);
|
nError = WritePatchInfo(hf);
|
||||||
if(nError != ERROR_SUCCESS)
|
|
||||||
hf->bErrorOccured = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now write sector offsets to the file
|
// Now write sector offsets to the file
|
||||||
if(hf->SectorOffsets != NULL)
|
if(hf->SectorOffsets != NULL)
|
||||||
{
|
{
|
||||||
nError = WriteSectorOffsets(hf);
|
nError = WriteSectorOffsets(hf);
|
||||||
if(nError != ERROR_SUCCESS)
|
|
||||||
hf->bErrorOccured = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the MD5 hashes of each file chunk, if required
|
// Write the MD5 hashes of each file chunk, if required
|
||||||
|
|
@ -622,16 +619,12 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d
|
||||||
ha->MpqPos + hf->pFileEntry->ByteOffset,
|
ha->MpqPos + hf->pFileEntry->ByteOffset,
|
||||||
hf->pFileEntry->dwCmpSize,
|
hf->pFileEntry->dwCmpSize,
|
||||||
ha->pHeader->dwRawChunkSize);
|
ha->pHeader->dwRawChunkSize);
|
||||||
if(nError != ERROR_SUCCESS)
|
|
||||||
hf->bErrorOccured = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
hf->bErrorOccured = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Store the error code from the Write File operation
|
||||||
|
hf->nAddFileError = nError;
|
||||||
return nError;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -639,88 +632,87 @@ int SFileAddFile_Finish(TMPQFile * hf)
|
||||||
{
|
{
|
||||||
TMPQArchive * ha = hf->ha;
|
TMPQArchive * ha = hf->ha;
|
||||||
TFileEntry * pFileEntry = hf->pFileEntry;
|
TFileEntry * pFileEntry = hf->pFileEntry;
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = hf->nAddFileError;
|
||||||
|
|
||||||
// If all previous operations succeeded, we can update the MPQ
|
// If all previous operations succeeded, we can update the MPQ
|
||||||
if(!hf->bErrorOccured)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// Verify if the caller wrote the file properly
|
// Verify if the caller wrote the file properly
|
||||||
if(hf->pPatchInfo == NULL)
|
if(hf->pPatchInfo == NULL)
|
||||||
{
|
{
|
||||||
assert(pFileEntry != NULL);
|
assert(pFileEntry != NULL);
|
||||||
if(hf->dwFilePos != pFileEntry->dwFileSize)
|
if(hf->dwFilePos != pFileEntry->dwFileSize)
|
||||||
{
|
|
||||||
nError = ERROR_CAN_NOT_COMPLETE;
|
nError = ERROR_CAN_NOT_COMPLETE;
|
||||||
hf->bErrorOccured = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(hf->dwFilePos != hf->pPatchInfo->dwDataSize)
|
if(hf->dwFilePos != hf->pPatchInfo->dwDataSize)
|
||||||
{
|
|
||||||
nError = ERROR_CAN_NOT_COMPLETE;
|
nError = ERROR_CAN_NOT_COMPLETE;
|
||||||
hf->bErrorOccured = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!hf->bErrorOccured)
|
// Now we need to recreate the HET table, if exists
|
||||||
|
if(nError == ERROR_SUCCESS && ha->pHetTable != NULL)
|
||||||
|
{
|
||||||
|
nError = RebuildHetTable(ha);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the block table size
|
||||||
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// Call the user callback, if any
|
// Call the user callback, if any
|
||||||
if(AddFileCB != NULL)
|
if(ha->pfnAddFileCB != NULL)
|
||||||
AddFileCB(pvUserData, hf->dwDataSize, hf->dwDataSize, true);
|
ha->pfnAddFileCB(ha->pvAddFileUserData, hf->dwDataSize, hf->dwDataSize, true);
|
||||||
|
|
||||||
// Update the size of the block table
|
|
||||||
ha->pHeader->dwBlockTableSize = ha->dwFileTableSize;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Free the file entry in MPQ tables
|
// Free the file entry in MPQ tables
|
||||||
if(pFileEntry != NULL)
|
if(pFileEntry != NULL)
|
||||||
FreeFileEntry(ha, pFileEntry);
|
DeleteFileEntry(ha, pFileEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the add file callback
|
// Clear the add file callback
|
||||||
FreeMPQFile(hf);
|
FreeFileHandle(hf);
|
||||||
pvUserData = NULL;
|
|
||||||
AddFileCB = NULL;
|
|
||||||
return nError;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Adds data as file to the archive
|
// Adds data as file to the archive
|
||||||
|
|
||||||
bool WINAPI SFileCreateFile(
|
bool WINAPI SFileCreateFile(
|
||||||
HANDLE hMpq,
|
HANDLE hMpq,
|
||||||
const char * szArchivedName,
|
const char * szArchivedName,
|
||||||
ULONGLONG FileTime,
|
ULONGLONG FileTime,
|
||||||
DWORD dwFileSize,
|
DWORD dwFileSize,
|
||||||
LCID lcLocale,
|
LCID lcLocale,
|
||||||
DWORD dwFlags,
|
DWORD dwFlags,
|
||||||
HANDLE * phFile)
|
HANDLE * phFile)
|
||||||
{
|
{
|
||||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Check valid parameters
|
// Check valid parameters
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
nError = ERROR_INVALID_HANDLE;
|
nError = ERROR_INVALID_HANDLE;
|
||||||
if(szArchivedName == NULL || *szArchivedName == 0)
|
if(szArchivedName == NULL || *szArchivedName == 0)
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
if(phFile == NULL)
|
if(phFile == NULL)
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
// Don't allow to add file if the MPQ is open for read only
|
// Don't allow to add file if the MPQ is open for read only
|
||||||
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
if(nError == ERROR_SUCCESS)
|
||||||
nError = ERROR_ACCESS_DENIED;
|
{
|
||||||
|
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
||||||
|
nError = ERROR_ACCESS_DENIED;
|
||||||
|
|
||||||
// Don't allow to add a file under pseudo-file name
|
// Don't allow to add a file under pseudo-file name
|
||||||
if(IsPseudoFileName(szArchivedName, NULL))
|
if(IsPseudoFileName(szArchivedName, NULL))
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
// Don't allow to add any of the internal files
|
// Don't allow to add any of the internal files
|
||||||
if(IsInternalMpqFileName(szArchivedName))
|
if(IsInternalMpqFileName(szArchivedName))
|
||||||
nError = ERROR_INTERNAL_FILE;
|
nError = ERROR_INTERNAL_FILE;
|
||||||
|
}
|
||||||
|
|
||||||
// Perform validity check of the MPQ flags
|
// Perform validity check of the MPQ flags
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
|
|
@ -733,17 +725,18 @@ bool WINAPI SFileCreateFile(
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the file in MPQ
|
// Check for MPQs that have invalid block table size
|
||||||
|
// Example: size of block table: 0x41, size of hash table: 0x40
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// Invalidate the entries for (listfile) and (attributes)
|
if(ha->dwFileTableSize > ha->dwMaxFileCount)
|
||||||
// After we are done with MPQ changes, we need to re-create them anyway
|
nError = ERROR_DISK_FULL;
|
||||||
InvalidateInternalFiles(ha);
|
|
||||||
|
|
||||||
// Initiate the add file operation
|
|
||||||
nError = SFileAddFile_Init(ha, szArchivedName, FileTime, dwFileSize, lcLocale, dwFlags, (TMPQFile **)phFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initiate the add file operation
|
||||||
|
if(nError == ERROR_SUCCESS)
|
||||||
|
nError = SFileAddFile_Init(ha, szArchivedName, FileTime, dwFileSize, lcLocale, dwFlags, (TMPQFile **)phFile);
|
||||||
|
|
||||||
// Deal with the errors
|
// Deal with the errors
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
SetLastError(nError);
|
SetLastError(nError);
|
||||||
|
|
@ -751,16 +744,16 @@ bool WINAPI SFileCreateFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WINAPI SFileWriteFile(
|
bool WINAPI SFileWriteFile(
|
||||||
HANDLE hFile,
|
HANDLE hFile,
|
||||||
const void * pvData,
|
const void * pvData,
|
||||||
DWORD dwSize,
|
DWORD dwSize,
|
||||||
DWORD dwCompression)
|
DWORD dwCompression)
|
||||||
{
|
{
|
||||||
TMPQFile * hf = (TMPQFile *)hFile;
|
TMPQFile * hf = (TMPQFile *)hFile;
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Check the proper parameters
|
// Check the proper parameters
|
||||||
if(!IsValidFileHandle(hf))
|
if(!IsValidFileHandle(hFile))
|
||||||
nError = ERROR_INVALID_HANDLE;
|
nError = ERROR_INVALID_HANDLE;
|
||||||
if(hf->bIsWriteHandle == false)
|
if(hf->bIsWriteHandle == false)
|
||||||
nError = ERROR_INVALID_HANDLE;
|
nError = ERROR_INVALID_HANDLE;
|
||||||
|
|
@ -774,14 +767,14 @@ bool WINAPI SFileWriteFile(
|
||||||
// the calling application must ensure that such flag combination doesn't get here
|
// the calling application must ensure that such flag combination doesn't get here
|
||||||
//
|
//
|
||||||
|
|
||||||
// if(dwFlags & MPQ_FILE_IMPLODE)
|
// if(dwFlags & MPQ_FILE_IMPLODE)
|
||||||
// nError = ERROR_INVALID_PARAMETER;
|
// nError = ERROR_INVALID_PARAMETER;
|
||||||
//
|
//
|
||||||
// if(dwFlags & MPQ_FILE_ENCRYPTED)
|
// if(dwFlags & MPQ_FILE_ENCRYPTED)
|
||||||
// nError = ERROR_INVALID_PARAMETER;
|
// nError = ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
// Lossy compression is not allowed on single unit files
|
// Lossy compression is not allowed on single unit files
|
||||||
if(dwCompression & LOSSY_COMPRESSION_MASK)
|
if(dwCompression & MPQ_LOSSY_COMPRESSION_MASK)
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -789,7 +782,7 @@ bool WINAPI SFileWriteFile(
|
||||||
// Write the data to the file
|
// Write the data to the file
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
nError = SFileAddFile_Write(hf, pvData, dwSize, dwCompression);
|
nError = SFileAddFile_Write(hf, pvData, dwSize, dwCompression);
|
||||||
|
|
||||||
// Deal with errors
|
// Deal with errors
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
SetLastError(nError);
|
SetLastError(nError);
|
||||||
|
|
@ -802,7 +795,7 @@ bool WINAPI SFileFinishFile(HANDLE hFile)
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Check the proper parameters
|
// Check the proper parameters
|
||||||
if(!IsValidFileHandle(hf))
|
if(!IsValidFileHandle(hFile))
|
||||||
nError = ERROR_INVALID_HANDLE;
|
nError = ERROR_INVALID_HANDLE;
|
||||||
if(hf->bIsWriteHandle == false)
|
if(hf->bIsWriteHandle == false)
|
||||||
nError = ERROR_INVALID_HANDLE;
|
nError = ERROR_INVALID_HANDLE;
|
||||||
|
|
@ -810,7 +803,7 @@ bool WINAPI SFileFinishFile(HANDLE hFile)
|
||||||
// Finish the file
|
// Finish the file
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
nError = SFileAddFile_Finish(hf);
|
nError = SFileAddFile_Finish(hf);
|
||||||
|
|
||||||
// Deal with errors
|
// Deal with errors
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
SetLastError(nError);
|
SetLastError(nError);
|
||||||
|
|
@ -818,15 +811,15 @@ bool WINAPI SFileFinishFile(HANDLE hFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Adds a file to the archive
|
// Adds a file to the archive
|
||||||
|
|
||||||
bool WINAPI SFileAddFileEx(
|
bool WINAPI SFileAddFileEx(
|
||||||
HANDLE hMpq,
|
HANDLE hMpq,
|
||||||
const TCHAR * szFileName,
|
const TCHAR * szFileName,
|
||||||
const char * szArchivedName,
|
const char * szArchivedName,
|
||||||
DWORD dwFlags,
|
DWORD dwFlags,
|
||||||
DWORD dwCompression, // Compression of the first sector
|
DWORD dwCompression, // Compression of the first sector
|
||||||
DWORD dwCompressionNext) // Compression of next sectors
|
DWORD dwCompressionNext) // Compression of next sectors
|
||||||
{
|
{
|
||||||
ULONGLONG FileSize = 0;
|
ULONGLONG FileSize = 0;
|
||||||
ULONGLONG FileTime = 0;
|
ULONGLONG FileTime = 0;
|
||||||
|
|
@ -842,13 +835,13 @@ bool WINAPI SFileAddFileEx(
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Check parameters
|
// Check parameters
|
||||||
if(szFileName == NULL || *szFileName == 0)
|
if(hMpq == NULL || szFileName == NULL || *szFileName == 0)
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
// Open added file
|
// Open added file
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
|
pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
|
||||||
if(pStream == NULL)
|
if(pStream == NULL)
|
||||||
nError = GetLastError();
|
nError = GetLastError();
|
||||||
}
|
}
|
||||||
|
|
@ -858,7 +851,7 @@ bool WINAPI SFileAddFileEx(
|
||||||
{
|
{
|
||||||
FileStream_GetTime(pStream, &FileTime);
|
FileStream_GetTime(pStream, &FileTime);
|
||||||
FileStream_GetSize(pStream, &FileSize);
|
FileStream_GetSize(pStream, &FileSize);
|
||||||
|
|
||||||
// Files bigger than 4GB cannot be added to MPQ
|
// Files bigger than 4GB cannot be added to MPQ
|
||||||
if(FileSize >> 32)
|
if(FileSize >> 32)
|
||||||
nError = ERROR_DISK_FULL;
|
nError = ERROR_DISK_FULL;
|
||||||
|
|
@ -880,15 +873,18 @@ bool WINAPI SFileAddFileEx(
|
||||||
// we will copy the compression for the first sector
|
// we will copy the compression for the first sector
|
||||||
if(dwCompressionNext == MPQ_COMPRESSION_NEXT_SAME)
|
if(dwCompressionNext == MPQ_COMPRESSION_NEXT_SAME)
|
||||||
dwCompressionNext = dwCompression;
|
dwCompressionNext = dwCompression;
|
||||||
|
|
||||||
// If the caller wants ADPCM compression, we make sure
|
// If the caller wants ADPCM compression, we make sure
|
||||||
// that the first sector is not compressed with lossy compression
|
// that the first sector is not compressed with lossy compression
|
||||||
if(dwCompressionNext & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO))
|
if(dwCompressionNext & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO))
|
||||||
{
|
{
|
||||||
// The first compression must not be WAVE
|
// The compression of the first file sector must not be ADPCM
|
||||||
|
// in order not to corrupt the headers
|
||||||
if(dwCompression & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO))
|
if(dwCompression & (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO))
|
||||||
dwCompression = MPQ_COMPRESSION_PKWARE;
|
dwCompression = MPQ_COMPRESSION_PKWARE;
|
||||||
|
|
||||||
|
// Remove both flag mono and stereo flags.
|
||||||
|
// They will be re-added according to WAVE type
|
||||||
dwCompressionNext &= ~(MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO);
|
dwCompressionNext &= ~(MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_ADPCM_STEREO);
|
||||||
bIsAdpcmCompression = true;
|
bIsAdpcmCompression = true;
|
||||||
}
|
}
|
||||||
|
|
@ -916,15 +912,19 @@ bool WINAPI SFileAddFileEx(
|
||||||
// If the file being added is a WAVE file, we check number of channels
|
// If the file being added is a WAVE file, we check number of channels
|
||||||
if(bIsFirstSector && bIsAdpcmCompression)
|
if(bIsFirstSector && bIsAdpcmCompression)
|
||||||
{
|
{
|
||||||
// The file must really be a wave file, otherwise it's data corruption
|
// The file must really be a WAVE file with at least 16 bits per sample,
|
||||||
if(!IsWaveFile(pbFileData, dwBytesToRead, &dwChannels))
|
// otherwise the ADPCM compression will corrupt it
|
||||||
|
if(IsWaveFile_16BitsPerAdpcmSample(pbFileData, dwBytesToRead, &dwChannels))
|
||||||
{
|
{
|
||||||
nError = ERROR_BAD_FORMAT;
|
// Setup the compression of next sectors according to number of channels
|
||||||
break;
|
dwCompressionNext |= (dwChannels == 1) ? MPQ_COMPRESSION_ADPCM_MONO : MPQ_COMPRESSION_ADPCM_STEREO;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Setup the compression of next sectors to a lossless compression
|
||||||
|
dwCompressionNext = (dwCompression & MPQ_LOSSY_COMPRESSION_MASK) ? MPQ_COMPRESSION_PKWARE : dwCompression;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the compression according to number of channels
|
|
||||||
dwCompressionNext |= (dwChannels == 1) ? MPQ_COMPRESSION_ADPCM_MONO : MPQ_COMPRESSION_ADPCM_STEREO;
|
|
||||||
bIsFirstSector = false;
|
bIsFirstSector = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -956,7 +956,7 @@ bool WINAPI SFileAddFileEx(
|
||||||
SetLastError(nError);
|
SetLastError(nError);
|
||||||
return (nError == ERROR_SUCCESS);
|
return (nError == ERROR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a data file into the archive
|
// Adds a data file into the archive
|
||||||
bool WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags)
|
bool WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags)
|
||||||
{
|
{
|
||||||
|
|
@ -983,25 +983,25 @@ bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szA
|
||||||
// Starcraft files are packed as Mono (0x41) on medium quality.
|
// Starcraft files are packed as Mono (0x41) on medium quality.
|
||||||
// Because this compression is not used anymore, our compression functions
|
// Because this compression is not used anymore, our compression functions
|
||||||
// will default to WaveCompressionLevel = 4 when using ADPCM compression
|
// will default to WaveCompressionLevel = 4 when using ADPCM compression
|
||||||
//
|
//
|
||||||
|
|
||||||
// Convert quality to data compression
|
// Convert quality to data compression
|
||||||
switch(dwQuality)
|
switch(dwQuality)
|
||||||
{
|
{
|
||||||
case MPQ_WAVE_QUALITY_HIGH:
|
case MPQ_WAVE_QUALITY_HIGH:
|
||||||
// WaveCompressionLevel = -1;
|
// WaveCompressionLevel = -1;
|
||||||
dwCompression = MPQ_COMPRESSION_PKWARE;
|
dwCompression = MPQ_COMPRESSION_PKWARE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPQ_WAVE_QUALITY_MEDIUM:
|
case MPQ_WAVE_QUALITY_MEDIUM:
|
||||||
// WaveCompressionLevel = 4;
|
// WaveCompressionLevel = 4;
|
||||||
dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN;
|
dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPQ_WAVE_QUALITY_LOW:
|
case MPQ_WAVE_QUALITY_LOW:
|
||||||
// WaveCompressionLevel = 2;
|
// WaveCompressionLevel = 2;
|
||||||
dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN;
|
dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SFileAddFileEx(hMpq,
|
return SFileAddFileEx(hMpq,
|
||||||
|
|
@ -1017,7 +1017,7 @@ bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szA
|
||||||
//
|
//
|
||||||
// This function removes a file from the archive. The file content
|
// This function removes a file from the archive. The file content
|
||||||
// remains there, only the entries in the hash table and in the block
|
// remains there, only the entries in the hash table and in the block
|
||||||
// table are updated.
|
// table are updated.
|
||||||
|
|
||||||
bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope)
|
bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope)
|
||||||
{
|
{
|
||||||
|
|
@ -1032,7 +1032,7 @@ bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearch
|
||||||
// Check the parameters
|
// Check the parameters
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
nError = ERROR_INVALID_HANDLE;
|
nError = ERROR_INVALID_HANDLE;
|
||||||
if(szFileName == NULL || *szFileName == 0)
|
if(szFileName == NULL || *szFileName == 0)
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
|
|
@ -1075,8 +1075,12 @@ bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearch
|
||||||
// After we are done with MPQ changes, we need to re-create them anyway
|
// After we are done with MPQ changes, we need to re-create them anyway
|
||||||
InvalidateInternalFiles(ha);
|
InvalidateInternalFiles(ha);
|
||||||
|
|
||||||
// Mark the file entry as free
|
// Delete the file entry in the file table and defragment the file table
|
||||||
nError = FreeFileEntry(ha, pFileEntry);
|
DeleteFileEntry(ha, pFileEntry);
|
||||||
|
|
||||||
|
// We also need to rebuild the HET table, if present
|
||||||
|
if(ha->pHetTable != NULL)
|
||||||
|
nError = RebuildHetTable(ha);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve error and exit
|
// Resolve error and exit
|
||||||
|
|
@ -1097,7 +1101,7 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s
|
||||||
// Test the valid parameters
|
// Test the valid parameters
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
nError = ERROR_INVALID_HANDLE;
|
nError = ERROR_INVALID_HANDLE;
|
||||||
if(szFileName == NULL || *szFileName == 0 || szNewFileName == NULL || *szNewFileName == 0)
|
if(szFileName == NULL || *szFileName == 0 || szNewFileName == NULL || *szNewFileName == 0)
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
|
|
@ -1127,7 +1131,7 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s
|
||||||
if(pFileEntry == NULL)
|
if(pFileEntry == NULL)
|
||||||
nError = ERROR_FILE_NOT_FOUND;
|
nError = ERROR_FILE_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also try to find file entry for the new file.
|
// Also try to find file entry for the new file.
|
||||||
// This verifies if we are not overwriting an existing file
|
// This verifies if we are not overwriting an existing file
|
||||||
// (whose name we perhaps don't know)
|
// (whose name we perhaps don't know)
|
||||||
|
|
@ -1144,6 +1148,10 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s
|
||||||
nError = RenameFileEntry(ha, pFileEntry, szNewFileName);
|
nError = RenameFileEntry(ha, pFileEntry, szNewFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now we need to recreate the HET table, if we have one
|
||||||
|
if(nError == ERROR_SUCCESS && ha->pHetTable != NULL)
|
||||||
|
nError = RebuildHetTable(ha);
|
||||||
|
|
||||||
// Now we copy the existing file entry to the new one
|
// Now we copy the existing file entry to the new one
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
|
|
@ -1151,14 +1159,12 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s
|
||||||
// with the new decryption key
|
// with the new decryption key
|
||||||
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
|
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
|
||||||
{
|
{
|
||||||
hf = CreateMpqFile(ha);
|
hf = CreateFileHandle(ha, pFileEntry);
|
||||||
if(hf != NULL)
|
if(hf != NULL)
|
||||||
{
|
{
|
||||||
// Recrypt the file data in the MPQ
|
// Recrypt the file data in the MPQ
|
||||||
hf->pFileEntry = pFileEntry;
|
|
||||||
hf->dwDataSize = pFileEntry->dwFileSize;
|
|
||||||
nError = RecryptFileData(ha, hf, szFileName, szNewFileName);
|
nError = RecryptFileData(ha, hf, szFileName, szNewFileName);
|
||||||
|
|
||||||
// Update the MD5
|
// Update the MD5
|
||||||
if(ha->pHeader->dwRawChunkSize != 0)
|
if(ha->pHeader->dwRawChunkSize != 0)
|
||||||
{
|
{
|
||||||
|
|
@ -1168,8 +1174,8 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s
|
||||||
pFileEntry->dwCmpSize,
|
pFileEntry->dwCmpSize,
|
||||||
ha->pHeader->dwRawChunkSize);
|
ha->pHeader->dwRawChunkSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeMPQFile(hf);
|
FreeFileHandle(hf);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -1178,9 +1184,8 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// Note: MPQ_FLAG_CHANGED is set by RenameFileEntry
|
// Note: MPQ_FLAG_CHANGED is set by RenameFileEntry
|
||||||
//
|
// assert((ha->dwFlags & MPQ_FLAG_CHANGED) != 0);
|
||||||
|
|
||||||
// Resolve error and return
|
// Resolve error and return
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
|
|
@ -1215,7 +1220,7 @@ bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale)
|
||||||
TMPQFile * hf = (TMPQFile *)hFile;
|
TMPQFile * hf = (TMPQFile *)hFile;
|
||||||
|
|
||||||
// Invalid handle => do nothing
|
// Invalid handle => do nothing
|
||||||
if(!IsValidFileHandle(hf))
|
if(!IsValidFileHandle(hFile))
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_INVALID_HANDLE);
|
SetLastError(ERROR_INVALID_HANDLE);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1278,9 +1283,17 @@ bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale)
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Sets add file callback
|
// Sets add file callback
|
||||||
|
|
||||||
bool WINAPI SFileSetAddFileCallback(HANDLE /* hMpq */, SFILE_ADDFILE_CALLBACK aAddFileCB, void * pvData)
|
bool WINAPI SFileSetAddFileCallback(HANDLE hMpq, SFILE_ADDFILE_CALLBACK AddFileCB, void * pvUserData)
|
||||||
{
|
{
|
||||||
pvUserData = pvData;
|
TMPQArchive * ha = (TMPQArchive *) hMpq;
|
||||||
AddFileCB = aAddFileCB;
|
|
||||||
|
if(!IsValidMpqHandle(hMpq))
|
||||||
|
{
|
||||||
|
SetLastError(ERROR_INVALID_HANDLE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha->pvAddFileUserData = pvUserData;
|
||||||
|
ha->pfnAddFileCB = AddFileCB;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,346 +24,450 @@ typedef struct _MPQ_ATTRIBUTES_HEADER
|
||||||
// Followed by an array of file times
|
// Followed by an array of file times
|
||||||
// Followed by an array of MD5
|
// Followed by an array of MD5
|
||||||
// Followed by an array of patch bits
|
// Followed by an array of patch bits
|
||||||
|
|
||||||
|
// Note: The MD5 in (attributes), if present, is a hash of the entire file.
|
||||||
|
// In case the file is an incremental patch, it contains MD5 of the file
|
||||||
|
// after being patched.
|
||||||
|
|
||||||
} MPQ_ATTRIBUTES_HEADER, *PMPQ_ATTRIBUTES_HEADER;
|
} MPQ_ATTRIBUTES_HEADER, *PMPQ_ATTRIBUTES_HEADER;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Local functions
|
||||||
|
|
||||||
|
static DWORD GetSizeOfAttributesFile(DWORD dwAttrFlags, DWORD dwFileTableSize)
|
||||||
|
{
|
||||||
|
DWORD cbAttrFile = sizeof(MPQ_ATTRIBUTES_HEADER);
|
||||||
|
|
||||||
|
// Calculate size of the (attributes) file
|
||||||
|
if(dwAttrFlags & MPQ_ATTRIBUTE_CRC32)
|
||||||
|
cbAttrFile += dwFileTableSize * sizeof(DWORD);
|
||||||
|
if(dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
|
||||||
|
cbAttrFile += dwFileTableSize * sizeof(ULONGLONG);
|
||||||
|
if(dwAttrFlags & MPQ_ATTRIBUTE_MD5)
|
||||||
|
cbAttrFile += dwFileTableSize * MD5_DIGEST_SIZE;
|
||||||
|
|
||||||
|
// The bit array has been created without the last bit belonging to (attributes)
|
||||||
|
// When the number of files is a multiplier of 8 plus one, then the size of (attributes)
|
||||||
|
// if 1 byte less than expected.
|
||||||
|
// Example: wow-update-13164.MPQ: BlockTableSize = 0x62E1, but there's only 0xC5C bytes
|
||||||
|
if(dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
|
||||||
|
cbAttrFile += (dwFileTableSize + 6) / 8;
|
||||||
|
|
||||||
|
return cbAttrFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWORD CheckSizeOfAttributesFile(DWORD cbAttrFile, DWORD dwAttrFlags, DWORD dwFileTableSize)
|
||||||
|
{
|
||||||
|
DWORD cbHeaderSize = sizeof(MPQ_ATTRIBUTES_HEADER);
|
||||||
|
DWORD cbChecksumSize1 = 0;
|
||||||
|
DWORD cbChecksumSize2 = 0;
|
||||||
|
DWORD cbFileTimeSize1 = 0;
|
||||||
|
DWORD cbFileTimeSize2 = 0;
|
||||||
|
DWORD cbFileHashSize1 = 0;
|
||||||
|
DWORD cbFileHashSize2 = 0;
|
||||||
|
DWORD cbPatchBitSize1 = 0;
|
||||||
|
DWORD cbPatchBitSize2 = 0;
|
||||||
|
DWORD cbPatchBitSize3 = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Various variants with the patch bit
|
||||||
|
//
|
||||||
|
// interface.MPQ.part from WoW build 10958 has
|
||||||
|
// the MPQ_ATTRIBUTE_PATCH_BIT set, but there's an array of DWORDs instead.
|
||||||
|
// The array is filled with zeros, so we don't know what it should contain
|
||||||
|
//
|
||||||
|
// Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing
|
||||||
|
//
|
||||||
|
// Elimination Tournament 2.w3x's (attributes) have one entry less
|
||||||
|
//
|
||||||
|
// There may be two variants: Either the (attributes) file has full
|
||||||
|
// number of entries, or has one entry less
|
||||||
|
//
|
||||||
|
|
||||||
|
// Get the expected size of CRC32 array
|
||||||
|
if(dwAttrFlags & MPQ_ATTRIBUTE_CRC32)
|
||||||
|
{
|
||||||
|
cbChecksumSize1 += dwFileTableSize * sizeof(DWORD);
|
||||||
|
cbChecksumSize2 += cbChecksumSize1 - sizeof(DWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the expected size of FILETIME array
|
||||||
|
if(dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
|
||||||
|
{
|
||||||
|
cbFileTimeSize1 += dwFileTableSize * sizeof(ULONGLONG);
|
||||||
|
cbFileTimeSize2 += cbFileTimeSize1 - sizeof(ULONGLONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the expected size of MD5 array
|
||||||
|
if(dwAttrFlags & MPQ_ATTRIBUTE_MD5)
|
||||||
|
{
|
||||||
|
cbFileHashSize1 += dwFileTableSize * MD5_DIGEST_SIZE;
|
||||||
|
cbFileHashSize2 += cbFileHashSize1 - MD5_DIGEST_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the expected size of patch bit array
|
||||||
|
if(dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
|
||||||
|
{
|
||||||
|
cbPatchBitSize1 =
|
||||||
|
cbPatchBitSize2 = ((dwFileTableSize + 6) / 8);
|
||||||
|
cbPatchBitSize3 = dwFileTableSize * sizeof(DWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the (attributes) file entry count is equal to our file table size
|
||||||
|
if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1 + cbPatchBitSize1))
|
||||||
|
return dwFileTableSize;
|
||||||
|
|
||||||
|
// Check if the (attributes) file entry count is equal to our file table size minus one
|
||||||
|
if(cbAttrFile == (cbHeaderSize + cbChecksumSize2 + cbFileTimeSize2 + cbFileHashSize2 + cbPatchBitSize2))
|
||||||
|
return dwFileTableSize - 1;
|
||||||
|
|
||||||
|
// Zenith.SC2MAP has the MPQ_ATTRIBUTE_PATCH_BIT set, but the bit array is missing
|
||||||
|
if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1))
|
||||||
|
return dwFileTableSize;
|
||||||
|
|
||||||
|
// interface.MPQ.part (WoW build 10958) has the MPQ_ATTRIBUTE_PATCH_BIT set
|
||||||
|
// but there's an array of DWORDs (filled with zeros) instead of array of bits
|
||||||
|
if(cbAttrFile == (cbHeaderSize + cbChecksumSize1 + cbFileTimeSize1 + cbFileHashSize1 + cbPatchBitSize3))
|
||||||
|
return dwFileTableSize;
|
||||||
|
|
||||||
|
#ifdef __STORMLIB_TEST__
|
||||||
|
// Invalid size of the (attributes) file
|
||||||
|
// Note that many MPQs, especially Warcraft III maps have the size of (attributes) invalid.
|
||||||
|
// We only perform this check if this is the STORMLIB testprogram itself
|
||||||
|
// assert(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int LoadAttributesFile(TMPQArchive * ha, LPBYTE pbAttrFile, DWORD cbAttrFile)
|
||||||
|
{
|
||||||
|
LPBYTE pbAttrFileEnd = pbAttrFile + cbAttrFile;
|
||||||
|
LPBYTE pbAttrPtr = pbAttrFile;
|
||||||
|
DWORD dwAttributesEntries = 0;
|
||||||
|
DWORD i;
|
||||||
|
|
||||||
|
// Load and verify the header
|
||||||
|
if((pbAttrPtr + sizeof(MPQ_ATTRIBUTES_HEADER)) <= pbAttrFileEnd)
|
||||||
|
{
|
||||||
|
PMPQ_ATTRIBUTES_HEADER pAttrHeader = (PMPQ_ATTRIBUTES_HEADER)pbAttrPtr;
|
||||||
|
|
||||||
|
// Verify the header version
|
||||||
|
BSWAP_ARRAY32_UNSIGNED(pAttrHeader, sizeof(MPQ_ATTRIBUTES_HEADER));
|
||||||
|
if(pAttrHeader->dwVersion != MPQ_ATTRIBUTES_V1)
|
||||||
|
return ERROR_BAD_FORMAT;
|
||||||
|
|
||||||
|
// Verify the flags
|
||||||
|
if(pAttrHeader->dwFlags & ~MPQ_ATTRIBUTE_ALL)
|
||||||
|
return ERROR_BAD_FORMAT;
|
||||||
|
|
||||||
|
// Verify whether file size of (attributes) is expected
|
||||||
|
dwAttributesEntries = CheckSizeOfAttributesFile(cbAttrFile, pAttrHeader->dwFlags, ha->pHeader->dwBlockTableSize);
|
||||||
|
if(dwAttributesEntries == 0)
|
||||||
|
return ERROR_BAD_FORMAT;
|
||||||
|
|
||||||
|
ha->dwAttrFlags = pAttrHeader->dwFlags;
|
||||||
|
pbAttrPtr = (LPBYTE)(pAttrHeader + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the CRC32 (if present)
|
||||||
|
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32)
|
||||||
|
{
|
||||||
|
LPDWORD ArrayCRC32 = (LPDWORD)pbAttrPtr;
|
||||||
|
DWORD cbArraySize = dwAttributesEntries * sizeof(DWORD);
|
||||||
|
|
||||||
|
// Verify if there's enough data
|
||||||
|
if((pbAttrPtr + cbArraySize) > pbAttrFileEnd)
|
||||||
|
return ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
|
BSWAP_ARRAY32_UNSIGNED(ArrayCRC32, cbCRC32Size);
|
||||||
|
for(i = 0; i < dwAttributesEntries; i++)
|
||||||
|
ha->pFileTable[i].dwCrc32 = ArrayCRC32[i];
|
||||||
|
pbAttrPtr += cbArraySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the FILETIME (if present)
|
||||||
|
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
|
||||||
|
{
|
||||||
|
ULONGLONG * ArrayFileTime = (ULONGLONG *)pbAttrPtr;
|
||||||
|
DWORD cbArraySize = dwAttributesEntries * sizeof(ULONGLONG);
|
||||||
|
|
||||||
|
// Verify if there's enough data
|
||||||
|
if((pbAttrPtr + cbArraySize) > pbAttrFileEnd)
|
||||||
|
return ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
|
BSWAP_ARRAY64_UNSIGNED(ArrayFileTime, cbFileTimeSize);
|
||||||
|
for(i = 0; i < dwAttributesEntries; i++)
|
||||||
|
ha->pFileTable[i].FileTime = ArrayFileTime[i];
|
||||||
|
pbAttrPtr += cbArraySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the MD5 (if present)
|
||||||
|
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5)
|
||||||
|
{
|
||||||
|
LPBYTE ArrayMd5 = pbAttrPtr;
|
||||||
|
DWORD cbArraySize = dwAttributesEntries * MD5_DIGEST_SIZE;
|
||||||
|
|
||||||
|
// Verify if there's enough data
|
||||||
|
if((pbAttrPtr + cbArraySize) > pbAttrFileEnd)
|
||||||
|
return ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
|
for(i = 0; i < dwAttributesEntries; i++)
|
||||||
|
{
|
||||||
|
memcpy(ha->pFileTable[i].md5, ArrayMd5, MD5_DIGEST_SIZE);
|
||||||
|
ArrayMd5 += MD5_DIGEST_SIZE;
|
||||||
|
}
|
||||||
|
pbAttrPtr += cbArraySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the patch bit for each file (if present)
|
||||||
|
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
|
||||||
|
{
|
||||||
|
LPBYTE pbBitArray = pbAttrPtr;
|
||||||
|
DWORD cbArraySize = (dwAttributesEntries + 7) / 8;
|
||||||
|
DWORD dwByteIndex = 0;
|
||||||
|
DWORD dwBitMask = 0x80;
|
||||||
|
|
||||||
|
// Verify if there's enough data
|
||||||
|
if((pbAttrPtr + cbArraySize) == pbAttrFileEnd)
|
||||||
|
{
|
||||||
|
for(i = 0; i < dwAttributesEntries; i++)
|
||||||
|
{
|
||||||
|
ha->pFileTable[i].dwFlags |= (pbBitArray[dwByteIndex] & dwBitMask) ? MPQ_FILE_PATCH_FILE : 0;
|
||||||
|
dwByteIndex += (dwBitMask & 0x01);
|
||||||
|
dwBitMask = (dwBitMask << 0x07) | (dwBitMask >> 0x01);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LPBYTE CreateAttributesFile(TMPQArchive * ha, DWORD * pcbAttrFile)
|
||||||
|
{
|
||||||
|
PMPQ_ATTRIBUTES_HEADER pAttrHeader;
|
||||||
|
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||||
|
TFileEntry * pFileEntry;
|
||||||
|
LPBYTE pbAttrFile;
|
||||||
|
LPBYTE pbAttrPtr;
|
||||||
|
size_t cbAttrFile;
|
||||||
|
DWORD dwFinalEntries = ha->dwFileTableSize + ha->dwReservedFiles;
|
||||||
|
|
||||||
|
// Check if we need patch bits in the (attributes) file
|
||||||
|
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||||
|
{
|
||||||
|
if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
|
||||||
|
{
|
||||||
|
ha->dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate the buffer for holding the entire (attributes)
|
||||||
|
// Allodate 1 byte more (See GetSizeOfAttributesFile for more info)
|
||||||
|
cbAttrFile = GetSizeOfAttributesFile(ha->dwAttrFlags, dwFinalEntries);
|
||||||
|
pbAttrFile = pbAttrPtr = STORM_ALLOC(BYTE, cbAttrFile + 1);
|
||||||
|
if(pbAttrFile != NULL)
|
||||||
|
{
|
||||||
|
// Make sure it's all zeroed
|
||||||
|
memset(pbAttrFile, 0, cbAttrFile + 1);
|
||||||
|
|
||||||
|
// Write the header of the (attributes) file
|
||||||
|
pAttrHeader = (PMPQ_ATTRIBUTES_HEADER)pbAttrPtr;
|
||||||
|
pAttrHeader->dwVersion = BSWAP_INT32_UNSIGNED(100);
|
||||||
|
pAttrHeader->dwFlags = BSWAP_INT32_UNSIGNED((ha->dwAttrFlags & MPQ_ATTRIBUTE_ALL));
|
||||||
|
pbAttrPtr = (LPBYTE)(pAttrHeader + 1);
|
||||||
|
|
||||||
|
// Write the array of CRC32, if present
|
||||||
|
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32)
|
||||||
|
{
|
||||||
|
LPDWORD pArrayCRC32 = (LPDWORD)pbAttrPtr;
|
||||||
|
|
||||||
|
// Copy from file table
|
||||||
|
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||||
|
*pArrayCRC32++ = BSWAP_INT32_UNSIGNED(pFileEntry->dwCrc32);
|
||||||
|
|
||||||
|
// Skip the reserved entries
|
||||||
|
pbAttrPtr = (LPBYTE)(pArrayCRC32 + ha->dwReservedFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the array of file time
|
||||||
|
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
|
||||||
|
{
|
||||||
|
ULONGLONG * pArrayFileTime = (ULONGLONG *)pbAttrPtr;
|
||||||
|
|
||||||
|
// Copy from file table
|
||||||
|
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||||
|
*pArrayFileTime++ = BSWAP_INT64_UNSIGNED(pFileEntry->FileTime);
|
||||||
|
|
||||||
|
// Skip the reserved entries
|
||||||
|
pbAttrPtr = (LPBYTE)(pArrayFileTime + ha->dwReservedFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the array of MD5s
|
||||||
|
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5)
|
||||||
|
{
|
||||||
|
LPBYTE pbArrayMD5 = pbAttrPtr;
|
||||||
|
|
||||||
|
// Copy from file table
|
||||||
|
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||||
|
{
|
||||||
|
memcpy(pbArrayMD5, pFileEntry->md5, MD5_DIGEST_SIZE);
|
||||||
|
pbArrayMD5 += MD5_DIGEST_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip the reserved items
|
||||||
|
pbAttrPtr = pbArrayMD5 + (ha->dwReservedFiles * MD5_DIGEST_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the array of patch bits
|
||||||
|
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
|
||||||
|
{
|
||||||
|
LPBYTE pbBitArray = pbAttrPtr;
|
||||||
|
DWORD dwByteIndex = 0;
|
||||||
|
BYTE dwBitMask = 0x80;
|
||||||
|
|
||||||
|
// Copy from file table
|
||||||
|
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||||
|
{
|
||||||
|
// Set the bit, if needed
|
||||||
|
if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
|
||||||
|
pbBitArray[dwByteIndex] |= dwBitMask;
|
||||||
|
|
||||||
|
// Update bit index and bit mask
|
||||||
|
dwByteIndex += (dwBitMask & 0x01);
|
||||||
|
dwBitMask = (dwBitMask << 0x07) | (dwBitMask >> 0x01);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Having incremented the bit array just by the number of items in the file table
|
||||||
|
// will create the bit array one byte less of the number of files is a multiplier of 8).
|
||||||
|
// Blizzard MPQs have the same feature.
|
||||||
|
|
||||||
|
// Move past the bit array
|
||||||
|
pbAttrPtr = (pbBitArray + dwByteIndex) + ((dwBitMask & 0x7F) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we expect that current position matches the estimated size
|
||||||
|
// Note that if there is 1 extra bit above the byte size,
|
||||||
|
// the table is actually 1 byte shorted in Blizzard MPQs. See GetSizeOfAttributesFile
|
||||||
|
assert((size_t)(pbAttrPtr - pbAttrFile) == cbAttrFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give away the attributes file
|
||||||
|
if(pcbAttrFile != NULL)
|
||||||
|
*pcbAttrFile = (DWORD)cbAttrFile;
|
||||||
|
return pbAttrFile;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Public functions (internal use by StormLib)
|
// Public functions (internal use by StormLib)
|
||||||
|
|
||||||
int SAttrLoadAttributes(TMPQArchive * ha)
|
int SAttrLoadAttributes(TMPQArchive * ha)
|
||||||
{
|
{
|
||||||
MPQ_ATTRIBUTES_HEADER AttrHeader;
|
|
||||||
HANDLE hFile = NULL;
|
HANDLE hFile = NULL;
|
||||||
DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize;
|
LPBYTE pbAttrFile;
|
||||||
DWORD dwArraySize;
|
|
||||||
DWORD dwBytesRead;
|
DWORD dwBytesRead;
|
||||||
DWORD i;
|
DWORD cbAttrFile = 0;
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
// File table must be initialized
|
// File table must be initialized
|
||||||
assert(ha->pFileTable != NULL);
|
assert(ha->pFileTable != NULL);
|
||||||
|
|
||||||
|
// Don't load the attributes file from malformed Warcraft III maps
|
||||||
|
if(ha->dwFlags & MPQ_FLAG_MALFORMED)
|
||||||
|
return ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
// Attempt to open the "(attributes)" file.
|
// Attempt to open the "(attributes)" file.
|
||||||
// If it's not there, then the archive doesn't support attributes
|
|
||||||
if(SFileOpenFileEx((HANDLE)ha, ATTRIBUTES_NAME, SFILE_OPEN_ANY_LOCALE, &hFile))
|
if(SFileOpenFileEx((HANDLE)ha, ATTRIBUTES_NAME, SFILE_OPEN_ANY_LOCALE, &hFile))
|
||||||
{
|
{
|
||||||
// Load the content of the attributes file
|
// Retrieve and check size of the (attributes) file
|
||||||
SFileReadFile(hFile, &AttrHeader, sizeof(MPQ_ATTRIBUTES_HEADER), &dwBytesRead, NULL);
|
cbAttrFile = SFileGetFileSize(hFile, NULL);
|
||||||
if(dwBytesRead != sizeof(MPQ_ATTRIBUTES_HEADER))
|
|
||||||
nError = ERROR_FILE_CORRUPT;
|
|
||||||
|
|
||||||
// Verify the header of the (attributes) file
|
// Size of the (attributes) might be 1 byte less than expected
|
||||||
if(nError == ERROR_SUCCESS)
|
// See GetSizeOfAttributesFile for more info
|
||||||
|
pbAttrFile = STORM_ALLOC(BYTE, cbAttrFile + 1);
|
||||||
|
if(pbAttrFile != NULL)
|
||||||
{
|
{
|
||||||
AttrHeader.dwVersion = BSWAP_INT32_UNSIGNED(AttrHeader.dwVersion);
|
// Set the last byte to 0 in case the size should be 1 byte greater
|
||||||
AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED(AttrHeader.dwFlags);
|
pbAttrFile[cbAttrFile] = 0;
|
||||||
ha->dwAttrFlags = AttrHeader.dwFlags;
|
|
||||||
if(dwBytesRead != sizeof(MPQ_ATTRIBUTES_HEADER))
|
// Load the entire file to memory
|
||||||
nError = ERROR_FILE_CORRUPT;
|
SFileReadFile(hFile, pbAttrFile, cbAttrFile, &dwBytesRead, NULL);
|
||||||
|
if(dwBytesRead == cbAttrFile)
|
||||||
|
nError = LoadAttributesFile(ha, pbAttrFile, cbAttrFile);
|
||||||
|
|
||||||
|
// Free the buffer
|
||||||
|
STORM_FREE(pbAttrFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify format of the attributes
|
// Close the attributes file
|
||||||
if(nError == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
if(AttrHeader.dwVersion > MPQ_ATTRIBUTES_V1)
|
|
||||||
nError = ERROR_BAD_FORMAT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the CRC32 (if any)
|
|
||||||
if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_CRC32))
|
|
||||||
{
|
|
||||||
LPDWORD pArrayCRC32 = STORM_ALLOC(DWORD, dwBlockTableSize);
|
|
||||||
|
|
||||||
if(pArrayCRC32 != NULL)
|
|
||||||
{
|
|
||||||
dwArraySize = dwBlockTableSize * sizeof(DWORD);
|
|
||||||
SFileReadFile(hFile, pArrayCRC32, dwArraySize, &dwBytesRead, NULL);
|
|
||||||
if(dwBytesRead == dwArraySize)
|
|
||||||
{
|
|
||||||
for(i = 0; i < dwBlockTableSize; i++)
|
|
||||||
ha->pFileTable[i].dwCrc32 = BSWAP_INT32_UNSIGNED(pArrayCRC32[i]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nError = ERROR_FILE_CORRUPT;
|
|
||||||
|
|
||||||
STORM_FREE(pArrayCRC32);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the array of file times
|
|
||||||
if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_FILETIME))
|
|
||||||
{
|
|
||||||
ULONGLONG * pArrayFileTime = STORM_ALLOC(ULONGLONG, dwBlockTableSize);
|
|
||||||
|
|
||||||
if(pArrayFileTime != NULL)
|
|
||||||
{
|
|
||||||
dwArraySize = dwBlockTableSize * sizeof(ULONGLONG);
|
|
||||||
SFileReadFile(hFile, pArrayFileTime, dwArraySize, &dwBytesRead, NULL);
|
|
||||||
if(dwBytesRead == dwArraySize)
|
|
||||||
{
|
|
||||||
for(i = 0; i < dwBlockTableSize; i++)
|
|
||||||
ha->pFileTable[i].FileTime = BSWAP_INT64_UNSIGNED(pArrayFileTime[i]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nError = ERROR_FILE_CORRUPT;
|
|
||||||
|
|
||||||
STORM_FREE(pArrayFileTime);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the MD5 (if any)
|
|
||||||
// Note: MD5 array can be incomplete, if it's the last array in the (attributes)
|
|
||||||
if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_MD5))
|
|
||||||
{
|
|
||||||
unsigned char * pArrayMD5 = STORM_ALLOC(unsigned char, (dwBlockTableSize * MD5_DIGEST_SIZE));
|
|
||||||
unsigned char * md5;
|
|
||||||
|
|
||||||
if(pArrayMD5 != NULL)
|
|
||||||
{
|
|
||||||
dwArraySize = dwBlockTableSize * MD5_DIGEST_SIZE;
|
|
||||||
SFileReadFile(hFile, pArrayMD5, dwArraySize, &dwBytesRead, NULL);
|
|
||||||
if(dwBytesRead == dwArraySize)
|
|
||||||
{
|
|
||||||
md5 = pArrayMD5;
|
|
||||||
for(i = 0; i < dwBlockTableSize; i++)
|
|
||||||
{
|
|
||||||
memcpy(ha->pFileTable[i].md5, md5, MD5_DIGEST_SIZE);
|
|
||||||
md5 += MD5_DIGEST_SIZE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nError = ERROR_FILE_CORRUPT;
|
|
||||||
|
|
||||||
STORM_FREE(pArrayMD5);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the patch bit for each file
|
|
||||||
if(nError == ERROR_SUCCESS && (AttrHeader.dwFlags & MPQ_ATTRIBUTE_PATCH_BIT))
|
|
||||||
{
|
|
||||||
LPBYTE pbBitArray;
|
|
||||||
DWORD dwByteSize = ((dwBlockTableSize - 1) / 8) + 1;
|
|
||||||
|
|
||||||
pbBitArray = STORM_ALLOC(BYTE, dwByteSize);
|
|
||||||
if(pbBitArray != NULL)
|
|
||||||
{
|
|
||||||
SFileReadFile(hFile, pbBitArray, dwByteSize, &dwBytesRead, NULL);
|
|
||||||
if(dwBytesRead == dwByteSize)
|
|
||||||
{
|
|
||||||
for(i = 0; i < dwBlockTableSize; i++)
|
|
||||||
{
|
|
||||||
DWORD dwByteIndex = i / 8;
|
|
||||||
DWORD dwBitMask = 0x80 >> (i & 7);
|
|
||||||
|
|
||||||
// Is the appropriate bit set?
|
|
||||||
if(pbBitArray[dwByteIndex] & dwBitMask)
|
|
||||||
{
|
|
||||||
// At the moment, we assume that the patch bit is present
|
|
||||||
// in both file table and (attributes)
|
|
||||||
assert((ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE) != 0);
|
|
||||||
ha->pFileTable[i].dwFlags |= MPQ_FILE_PATCH_FILE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nError = ERROR_FILE_CORRUPT;
|
|
||||||
|
|
||||||
STORM_FREE(pbBitArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Note: Version 7.00 of StormLib saved the (attributes) incorrectly.
|
|
||||||
// Sometimes, number of entries in the (attributes) was 1 item less
|
|
||||||
// than block table size.
|
|
||||||
// If we encounter such table, we will zero all three arrays
|
|
||||||
//
|
|
||||||
|
|
||||||
if(nError != ERROR_SUCCESS)
|
|
||||||
ha->dwAttrFlags = 0;
|
|
||||||
|
|
||||||
// Cleanup & exit
|
|
||||||
SFileCloseFile(hFile);
|
SFileCloseFile(hFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nError;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Saves the (attributes) to the MPQ
|
||||||
int SAttrFileSaveToMpq(TMPQArchive * ha)
|
int SAttrFileSaveToMpq(TMPQArchive * ha)
|
||||||
{
|
{
|
||||||
MPQ_ATTRIBUTES_HEADER AttrHeader;
|
|
||||||
TFileEntry * pFileEntry;
|
|
||||||
TMPQFile * hf = NULL;
|
TMPQFile * hf = NULL;
|
||||||
DWORD dwFinalBlockTableSize = ha->dwFileTableSize;
|
LPBYTE pbAttrFile;
|
||||||
DWORD dwFileSize = 0;
|
DWORD cbAttrFile = 0;
|
||||||
DWORD dwToWrite;
|
|
||||||
DWORD i;
|
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Now we have to check if we need patch bits in the (attributes)
|
// Only save the attributes if we should do so
|
||||||
if(nError == ERROR_SUCCESS)
|
if(ha->dwFileFlags2 != 0)
|
||||||
{
|
{
|
||||||
for(i = 0; i < ha->dwFileTableSize; i++)
|
// At this point, we expect to have at least one reserved entry in the file table
|
||||||
|
assert(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_INVALID);
|
||||||
|
assert(ha->dwReservedFiles >= 1);
|
||||||
|
|
||||||
|
// Create the raw data that is to be written to (attributes)
|
||||||
|
// Note: Blizzard MPQs have entries for (listfile) and (attributes),
|
||||||
|
// but they are filled empty
|
||||||
|
pbAttrFile = CreateAttributesFile(ha, &cbAttrFile);
|
||||||
|
if(pbAttrFile != NULL)
|
||||||
{
|
{
|
||||||
if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE)
|
// We expect it to be nonzero size
|
||||||
|
assert(cbAttrFile != 0);
|
||||||
|
|
||||||
|
// Determine the real flags for (attributes)
|
||||||
|
if(ha->dwFileFlags2 == MPQ_FILE_EXISTS)
|
||||||
|
ha->dwFileFlags2 = GetDefaultSpecialFileFlags(cbAttrFile, ha->pHeader->wFormatVersion);
|
||||||
|
|
||||||
|
// Create the attributes file in the MPQ
|
||||||
|
nError = SFileAddFile_Init(ha, ATTRIBUTES_NAME,
|
||||||
|
0,
|
||||||
|
cbAttrFile,
|
||||||
|
LANG_NEUTRAL,
|
||||||
|
ha->dwFileFlags2 | MPQ_FILE_REPLACEEXISTING,
|
||||||
|
&hf);
|
||||||
|
|
||||||
|
// Write the attributes file raw data to it
|
||||||
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
ha->dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT;
|
// Write the content of the attributes file to the MPQ
|
||||||
break;
|
nError = SFileAddFile_Write(hf, pbAttrFile, cbAttrFile, MPQ_COMPRESSION_ZLIB);
|
||||||
}
|
SFileAddFile_Finish(hf);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the (attributes) is not in the file table yet,
|
|
||||||
// we have to increase the final block table size
|
|
||||||
pFileEntry = GetFileEntryExact(ha, ATTRIBUTES_NAME, LANG_NEUTRAL);
|
|
||||||
if(pFileEntry != NULL)
|
|
||||||
{
|
|
||||||
// If "(attributes)" file exists, and it's set to 0, then remove it
|
|
||||||
if(ha->dwAttrFlags == 0)
|
|
||||||
{
|
|
||||||
FreeFileEntry(ha, pFileEntry);
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If we don't want to create file atributes, do nothing
|
|
||||||
if(ha->dwAttrFlags == 0)
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
|
|
||||||
// Check where the file entry is going to be allocated.
|
|
||||||
// If at the end of the file table, we have to increment
|
|
||||||
// the expected size of the (attributes) file.
|
|
||||||
pFileEntry = FindFreeFileEntry(ha);
|
|
||||||
if(pFileEntry == ha->pFileTable + ha->dwFileTableSize)
|
|
||||||
dwFinalBlockTableSize++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the size of the attributes file
|
|
||||||
if(nError == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
dwFileSize = sizeof(MPQ_ATTRIBUTES_HEADER); // Header
|
|
||||||
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32)
|
|
||||||
dwFileSize += dwFinalBlockTableSize * sizeof(DWORD);
|
|
||||||
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME)
|
|
||||||
dwFileSize += dwFinalBlockTableSize * sizeof(ULONGLONG);
|
|
||||||
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5)
|
|
||||||
dwFileSize += dwFinalBlockTableSize * MD5_DIGEST_SIZE;
|
|
||||||
if(ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT)
|
|
||||||
dwFileSize += ((dwFinalBlockTableSize - 1)) / 8 + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the flags for (attributes)
|
|
||||||
if(ha->dwFileFlags2 == 0)
|
|
||||||
ha->dwFileFlags2 = GetDefaultSpecialFileFlags(ha, dwFileSize);
|
|
||||||
|
|
||||||
// Create the attributes file in the MPQ
|
|
||||||
nError = SFileAddFile_Init(ha, ATTRIBUTES_NAME,
|
|
||||||
0,
|
|
||||||
dwFileSize,
|
|
||||||
LANG_NEUTRAL,
|
|
||||||
ha->dwFileFlags2 | MPQ_FILE_REPLACEEXISTING,
|
|
||||||
&hf);
|
|
||||||
|
|
||||||
// Write all parts of the (attributes) file
|
|
||||||
if(nError == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
assert(ha->dwFileTableSize == dwFinalBlockTableSize);
|
|
||||||
|
|
||||||
// Note that we don't know what the new bit (0x08) means.
|
|
||||||
AttrHeader.dwVersion = BSWAP_INT32_UNSIGNED(100);
|
|
||||||
AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED((ha->dwAttrFlags & MPQ_ATTRIBUTE_ALL));
|
|
||||||
dwToWrite = sizeof(MPQ_ATTRIBUTES_HEADER);
|
|
||||||
nError = SFileAddFile_Write(hf, &AttrHeader, dwToWrite, MPQ_COMPRESSION_ZLIB);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the array of CRC32
|
|
||||||
if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_CRC32))
|
|
||||||
{
|
|
||||||
LPDWORD pArrayCRC32 = STORM_ALLOC(DWORD, dwFinalBlockTableSize);
|
|
||||||
|
|
||||||
if(pArrayCRC32 != NULL)
|
|
||||||
{
|
|
||||||
// Copy from file table
|
|
||||||
for(i = 0; i < ha->dwFileTableSize; i++)
|
|
||||||
pArrayCRC32[i] = BSWAP_INT32_UNSIGNED(ha->pFileTable[i].dwCrc32);
|
|
||||||
|
|
||||||
dwToWrite = ha->dwFileTableSize * sizeof(DWORD);
|
|
||||||
nError = SFileAddFile_Write(hf, pArrayCRC32, dwToWrite, MPQ_COMPRESSION_ZLIB);
|
|
||||||
STORM_FREE(pArrayCRC32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the array of file time
|
|
||||||
if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_FILETIME))
|
|
||||||
{
|
|
||||||
ULONGLONG * pArrayFileTime = STORM_ALLOC(ULONGLONG, ha->dwFileTableSize);
|
|
||||||
|
|
||||||
if(pArrayFileTime != NULL)
|
|
||||||
{
|
|
||||||
// Copy from file table
|
|
||||||
for(i = 0; i < ha->dwFileTableSize; i++)
|
|
||||||
pArrayFileTime[i] = BSWAP_INT64_UNSIGNED(ha->pFileTable[i].FileTime);
|
|
||||||
|
|
||||||
dwToWrite = ha->dwFileTableSize * sizeof(ULONGLONG);
|
|
||||||
nError = SFileAddFile_Write(hf, pArrayFileTime, dwToWrite, MPQ_COMPRESSION_ZLIB);
|
|
||||||
STORM_FREE(pArrayFileTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the array of MD5s
|
|
||||||
if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_MD5))
|
|
||||||
{
|
|
||||||
char * pArrayMD5 = STORM_ALLOC(char, ha->dwFileTableSize * MD5_DIGEST_SIZE);
|
|
||||||
|
|
||||||
if(pArrayMD5 != NULL)
|
|
||||||
{
|
|
||||||
// Copy from file table
|
|
||||||
for(i = 0; i < ha->dwFileTableSize; i++)
|
|
||||||
memcpy(&pArrayMD5[i * MD5_DIGEST_SIZE], ha->pFileTable[i].md5, MD5_DIGEST_SIZE);
|
|
||||||
|
|
||||||
dwToWrite = ha->dwFileTableSize * MD5_DIGEST_SIZE;
|
|
||||||
nError = SFileAddFile_Write(hf, pArrayMD5, dwToWrite, MPQ_COMPRESSION_ZLIB);
|
|
||||||
STORM_FREE(pArrayMD5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the array of patch bits
|
|
||||||
if(nError == ERROR_SUCCESS && (ha->dwAttrFlags & MPQ_ATTRIBUTE_PATCH_BIT))
|
|
||||||
{
|
|
||||||
LPBYTE pbBitArray;
|
|
||||||
DWORD dwByteSize = ((ha->dwFileTableSize - 1) / 8) + 1;
|
|
||||||
|
|
||||||
pbBitArray = STORM_ALLOC(BYTE, dwByteSize);
|
|
||||||
if(pbBitArray != NULL)
|
|
||||||
{
|
|
||||||
memset(pbBitArray, 0, dwByteSize);
|
|
||||||
for(i = 0; i < ha->dwFileTableSize; i++)
|
|
||||||
{
|
|
||||||
DWORD dwByteIndex = i / 8;
|
|
||||||
DWORD dwBitMask = 0x80 >> (i & 7);
|
|
||||||
|
|
||||||
if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE)
|
|
||||||
pbBitArray[dwByteIndex] |= dwBitMask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nError = SFileAddFile_Write(hf, pbBitArray, dwByteSize, MPQ_COMPRESSION_ZLIB);
|
// Free the attributes buffer
|
||||||
STORM_FREE(pbBitArray);
|
STORM_FREE(pbAttrFile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the list file is empty, we assume ERROR_SUCCESS
|
||||||
|
nError = (cbAttrFile == 0) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the save process succeeded, we clear the MPQ_FLAG_ATTRIBUTE_INVALID flag
|
||||||
|
if(nError == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
ha->dwFlags &= ~MPQ_FLAG_ATTRIBUTES_INVALID;
|
||||||
|
ha->dwReservedFiles--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize the file in the archive
|
|
||||||
if(hf != NULL)
|
|
||||||
{
|
|
||||||
SFileAddFile_Finish(hf);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(nError == ERROR_SUCCESS)
|
|
||||||
ha->dwFlags &= ~MPQ_FLAG_INV_ATTRIBUTES;
|
|
||||||
return nError;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -375,7 +479,7 @@ DWORD WINAPI SFileGetAttributes(HANDLE hMpq)
|
||||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||||
|
|
||||||
// Verify the parameters
|
// Verify the parameters
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return SFILE_INVALID_ATTRIBUTES;
|
return SFILE_INVALID_ATTRIBUTES;
|
||||||
|
|
@ -389,7 +493,7 @@ bool WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags)
|
||||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||||
|
|
||||||
// Verify the parameters
|
// Verify the parameters
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -434,12 +538,12 @@ bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to open the file
|
// Attempt to open the file
|
||||||
if(!SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_FROM_MPQ, &hFile))
|
if(!SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_BASE_FILE, &hFile))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Get the file size
|
// Get the file size
|
||||||
hf = (TMPQFile *)hFile;
|
hf = (TMPQFile *)hFile;
|
||||||
SFileGetFileInfo(hFile, SFILE_INFO_FILE_SIZE, &dwTotalBytes, sizeof(DWORD), NULL);
|
dwTotalBytes = hf->pFileEntry->dwFileSize;
|
||||||
|
|
||||||
// Initialize the CRC32 and MD5 contexts
|
// Initialize the CRC32 and MD5 contexts
|
||||||
md5_init(&md5_state);
|
md5_init(&md5_state);
|
||||||
|
|
|
||||||
|
|
@ -7,26 +7,47 @@
|
||||||
/* -------- ---- --- ------- */
|
/* -------- ---- --- ------- */
|
||||||
/* 14.04.03 1.00 Lad Splitted from SFileCreateArchiveEx.cpp */
|
/* 14.04.03 1.00 Lad Splitted from SFileCreateArchiveEx.cpp */
|
||||||
/* 19.11.03 1.01 Dan Big endian handling */
|
/* 19.11.03 1.01 Dan Big endian handling */
|
||||||
|
/* 21.04.13 1.02 Dea Compact callback now part of TMPQArchive */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
#define __STORMLIB_SELF__
|
#define __STORMLIB_SELF__
|
||||||
#include "StormLib.h"
|
#include "StormLib.h"
|
||||||
#include "StormCommon.h"
|
#include "StormCommon.h"
|
||||||
|
|
||||||
/*****************************************************************************/
|
|
||||||
/* Local variables */
|
|
||||||
/*****************************************************************************/
|
|
||||||
|
|
||||||
static SFILE_COMPACT_CALLBACK CompactCB = NULL;
|
|
||||||
static ULONGLONG CompactBytesProcessed = 0;
|
|
||||||
static ULONGLONG CompactTotalBytes = 0;
|
|
||||||
static void * pvUserData = NULL;
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* Local functions */
|
/* Local functions */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, LPDWORD pFileKeys)
|
static int CheckIfAllFilesKnown(TMPQArchive * ha)
|
||||||
|
{
|
||||||
|
TFileEntry * pFileTableEnd;
|
||||||
|
TFileEntry * pFileEntry;
|
||||||
|
DWORD dwBlockIndex = 0;
|
||||||
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// Verify the file table
|
||||||
|
if(nError == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||||
|
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++, dwBlockIndex++)
|
||||||
|
{
|
||||||
|
// If there is an existing entry in the file table, check its name
|
||||||
|
if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
|
||||||
|
{
|
||||||
|
// The name must be valid and must not be a pseudo-name
|
||||||
|
if(pFileEntry->szFileName == NULL || IsPseudoFileName(pFileEntry->szFileName, NULL))
|
||||||
|
{
|
||||||
|
nError = ERROR_UNKNOWN_FILE_NAMES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nError;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CheckIfAllKeysKnown(TMPQArchive * ha, const char * szListFile, LPDWORD pFileKeys)
|
||||||
{
|
{
|
||||||
TFileEntry * pFileTableEnd;
|
TFileEntry * pFileTableEnd;
|
||||||
TFileEntry * pFileEntry;
|
TFileEntry * pFileEntry;
|
||||||
|
|
@ -34,11 +55,11 @@ static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, LPDWO
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Add the listfile to the MPQ
|
// Add the listfile to the MPQ
|
||||||
if(nError == ERROR_SUCCESS && szListFile != NULL)
|
if(szListFile != NULL)
|
||||||
{
|
{
|
||||||
// Notify the user
|
// Notify the user
|
||||||
if(CompactCB != NULL)
|
if(ha->pfnCompactCB != NULL)
|
||||||
CompactCB(pvUserData, CCB_CHECKING_FILES, CompactBytesProcessed, CompactTotalBytes);
|
ha->pfnCompactCB(ha->pvCompactUserData, CCB_CHECKING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||||
|
|
||||||
nError = SFileAddListFile((HANDLE)ha, szListFile);
|
nError = SFileAddListFile((HANDLE)ha, szListFile);
|
||||||
}
|
}
|
||||||
|
|
@ -49,30 +70,49 @@ static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, LPDWO
|
||||||
pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||||
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++, dwBlockIndex++)
|
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++, dwBlockIndex++)
|
||||||
{
|
{
|
||||||
|
// If the file exists and it's encrypted
|
||||||
if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
|
if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
|
||||||
{
|
{
|
||||||
|
// If we know the name, we decrypt the file key from the file name
|
||||||
if(pFileEntry->szFileName != NULL && !IsPseudoFileName(pFileEntry->szFileName, NULL))
|
if(pFileEntry->szFileName != NULL && !IsPseudoFileName(pFileEntry->szFileName, NULL))
|
||||||
{
|
{
|
||||||
DWORD dwFileKey = 0;
|
// Give the key to the caller
|
||||||
|
pFileKeys[dwBlockIndex] = DecryptFileKey(pFileEntry->szFileName,
|
||||||
|
pFileEntry->ByteOffset,
|
||||||
|
pFileEntry->dwFileSize,
|
||||||
|
pFileEntry->dwFlags);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
// If the file has a nonzero size, we can try to read few bytes of data
|
||||||
|
// and force to detect the decryption key that way
|
||||||
|
if(pFileEntry->dwFileSize > 0x10)
|
||||||
|
{
|
||||||
|
TMPQFile * hf = NULL;
|
||||||
|
DWORD dwBytesRead = 0;
|
||||||
|
DWORD FileData[4];
|
||||||
|
|
||||||
// Resolve the file key. Use plain file name for it
|
// Create file handle where we load the sector offset table
|
||||||
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
|
hf = CreateFileHandle(ha, pFileEntry);
|
||||||
|
if(hf != NULL)
|
||||||
{
|
{
|
||||||
dwFileKey = DecryptFileKey(pFileEntry->szFileName,
|
// Call one dummy load of the first 4 bytes.
|
||||||
pFileEntry->ByteOffset,
|
// This enforces loading all buffers and also detecting of the decryption key
|
||||||
pFileEntry->dwFileSize,
|
SFileReadFile((HANDLE)hf, FileData, sizeof(FileData), &dwBytesRead, NULL);
|
||||||
pFileEntry->dwFlags);
|
pFileKeys[dwBlockIndex] = hf->dwFileKey;
|
||||||
|
FreeFileHandle(hf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give the key to the caller
|
// If we succeeded in reading 16 bytes from the file,
|
||||||
if(pFileKeys != NULL)
|
// we also know the encryption key
|
||||||
pFileKeys[dwBlockIndex] = dwFileKey;
|
if(dwBytesRead == sizeof(FileData))
|
||||||
}
|
continue;
|
||||||
else
|
|
||||||
{
|
|
||||||
nError = ERROR_CAN_NOT_COMPLETE;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
// We don't know the encryption key of this file,
|
||||||
|
// thus we cannot compact the file
|
||||||
|
nError = ERROR_UNKNOWN_FILE_NAMES;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -81,10 +121,11 @@ static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, LPDWO
|
||||||
}
|
}
|
||||||
|
|
||||||
static int CopyNonMpqData(
|
static int CopyNonMpqData(
|
||||||
TFileStream * pSrcStream,
|
TMPQArchive * ha,
|
||||||
TFileStream * pTrgStream,
|
TFileStream * pSrcStream,
|
||||||
ULONGLONG & ByteOffset,
|
TFileStream * pTrgStream,
|
||||||
ULONGLONG & ByteCount)
|
ULONGLONG & ByteOffset,
|
||||||
|
ULONGLONG & ByteCount)
|
||||||
{
|
{
|
||||||
ULONGLONG DataSize = ByteCount;
|
ULONGLONG DataSize = ByteCount;
|
||||||
DWORD dwToRead;
|
DWORD dwToRead;
|
||||||
|
|
@ -114,10 +155,10 @@ static int CopyNonMpqData(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the progress
|
// Update the progress
|
||||||
if(CompactCB != NULL)
|
if(ha->pfnCompactCB != NULL)
|
||||||
{
|
{
|
||||||
CompactBytesProcessed += dwToRead;
|
ha->CompactBytesProcessed += dwToRead;
|
||||||
CompactCB(pvUserData, CCB_COPYING_NON_MPQ_DATA, CompactBytesProcessed, CompactTotalBytes);
|
ha->pfnCompactCB(ha->pvCompactUserData, CCB_COPYING_NON_MPQ_DATA, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrement the number of data to be copied
|
// Decrement the number of data to be copied
|
||||||
|
|
@ -125,18 +166,18 @@ static int CopyNonMpqData(
|
||||||
DataSize -= dwToRead;
|
DataSize -= dwToRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copies all file sectors into another archive.
|
// Copies all file sectors into another archive.
|
||||||
static int CopyMpqFileSectors(
|
static int CopyMpqFileSectors(
|
||||||
TMPQArchive * ha,
|
TMPQArchive * ha,
|
||||||
TMPQFile * hf,
|
TMPQFile * hf,
|
||||||
TFileStream * pNewStream)
|
TFileStream * pNewStream,
|
||||||
|
ULONGLONG MpqFilePos) // MPQ file position in the new archive
|
||||||
{
|
{
|
||||||
TFileEntry * pFileEntry = hf->pFileEntry;
|
TFileEntry * pFileEntry = hf->pFileEntry;
|
||||||
ULONGLONG RawFilePos; // Used for calculating sector offset in the old MPQ archive
|
ULONGLONG RawFilePos; // Used for calculating sector offset in the old MPQ archive
|
||||||
ULONGLONG MpqFilePos; // MPQ file position in the new archive
|
|
||||||
DWORD dwBytesToCopy = pFileEntry->dwCmpSize;
|
DWORD dwBytesToCopy = pFileEntry->dwCmpSize;
|
||||||
DWORD dwPatchSize = 0; // Size of patch header
|
DWORD dwPatchSize = 0; // Size of patch header
|
||||||
DWORD dwFileKey1 = 0; // File key used for decryption
|
DWORD dwFileKey1 = 0; // File key used for decryption
|
||||||
|
|
@ -144,11 +185,7 @@ static int CopyMpqFileSectors(
|
||||||
DWORD dwCmpSize = 0; // Compressed file size, including patch header
|
DWORD dwCmpSize = 0; // Compressed file size, including patch header
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Remember the position in the destination file
|
// Resolve decryption keys. Note that the file key given
|
||||||
FileStream_GetPos(pNewStream, &MpqFilePos);
|
|
||||||
MpqFilePos -= ha->MpqPos;
|
|
||||||
|
|
||||||
// Resolve decryption keys. Note that the file key given
|
|
||||||
// in the TMPQFile structure also includes the key adjustment
|
// in the TMPQFile structure also includes the key adjustment
|
||||||
if(nError == ERROR_SUCCESS && (pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED))
|
if(nError == ERROR_SUCCESS && (pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED))
|
||||||
{
|
{
|
||||||
|
|
@ -174,11 +211,11 @@ static int CopyMpqFileSectors(
|
||||||
// If we have to save sector offset table, do it.
|
// If we have to save sector offset table, do it.
|
||||||
if(nError == ERROR_SUCCESS && hf->SectorOffsets != NULL)
|
if(nError == ERROR_SUCCESS && hf->SectorOffsets != NULL)
|
||||||
{
|
{
|
||||||
DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]);
|
DWORD * SectorOffsetsCopy = STORM_ALLOC(DWORD, hf->SectorOffsets[0] / sizeof(DWORD));
|
||||||
DWORD dwSectorOffsLen = hf->SectorOffsets[0];
|
DWORD dwSectorOffsLen = hf->SectorOffsets[0];
|
||||||
|
|
||||||
assert((pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) == 0);
|
assert((pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) == 0);
|
||||||
assert(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED);
|
assert(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK);
|
||||||
|
|
||||||
if(SectorOffsetsCopy == NULL)
|
if(SectorOffsetsCopy == NULL)
|
||||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
|
@ -199,10 +236,10 @@ static int CopyMpqFileSectors(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update compact progress
|
// Update compact progress
|
||||||
if(CompactCB != NULL)
|
if(ha->pfnCompactCB != NULL)
|
||||||
{
|
{
|
||||||
CompactBytesProcessed += dwSectorOffsLen;
|
ha->CompactBytesProcessed += dwSectorOffsLen;
|
||||||
CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes);
|
ha->pfnCompactCB(ha->pvCompactUserData, CCB_COMPACTING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
STORM_FREE(SectorOffsetsCopy);
|
STORM_FREE(SectorOffsetsCopy);
|
||||||
|
|
@ -230,7 +267,7 @@ static int CopyMpqFileSectors(
|
||||||
|
|
||||||
// Calculate the raw file offset of the file sector
|
// Calculate the raw file offset of the file sector
|
||||||
CalculateRawSectorOffset(RawFilePos, hf, dwRawByteOffset);
|
CalculateRawSectorOffset(RawFilePos, hf, dwRawByteOffset);
|
||||||
|
|
||||||
// Read the file sector
|
// Read the file sector
|
||||||
if(!FileStream_Read(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector))
|
if(!FileStream_Read(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector))
|
||||||
{
|
{
|
||||||
|
|
@ -239,7 +276,7 @@ static int CopyMpqFileSectors(
|
||||||
}
|
}
|
||||||
|
|
||||||
// If necessary, re-encrypt the sector
|
// If necessary, re-encrypt the sector
|
||||||
// Note: Recompression is not necessary here. Unlike encryption,
|
// Note: Recompression is not necessary here. Unlike encryption,
|
||||||
// the compression does not depend on the position of the file in MPQ.
|
// the compression does not depend on the position of the file in MPQ.
|
||||||
if((pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) && dwFileKey1 != dwFileKey2)
|
if((pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) && dwFileKey1 != dwFileKey2)
|
||||||
{
|
{
|
||||||
|
|
@ -257,10 +294,10 @@ static int CopyMpqFileSectors(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update compact progress
|
// Update compact progress
|
||||||
if(CompactCB != NULL)
|
if(ha->pfnCompactCB != NULL)
|
||||||
{
|
{
|
||||||
CompactBytesProcessed += dwRawDataInSector;
|
ha->CompactBytesProcessed += dwRawDataInSector;
|
||||||
CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes);
|
ha->pfnCompactCB(ha->pvCompactUserData, CCB_COMPACTING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust byte counts
|
// Adjust byte counts
|
||||||
|
|
@ -285,10 +322,10 @@ static int CopyMpqFileSectors(
|
||||||
nError = GetLastError();
|
nError = GetLastError();
|
||||||
|
|
||||||
// Update compact progress
|
// Update compact progress
|
||||||
if(CompactCB != NULL)
|
if(ha->pfnCompactCB != NULL)
|
||||||
{
|
{
|
||||||
CompactBytesProcessed += dwCrcLength;
|
ha->CompactBytesProcessed += dwCrcLength;
|
||||||
CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes);
|
ha->pfnCompactCB(ha->pvCompactUserData, CCB_COMPACTING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size of the CRC block is also included in the compressed file size
|
// Size of the CRC block is also included in the compressed file size
|
||||||
|
|
@ -318,7 +355,6 @@ static int CopyMpqFileSectors(
|
||||||
|
|
||||||
// Include these extra data in the compressed size
|
// Include these extra data in the compressed size
|
||||||
dwCmpSize += dwBytesToCopy;
|
dwCmpSize += dwBytesToCopy;
|
||||||
dwBytesToCopy = 0;
|
|
||||||
STORM_FREE(pbExtraData);
|
STORM_FREE(pbExtraData);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -328,36 +364,30 @@ static int CopyMpqFileSectors(
|
||||||
// Write the MD5's of the raw file data, if needed
|
// Write the MD5's of the raw file data, if needed
|
||||||
if(nError == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0)
|
if(nError == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0)
|
||||||
{
|
{
|
||||||
nError = WriteMpqDataMD5(pNewStream,
|
nError = WriteMpqDataMD5(pNewStream,
|
||||||
ha->MpqPos + MpqFilePos,
|
ha->MpqPos + MpqFilePos,
|
||||||
pFileEntry->dwCmpSize,
|
pFileEntry->dwCmpSize,
|
||||||
ha->pHeader->dwRawChunkSize);
|
ha->pHeader->dwRawChunkSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update file position in the block table
|
// Verify the number of bytes written
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// At this point, number of bytes written should be exactly
|
// At this point, number of bytes written should be exactly
|
||||||
// the same like the compressed file size. If it isn't,
|
// the same like the compressed file size. If it isn't,
|
||||||
// there's something wrong (an unknown archive version, MPQ protection, ...)
|
// there's something wrong (an unknown archive version, MPQ malformation, ...)
|
||||||
//
|
//
|
||||||
// Note: Diablo savegames have very weird layout, and the file "hero"
|
// Note: Diablo savegames have very weird layout, and the file "hero"
|
||||||
// seems to have improper compressed size. Instead of real compressed size,
|
// seems to have improper compressed size. Instead of real compressed size,
|
||||||
// the "dwCmpSize" member of the block table entry contains
|
// the "dwCmpSize" member of the block table entry contains
|
||||||
// uncompressed size of file data + size of the sector table.
|
// uncompressed size of file data + size of the sector table.
|
||||||
// If we compact the archive, Diablo will refuse to load the game
|
// If we compact the archive, Diablo will refuse to load the game
|
||||||
// Seems like some sort of protection to me.
|
|
||||||
//
|
//
|
||||||
// Note: Some patch files in WOW patches don't count the patch header
|
// Note: Some patch files in WOW patches don't count the patch header
|
||||||
// into compressed size
|
// into compressed size
|
||||||
//
|
//
|
||||||
|
|
||||||
if(dwCmpSize <= pFileEntry->dwCmpSize && pFileEntry->dwCmpSize <= dwCmpSize + dwPatchSize)
|
if(!(dwCmpSize <= pFileEntry->dwCmpSize && pFileEntry->dwCmpSize <= dwCmpSize + dwPatchSize))
|
||||||
{
|
|
||||||
// Note: DO NOT update the compressed size in the file entry, no matter how bad it is.
|
|
||||||
pFileEntry->ByteOffset = MpqFilePos;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
nError = ERROR_FILE_CORRUPT;
|
nError = ERROR_FILE_CORRUPT;
|
||||||
assert(false);
|
assert(false);
|
||||||
|
|
@ -372,6 +402,7 @@ static int CopyMpqFiles(TMPQArchive * ha, LPDWORD pFileKeys, TFileStream * pNewS
|
||||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||||
TFileEntry * pFileEntry;
|
TFileEntry * pFileEntry;
|
||||||
TMPQFile * hf = NULL;
|
TMPQFile * hf = NULL;
|
||||||
|
ULONGLONG MpqFilePos;
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Walk through all files and write them to the destination MPQ archive
|
// Walk through all files and write them to the destination MPQ archive
|
||||||
|
|
@ -379,63 +410,66 @@ static int CopyMpqFiles(TMPQArchive * ha, LPDWORD pFileKeys, TFileStream * pNewS
|
||||||
{
|
{
|
||||||
// Copy all the file sectors
|
// Copy all the file sectors
|
||||||
// Only do that when the file has nonzero size
|
// Only do that when the file has nonzero size
|
||||||
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->dwFileSize != 0)
|
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS))
|
||||||
{
|
{
|
||||||
// Allocate structure for the MPQ file
|
// Query the position where the destination file will be
|
||||||
hf = CreateMpqFile(ha);
|
FileStream_GetPos(pNewStream, &MpqFilePos);
|
||||||
if(hf == NULL)
|
MpqFilePos = MpqFilePos - ha->MpqPos;
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
// Store file entry
|
// Perform file copy ONLY if the file has nonzero size
|
||||||
hf->pFileEntry = pFileEntry;
|
if(pFileEntry->dwFileSize != 0)
|
||||||
|
|
||||||
// Set the raw file position
|
|
||||||
hf->MpqFilePos = pFileEntry->ByteOffset;
|
|
||||||
hf->RawFilePos = ha->MpqPos + hf->MpqFilePos;
|
|
||||||
|
|
||||||
// Set the file decryption key
|
|
||||||
hf->dwFileKey = pFileKeys[pFileEntry - ha->pFileTable];
|
|
||||||
hf->dwDataSize = pFileEntry->dwFileSize;
|
|
||||||
|
|
||||||
// If the file is a patch file, load the patch header
|
|
||||||
if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
|
|
||||||
{
|
{
|
||||||
nError = AllocatePatchInfo(hf, true);
|
// Allocate structure for the MPQ file
|
||||||
|
hf = CreateFileHandle(ha, pFileEntry);
|
||||||
|
if(hf == NULL)
|
||||||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
|
||||||
|
// Set the file decryption key
|
||||||
|
hf->dwFileKey = pFileKeys[pFileEntry - ha->pFileTable];
|
||||||
|
|
||||||
|
// If the file is a patch file, load the patch header
|
||||||
|
if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
|
||||||
|
{
|
||||||
|
nError = AllocatePatchInfo(hf, true);
|
||||||
|
if(nError != ERROR_SUCCESS)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate buffers for file sector and sector offset table
|
||||||
|
nError = AllocateSectorBuffer(hf);
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate buffers for file sector and sector offset table
|
// Also allocate sector offset table and sector checksum table
|
||||||
nError = AllocateSectorBuffer(hf);
|
nError = AllocateSectorOffsets(hf, true);
|
||||||
if(nError != ERROR_SUCCESS)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Also allocate sector offset table and sector checksum table
|
|
||||||
nError = AllocateSectorOffsets(hf, true);
|
|
||||||
if(nError != ERROR_SUCCESS)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Also load sector checksums, if any
|
|
||||||
if(pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC)
|
|
||||||
{
|
|
||||||
nError = AllocateSectorChecksums(hf, false);
|
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// Also load sector checksums, if any
|
||||||
|
if(pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC)
|
||||||
|
{
|
||||||
|
nError = AllocateSectorChecksums(hf, false);
|
||||||
|
if(nError != ERROR_SUCCESS)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy all file sectors
|
||||||
|
nError = CopyMpqFileSectors(ha, hf, pNewStream, MpqFilePos);
|
||||||
|
if(nError != ERROR_SUCCESS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Free buffers. This also sets "hf" to NULL.
|
||||||
|
FreeFileHandle(hf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy all file sectors
|
// Note: DO NOT update the compressed size in the file entry, no matter how bad it is.
|
||||||
nError = CopyMpqFileSectors(ha, hf, pNewStream);
|
pFileEntry->ByteOffset = MpqFilePos;
|
||||||
if(nError != ERROR_SUCCESS)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Free buffers. This also sets "hf" to NULL.
|
|
||||||
FreeMPQFile(hf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup and exit
|
// Cleanup and exit
|
||||||
if(hf != NULL)
|
if(hf != NULL)
|
||||||
FreeMPQFile(hf);
|
FreeFileHandle(hf);
|
||||||
return nError;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -444,10 +478,18 @@ static int CopyMpqFiles(TMPQArchive * ha, LPDWORD pFileKeys, TFileStream * pNewS
|
||||||
/* Public functions */
|
/* Public functions */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
bool WINAPI SFileSetCompactCallback(HANDLE /* hMpq */, SFILE_COMPACT_CALLBACK aCompactCB, void * pvData)
|
bool WINAPI SFileSetCompactCallback(HANDLE hMpq, SFILE_COMPACT_CALLBACK pfnCompactCB, void * pvUserData)
|
||||||
{
|
{
|
||||||
CompactCB = aCompactCB;
|
TMPQArchive * ha = (TMPQArchive *) hMpq;
|
||||||
pvUserData = pvData;
|
|
||||||
|
if (!IsValidMpqHandle(hMpq))
|
||||||
|
{
|
||||||
|
SetLastError(ERROR_INVALID_HANDLE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha->pfnCompactCB = pfnCompactCB;
|
||||||
|
ha->pvCompactUserData = pvUserData;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -466,7 +508,7 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Test the valid parameters
|
// Test the valid parameters
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
nError = ERROR_INVALID_HANDLE;
|
nError = ERROR_INVALID_HANDLE;
|
||||||
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
||||||
nError = ERROR_ACCESS_DENIED;
|
nError = ERROR_ACCESS_DENIED;
|
||||||
|
|
@ -491,9 +533,9 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// Initialize the progress variables for compact callback
|
// Initialize the progress variables for compact callback
|
||||||
FileStream_GetSize(ha->pStream, &CompactTotalBytes);
|
FileStream_GetSize(ha->pStream, &(ha->CompactTotalBytes));
|
||||||
CompactBytesProcessed = 0;
|
ha->CompactBytesProcessed = 0;
|
||||||
nError = CheckIfAllFilesKnown(ha, szListFile, pFileKeys);
|
nError = CheckIfAllKeysKnown(ha, szListFile, pFileKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the temporary file name and create it
|
// Get the temporary file name and create it
|
||||||
|
|
@ -505,7 +547,7 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
|
||||||
else
|
else
|
||||||
_tcscat(szTempFile, _T("_"));
|
_tcscat(szTempFile, _T("_"));
|
||||||
|
|
||||||
pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
|
pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
|
||||||
if(pTempStream == NULL)
|
if(pTempStream == NULL)
|
||||||
nError = GetLastError();
|
nError = GetLastError();
|
||||||
}
|
}
|
||||||
|
|
@ -514,12 +556,12 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
|
||||||
if(nError == ERROR_SUCCESS && ha->UserDataPos != 0)
|
if(nError == ERROR_SUCCESS && ha->UserDataPos != 0)
|
||||||
{
|
{
|
||||||
// Inform the application about the progress
|
// Inform the application about the progress
|
||||||
if(CompactCB != NULL)
|
if(ha->pfnCompactCB != NULL)
|
||||||
CompactCB(pvUserData, CCB_COPYING_NON_MPQ_DATA, CompactBytesProcessed, CompactTotalBytes);
|
ha->pfnCompactCB(ha->pvCompactUserData, CCB_COPYING_NON_MPQ_DATA, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||||
|
|
||||||
ByteOffset = 0;
|
ByteOffset = 0;
|
||||||
ByteCount = ha->UserDataPos;
|
ByteCount = ha->UserDataPos;
|
||||||
nError = CopyNonMpqData(ha->pStream, pTempStream, ByteOffset, ByteCount);
|
nError = CopyNonMpqData(ha, ha->pStream, pTempStream, ByteOffset, ByteCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the MPQ user data (if any)
|
// Write the MPQ user data (if any)
|
||||||
|
|
@ -533,35 +575,50 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
|
||||||
|
|
||||||
assert(ha->pUserData != NULL);
|
assert(ha->pUserData != NULL);
|
||||||
assert(ha->pUserData->dwHeaderOffs == ByteCount);
|
assert(ha->pUserData->dwHeaderOffs == ByteCount);
|
||||||
nError = CopyNonMpqData(ha->pStream, pTempStream, ByteOffset, ByteCount);
|
nError = CopyNonMpqData(ha, ha->pStream, pTempStream, ByteOffset, ByteCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the MPQ header
|
// Write the MPQ header
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// Remember the header size before swapping
|
TMPQHeader SaveMpqHeader;
|
||||||
DWORD dwBytesToWrite = ha->pHeader->dwHeaderSize;
|
|
||||||
|
|
||||||
BSWAP_TMPQHEADER(ha->pHeader);
|
// Write the MPQ header to the file
|
||||||
if(!FileStream_Write(pTempStream, NULL, ha->pHeader, dwBytesToWrite))
|
memcpy(&SaveMpqHeader, ha->pHeader, ha->pHeader->dwHeaderSize);
|
||||||
|
BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_1);
|
||||||
|
BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_2);
|
||||||
|
BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_3);
|
||||||
|
BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_4);
|
||||||
|
if(!FileStream_Write(pTempStream, NULL, &SaveMpqHeader, ha->pHeader->dwHeaderSize))
|
||||||
nError = GetLastError();
|
nError = GetLastError();
|
||||||
BSWAP_TMPQHEADER(ha->pHeader);
|
|
||||||
|
|
||||||
// Update the progress
|
// Update the progress
|
||||||
CompactBytesProcessed += ha->pHeader->dwHeaderSize;
|
ha->CompactBytesProcessed += ha->pHeader->dwHeaderSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now copy all files
|
// Now copy all files
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
|
||||||
nError = CopyMpqFiles(ha, pFileKeys, pTempStream);
|
nError = CopyMpqFiles(ha, pFileKeys, pTempStream);
|
||||||
ha->dwFlags |= MPQ_FLAG_CHANGED;
|
|
||||||
|
// Defragment the file table
|
||||||
|
if(nError == ERROR_SUCCESS)
|
||||||
|
nError = RebuildFileTable(ha, ha->pHeader->dwHashTableSize, ha->dwMaxFileCount);
|
||||||
|
|
||||||
|
// We also need to rebuild the HET table, if any
|
||||||
|
if(nError == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
// Invalidate (listfile) and (attributes)
|
||||||
|
InvalidateInternalFiles(ha);
|
||||||
|
|
||||||
|
// Rebuild the HET table, if we have any
|
||||||
|
if(ha->pHetTable != NULL)
|
||||||
|
nError = RebuildHetTable(ha);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If succeeded, switch the streams
|
// If succeeded, switch the streams
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
if(FileStream_Switch(ha->pStream, pTempStream))
|
if(FileStream_Replace(ha->pStream, pTempStream))
|
||||||
pTempStream = NULL;
|
pTempStream = NULL;
|
||||||
else
|
else
|
||||||
nError = ERROR_CAN_NOT_COMPLETE;
|
nError = ERROR_CAN_NOT_COMPLETE;
|
||||||
|
|
@ -573,21 +630,17 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
|
||||||
//
|
//
|
||||||
// Note: We don't recalculate position of the MPQ tables at this point.
|
// Note: We don't recalculate position of the MPQ tables at this point.
|
||||||
// SaveMPQTables does it automatically.
|
// SaveMPQTables does it automatically.
|
||||||
//
|
//
|
||||||
|
|
||||||
nError = SaveMPQTables(ha);
|
nError = SaveMPQTables(ha);
|
||||||
if(nError == ERROR_SUCCESS && CompactCB != NULL)
|
if(nError == ERROR_SUCCESS && ha->pfnCompactCB != NULL)
|
||||||
{
|
{
|
||||||
CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
|
ha->CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
|
||||||
CompactBytesProcessed += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock));
|
ha->CompactBytesProcessed += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock));
|
||||||
CompactCB(pvUserData, CCB_CLOSING_ARCHIVE, CompactBytesProcessed, CompactTotalBytes);
|
ha->pfnCompactCB(ha->pvCompactUserData, CCB_CLOSING_ARCHIVE, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate the compact callback
|
|
||||||
pvUserData = NULL;
|
|
||||||
CompactCB = NULL;
|
|
||||||
|
|
||||||
// Cleanup and return
|
// Cleanup and return
|
||||||
if(pTempStream != NULL)
|
if(pTempStream != NULL)
|
||||||
FileStream_Close(pTempStream);
|
FileStream_Close(pTempStream);
|
||||||
|
|
@ -610,156 +663,48 @@ DWORD WINAPI SFileGetMaxFileCount(HANDLE hMpq)
|
||||||
|
|
||||||
bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount)
|
bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount)
|
||||||
{
|
{
|
||||||
TMPQHetTable * pOldHetTable = NULL;
|
|
||||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||||
TFileEntry * pOldFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
DWORD dwNewHashTableSize = 0;
|
||||||
TFileEntry * pOldFileTable = NULL;
|
|
||||||
TFileEntry * pOldFileEntry;
|
|
||||||
TFileEntry * pFileEntry;
|
|
||||||
TMPQHash * pOldHashTable = NULL;
|
|
||||||
DWORD dwOldHashTableSize = 0;
|
|
||||||
DWORD dwOldFileTableSize = 0;
|
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Test the valid parameters
|
// Test the valid parameters
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
nError = ERROR_INVALID_HANDLE;
|
nError = ERROR_INVALID_HANDLE;
|
||||||
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
||||||
nError = ERROR_ACCESS_DENIED;
|
nError = ERROR_ACCESS_DENIED;
|
||||||
|
if(dwMaxFileCount < ha->dwFileTableSize)
|
||||||
// The new limit must not be lower than the index of the last file entry in the table
|
|
||||||
if(nError == ERROR_SUCCESS && ha->dwFileTableSize > dwMaxFileCount)
|
|
||||||
nError = ERROR_DISK_FULL;
|
nError = ERROR_DISK_FULL;
|
||||||
|
|
||||||
// ALL file names must be known in order to be able
|
// ALL file names must be known in order to be able
|
||||||
// to rebuild hash table size
|
// to rebuild hash table
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
nError = CheckIfAllFilesKnown(ha, NULL, NULL);
|
nError = CheckIfAllFilesKnown(ha);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the MPQ has a hash table, then we relocate the hash table
|
// If the MPQ has a hash table, then we relocate the hash table
|
||||||
if(nError == ERROR_SUCCESS && ha->pHashTable != NULL)
|
|
||||||
{
|
|
||||||
// Save parameters for the current hash table
|
|
||||||
dwOldHashTableSize = ha->pHeader->dwHashTableSize;
|
|
||||||
pOldHashTable = ha->pHashTable;
|
|
||||||
|
|
||||||
// Allocate new hash table
|
|
||||||
ha->pHeader->dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount);
|
|
||||||
ha->pHashTable = STORM_ALLOC(TMPQHash, ha->pHeader->dwHashTableSize);
|
|
||||||
if(ha->pHashTable != NULL)
|
|
||||||
memset(ha->pHashTable, 0xFF, ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
|
|
||||||
else
|
|
||||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the MPQ has HET table, allocate new one as well
|
|
||||||
if(nError == ERROR_SUCCESS && ha->pHetTable != NULL)
|
|
||||||
{
|
|
||||||
// Save the original HET table
|
|
||||||
pOldHetTable = ha->pHetTable;
|
|
||||||
|
|
||||||
// Create new one
|
|
||||||
ha->pHetTable = CreateHetTable(dwMaxFileCount, 0x40, true);
|
|
||||||
if(ha->pHetTable == NULL)
|
|
||||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now reallocate the file table
|
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// Save the current file table
|
// Calculate the hash table size for the new file limit
|
||||||
dwOldFileTableSize = ha->dwFileTableSize;
|
dwNewHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount);
|
||||||
pOldFileTable = ha->pFileTable;
|
|
||||||
|
|
||||||
// Create new one
|
// Rebuild both file tables
|
||||||
ha->pFileTable = STORM_ALLOC(TFileEntry, dwMaxFileCount);
|
nError = RebuildFileTable(ha, dwNewHashTableSize, dwMaxFileCount);
|
||||||
if(ha->pFileTable != NULL)
|
|
||||||
memset(ha->pFileTable, 0, dwMaxFileCount * sizeof(TFileEntry));
|
|
||||||
else
|
|
||||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we have to build both classic hash table and HET table.
|
// We always have to rebuild the (attributes) file due to file table change
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
DWORD dwFileIndex = 0;
|
// Invalidate (listfile) and (attributes)
|
||||||
DWORD dwHashIndex = 0;
|
|
||||||
|
|
||||||
// Create new hash and HET entry for each file
|
|
||||||
pFileEntry = ha->pFileTable;
|
|
||||||
for(pOldFileEntry = pOldFileTable; pOldFileEntry < pOldFileTableEnd; pOldFileEntry++)
|
|
||||||
{
|
|
||||||
if(pOldFileEntry->dwFlags & MPQ_FILE_EXISTS)
|
|
||||||
{
|
|
||||||
// Copy the old file entry to the new one
|
|
||||||
memcpy(pFileEntry, pOldFileEntry, sizeof(TFileEntry));
|
|
||||||
assert(pFileEntry->szFileName != NULL);
|
|
||||||
|
|
||||||
// Create new entry in the hash table
|
|
||||||
if(ha->pHashTable != NULL)
|
|
||||||
{
|
|
||||||
dwHashIndex = AllocateHashEntry(ha, pFileEntry);
|
|
||||||
if(dwHashIndex == HASH_ENTRY_FREE)
|
|
||||||
{
|
|
||||||
nError = ERROR_CAN_NOT_COMPLETE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new entry in the HET table, if needed
|
|
||||||
if(ha->pHetTable != NULL)
|
|
||||||
{
|
|
||||||
dwHashIndex = AllocateHetEntry(ha, pFileEntry);
|
|
||||||
if(dwHashIndex == HASH_ENTRY_FREE)
|
|
||||||
{
|
|
||||||
nError = ERROR_CAN_NOT_COMPLETE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to the next file entry in the new table
|
|
||||||
pFileEntry++;
|
|
||||||
dwFileIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark the archive as changed
|
|
||||||
// Note: We always have to rebuild the (attributes) file due to file table change
|
|
||||||
if(nError == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
ha->dwMaxFileCount = dwMaxFileCount;
|
|
||||||
InvalidateInternalFiles(ha);
|
InvalidateInternalFiles(ha);
|
||||||
|
|
||||||
|
// Rebuild the HET table, if we have any
|
||||||
|
if(ha->pHetTable != NULL)
|
||||||
|
nError = RebuildHetTable(ha);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// Revert the hash table
|
|
||||||
if(ha->pHashTable != NULL && pOldHashTable != NULL)
|
|
||||||
{
|
|
||||||
STORM_FREE(ha->pHashTable);
|
|
||||||
ha->pHeader->dwHashTableSize = dwOldHashTableSize;
|
|
||||||
ha->pHashTable = pOldHashTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revert the HET table
|
|
||||||
if(ha->pHetTable != NULL && pOldHetTable != NULL)
|
|
||||||
{
|
|
||||||
FreeHetTable(ha->pHetTable);
|
|
||||||
ha->pHetTable = pOldHetTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revert the file table
|
|
||||||
if(pOldFileTable != NULL)
|
|
||||||
{
|
|
||||||
STORM_FREE(ha->pFileTable);
|
|
||||||
ha->pFileTable = pOldFileTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Return the error
|
||||||
|
if(nError != ERROR_SUCCESS)
|
||||||
SetLastError(nError);
|
SetLastError(nError);
|
||||||
}
|
|
||||||
|
|
||||||
// Return the result
|
|
||||||
return (nError == ERROR_SUCCESS);
|
return (nError == ERROR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,10 @@ static int WriteNakedMPQHeader(TMPQArchive * ha)
|
||||||
Header.wSectorSize = pHeader->wSectorSize;
|
Header.wSectorSize = pHeader->wSectorSize;
|
||||||
|
|
||||||
// Write it to the file
|
// Write it to the file
|
||||||
BSWAP_TMPQHEADER(&Header);
|
BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_1);
|
||||||
|
BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_2);
|
||||||
|
BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_3);
|
||||||
|
BSWAP_TMPQHEADER(&Header, MPQ_FORMAT_VERSION_4);
|
||||||
if(!FileStream_Write(ha->pStream, &ha->MpqPos, &Header, dwBytesToWrite))
|
if(!FileStream_Write(ha->pStream, &ha->MpqPos, &Header, dwBytesToWrite))
|
||||||
nError = GetLastError();
|
nError = GetLastError();
|
||||||
|
|
||||||
|
|
@ -66,19 +69,32 @@ static int WriteNakedMPQHeader(TMPQArchive * ha)
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Creates a new MPQ archive.
|
// Creates a new MPQ archive.
|
||||||
|
|
||||||
bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq)
|
bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq)
|
||||||
{
|
{
|
||||||
SFILE_CREATE_MPQ CreateInfo;
|
SFILE_CREATE_MPQ CreateInfo;
|
||||||
|
|
||||||
// Fill the create structure
|
// Fill the create structure
|
||||||
memset(&CreateInfo, 0, sizeof(SFILE_CREATE_MPQ));
|
memset(&CreateInfo, 0, sizeof(SFILE_CREATE_MPQ));
|
||||||
CreateInfo.cbSize = sizeof(SFILE_CREATE_MPQ);
|
CreateInfo.cbSize = sizeof(SFILE_CREATE_MPQ);
|
||||||
CreateInfo.dwMpqVersion = (dwFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT;
|
CreateInfo.dwMpqVersion = (dwCreateFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT;
|
||||||
CreateInfo.dwStreamFlags = STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE;
|
CreateInfo.dwStreamFlags = STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE;
|
||||||
CreateInfo.dwAttrFlags = (dwFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_ATTRIBUTE_ALL : 0;
|
CreateInfo.dwFileFlags1 = (dwCreateFlags & MPQ_CREATE_LISTFILE) ? MPQ_FILE_EXISTS : 0;
|
||||||
|
CreateInfo.dwFileFlags2 = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_FILE_EXISTS : 0;
|
||||||
|
CreateInfo.dwFileFlags3 = (dwCreateFlags & MPQ_CREATE_SIGNATURE) ? MPQ_FILE_EXISTS : 0;
|
||||||
|
CreateInfo.dwAttrFlags = (dwCreateFlags & MPQ_CREATE_ATTRIBUTES) ? (MPQ_ATTRIBUTE_CRC32 | MPQ_ATTRIBUTE_FILETIME | MPQ_ATTRIBUTE_MD5) : 0;
|
||||||
CreateInfo.dwSectorSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000;
|
CreateInfo.dwSectorSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000;
|
||||||
CreateInfo.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0;
|
CreateInfo.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0;
|
||||||
CreateInfo.dwMaxFileCount = dwMaxFileCount;
|
CreateInfo.dwMaxFileCount = dwMaxFileCount;
|
||||||
|
|
||||||
|
// Set the proper attribute parts
|
||||||
|
if((CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) && (dwCreateFlags & MPQ_CREATE_ATTRIBUTES))
|
||||||
|
CreateInfo.dwAttrFlags |= MPQ_ATTRIBUTE_PATCH_BIT;
|
||||||
|
|
||||||
|
// Backward compatibility: SFileCreateArchive always used to add (listfile)
|
||||||
|
// We would break loads of applications if we change that
|
||||||
|
CreateInfo.dwFileFlags1 = MPQ_FILE_EXISTS;
|
||||||
|
|
||||||
|
// Let the main function create the archive
|
||||||
return SFileCreateArchive2(szMpqName, &CreateInfo, phMpq);
|
return SFileCreateArchive2(szMpqName, &CreateInfo, phMpq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,11 +102,13 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea
|
||||||
{
|
{
|
||||||
TFileStream * pStream = NULL; // File stream
|
TFileStream * pStream = NULL; // File stream
|
||||||
TMPQArchive * ha = NULL; // MPQ archive handle
|
TMPQArchive * ha = NULL; // MPQ archive handle
|
||||||
|
TMPQHeader * pHeader;
|
||||||
ULONGLONG MpqPos = 0; // Position of MPQ header in the file
|
ULONGLONG MpqPos = 0; // Position of MPQ header in the file
|
||||||
HANDLE hMpq = NULL;
|
HANDLE hMpq = NULL;
|
||||||
DWORD dwBlockTableSize = 0; // Initial block table size
|
DWORD dwBlockTableSize = 0; // Initial block table size
|
||||||
DWORD dwHashTableSize = 0;
|
DWORD dwHashTableSize = 0;
|
||||||
DWORD dwMaxFileCount;
|
DWORD dwReservedFiles = 0; // Number of reserved file entries
|
||||||
|
DWORD dwMpqFlags = 0;
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Check the parameters, if they are valid
|
// Check the parameters, if they are valid
|
||||||
|
|
@ -101,13 +119,12 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify if all variables in SFILE_CREATE_MPQ are correct
|
// Verify if all variables in SFILE_CREATE_MPQ are correct
|
||||||
if((pCreateInfo->cbSize == 0 || pCreateInfo->cbSize > sizeof(SFILE_CREATE_MPQ)) ||
|
if((pCreateInfo->cbSize == 0 || pCreateInfo->cbSize > sizeof(SFILE_CREATE_MPQ)) ||
|
||||||
(pCreateInfo->dwMpqVersion > MPQ_FORMAT_VERSION_4) ||
|
(pCreateInfo->dwMpqVersion > MPQ_FORMAT_VERSION_4) ||
|
||||||
(pCreateInfo->pvUserData != NULL || pCreateInfo->cbUserData != 0) ||
|
(pCreateInfo->pvUserData != NULL || pCreateInfo->cbUserData != 0) ||
|
||||||
(pCreateInfo->dwAttrFlags & ~MPQ_ATTRIBUTE_ALL) ||
|
(pCreateInfo->dwAttrFlags & ~MPQ_ATTRIBUTE_ALL) ||
|
||||||
(pCreateInfo->dwSectorSize & (pCreateInfo->dwSectorSize - 1)) ||
|
(pCreateInfo->dwSectorSize & (pCreateInfo->dwSectorSize - 1)) ||
|
||||||
(pCreateInfo->dwRawChunkSize & (pCreateInfo->dwRawChunkSize - 1)) ||
|
(pCreateInfo->dwRawChunkSize & (pCreateInfo->dwRawChunkSize - 1)))
|
||||||
(pCreateInfo->dwMaxFileCount < 4))
|
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -118,7 +135,7 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea
|
||||||
|
|
||||||
// We verify if the file already exists and if it's a MPQ archive.
|
// We verify if the file already exists and if it's a MPQ archive.
|
||||||
// If yes, we won't allow to overwrite it.
|
// If yes, we won't allow to overwrite it.
|
||||||
if(SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE | MPQ_OPEN_NO_ATTRIBUTES | MPQ_OPEN_NO_LISTFILE, &hMpq))
|
if(SFileOpenArchive(szMpqName, 0, STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE | MPQ_OPEN_NO_ATTRIBUTES | MPQ_OPEN_NO_LISTFILE, &hMpq))
|
||||||
{
|
{
|
||||||
SFileCloseArchive(hMpq);
|
SFileCloseArchive(hMpq);
|
||||||
SetLastError(ERROR_ALREADY_EXISTS);
|
SetLastError(ERROR_ALREADY_EXISTS);
|
||||||
|
|
@ -139,15 +156,29 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment the maximum amount of files to have space
|
// Increment the maximum amount of files to have space for (listfile)
|
||||||
// for listfile and attributes file
|
if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags1)
|
||||||
dwMaxFileCount = pCreateInfo->dwMaxFileCount;
|
{
|
||||||
if(pCreateInfo->dwAttrFlags != 0)
|
dwMpqFlags |= MPQ_FLAG_LISTFILE_INVALID;
|
||||||
dwMaxFileCount++;
|
dwReservedFiles++;
|
||||||
dwMaxFileCount++;
|
}
|
||||||
|
|
||||||
|
// Increment the maximum amount of files to have space for (attributes)
|
||||||
|
if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags2 && pCreateInfo->dwAttrFlags)
|
||||||
|
{
|
||||||
|
dwMpqFlags |= MPQ_FLAG_ATTRIBUTES_INVALID;
|
||||||
|
dwReservedFiles++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the maximum amount of files to have space for (signature)
|
||||||
|
if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags3)
|
||||||
|
{
|
||||||
|
dwMpqFlags |= MPQ_FLAG_SIGNATURE_INVALID;
|
||||||
|
dwReservedFiles++;
|
||||||
|
}
|
||||||
|
|
||||||
// If file count is not zero, initialize the hash table size
|
// If file count is not zero, initialize the hash table size
|
||||||
dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount);
|
dwHashTableSize = GetHashTableSizeForFileCount(pCreateInfo->dwMaxFileCount + dwReservedFiles);
|
||||||
|
|
||||||
// Retrieve the file size and round it up to 0x200 bytes
|
// Retrieve the file size and round it up to 0x200 bytes
|
||||||
FileStream_GetSize(pStream, &MpqPos);
|
FileStream_GetSize(pStream, &MpqPos);
|
||||||
|
|
@ -155,9 +186,9 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea
|
||||||
if(!FileStream_SetSize(pStream, MpqPos))
|
if(!FileStream_SetSize(pStream, MpqPos))
|
||||||
nError = GetLastError();
|
nError = GetLastError();
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
// Debug code, used for testing StormLib
|
// Debug code, used for testing StormLib
|
||||||
// dwBlockTableSize = dwHashTableSize * 2;
|
// dwBlockTableSize = dwHashTableSize * 2;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Create the archive handle
|
// Create the archive handle
|
||||||
|
|
@ -171,26 +202,21 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
memset(ha, 0, sizeof(TMPQArchive));
|
memset(ha, 0, sizeof(TMPQArchive));
|
||||||
|
ha->pfnHashString = HashString;
|
||||||
ha->pStream = pStream;
|
ha->pStream = pStream;
|
||||||
ha->dwSectorSize = pCreateInfo->dwSectorSize;
|
ha->dwSectorSize = pCreateInfo->dwSectorSize;
|
||||||
ha->UserDataPos = MpqPos;
|
ha->UserDataPos = MpqPos;
|
||||||
ha->MpqPos = MpqPos;
|
ha->MpqPos = MpqPos;
|
||||||
ha->pHeader = (TMPQHeader *)ha->HeaderData;
|
ha->pHeader = pHeader = (TMPQHeader *)ha->HeaderData;
|
||||||
ha->dwMaxFileCount = dwMaxFileCount;
|
ha->dwMaxFileCount = dwHashTableSize;
|
||||||
ha->dwFileTableSize = 0;
|
ha->dwFileTableSize = 0;
|
||||||
|
ha->dwReservedFiles = dwReservedFiles;
|
||||||
ha->dwFileFlags1 = pCreateInfo->dwFileFlags1;
|
ha->dwFileFlags1 = pCreateInfo->dwFileFlags1;
|
||||||
ha->dwFileFlags2 = pCreateInfo->dwFileFlags2;
|
ha->dwFileFlags2 = pCreateInfo->dwFileFlags2;
|
||||||
ha->dwFlags = 0;
|
ha->dwFileFlags3 = pCreateInfo->dwFileFlags3 ? MPQ_FILE_EXISTS : 0;
|
||||||
|
|
||||||
// Setup the attributes
|
|
||||||
ha->dwAttrFlags = pCreateInfo->dwAttrFlags;
|
ha->dwAttrFlags = pCreateInfo->dwAttrFlags;
|
||||||
|
ha->dwFlags = dwMpqFlags | MPQ_FLAG_CHANGED;
|
||||||
pStream = NULL;
|
pStream = NULL;
|
||||||
}
|
|
||||||
|
|
||||||
// Fill the MPQ header
|
|
||||||
if(nError == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
TMPQHeader * pHeader = ha->pHeader;
|
|
||||||
|
|
||||||
// Fill the MPQ header
|
// Fill the MPQ header
|
||||||
memset(pHeader, 0, sizeof(ha->HeaderData));
|
memset(pHeader, 0, sizeof(ha->HeaderData));
|
||||||
|
|
@ -211,27 +237,24 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea
|
||||||
|
|
||||||
// Write the naked MPQ header
|
// Write the naked MPQ header
|
||||||
nError = WriteNakedMPQHeader(ha);
|
nError = WriteNakedMPQHeader(ha);
|
||||||
|
|
||||||
// Remember that the (listfile) and (attributes) need to be saved
|
|
||||||
ha->dwFlags |= MPQ_FLAG_CHANGED | MPQ_FLAG_INV_LISTFILE | MPQ_FLAG_INV_ATTRIBUTES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create initial HET table, if the caller required an MPQ format 3.0 or newer
|
// Create initial HET table, if the caller required an MPQ format 3.0 or newer
|
||||||
if(nError == ERROR_SUCCESS && pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_3)
|
if(nError == ERROR_SUCCESS && pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_3 && pCreateInfo->dwMaxFileCount != 0)
|
||||||
{
|
{
|
||||||
ha->pHetTable = CreateHetTable(ha->dwMaxFileCount, 0x40, true);
|
ha->pHetTable = CreateHetTable(ha->dwFileTableSize, 0, 0x40, NULL);
|
||||||
if(ha->pHetTable == NULL)
|
if(ha->pHetTable == NULL)
|
||||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create initial hash table
|
// Create initial hash table
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS && dwHashTableSize != 0)
|
||||||
{
|
{
|
||||||
nError = CreateHashTable(ha, dwHashTableSize);
|
nError = CreateHashTable(ha, dwHashTableSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create initial file table
|
// Create initial file table
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS && ha->dwMaxFileCount != 0)
|
||||||
{
|
{
|
||||||
ha->pFileTable = STORM_ALLOC(TFileEntry, ha->dwMaxFileCount);
|
ha->pFileTable = STORM_ALLOC(TFileEntry, ha->dwMaxFileCount);
|
||||||
if(ha->pFileTable != NULL)
|
if(ha->pFileTable != NULL)
|
||||||
|
|
@ -244,11 +267,11 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
FileStream_Close(pStream);
|
FileStream_Close(pStream);
|
||||||
FreeMPQArchive(ha);
|
FreeArchiveHandle(ha);
|
||||||
SetLastError(nError);
|
SetLastError(nError);
|
||||||
ha = NULL;
|
ha = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the values
|
// Return the values
|
||||||
*phMpq = (HANDLE)ha;
|
*phMpq = (HANDLE)ha;
|
||||||
return (nError == ERROR_SUCCESS);
|
return (nError == ERROR_SUCCESS);
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR
|
||||||
// Create the local file
|
// Create the local file
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
pLocalFile = FileStream_CreateFile(szExtracted, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
|
pLocalFile = FileStream_CreateFile(szExtracted, 0);
|
||||||
if(pLocalFile == NULL)
|
if(pLocalFile == NULL)
|
||||||
nError = GetLastError();
|
nError = GetLastError();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,14 @@ struct TMPQSearch
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Local functions
|
// Local functions
|
||||||
|
|
||||||
static bool IsValidSearchHandle(TMPQSearch * hs)
|
static TMPQSearch * IsValidSearchHandle(HANDLE hFind)
|
||||||
{
|
{
|
||||||
if(hs == NULL)
|
TMPQSearch * hs = (TMPQSearch *)hFind;
|
||||||
return false;
|
|
||||||
|
|
||||||
return IsValidMpqHandle(hs->ha);
|
if(hs != NULL && IsValidMpqHandle(hs->ha))
|
||||||
|
return hs;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckWildCard(const char * szString, const char * szWildCard)
|
bool CheckWildCard(const char * szString, const char * szWildCard)
|
||||||
|
|
@ -69,7 +71,7 @@ bool CheckWildCard(const char * szString, const char * szWildCard)
|
||||||
szString++;
|
szString++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is '*', means zero or more chars. We have to
|
// If there is '*', means zero or more chars. We have to
|
||||||
// find the sequence after '*'
|
// find the sequence after '*'
|
||||||
if(*szWildCard == '*')
|
if(*szWildCard == '*')
|
||||||
{
|
{
|
||||||
|
|
@ -95,7 +97,7 @@ bool CheckWildCard(const char * szString, const char * szWildCard)
|
||||||
// Calculate match count
|
// Calculate match count
|
||||||
while(nMatchCount < nSubStringLength)
|
while(nMatchCount < nSubStringLength)
|
||||||
{
|
{
|
||||||
if(toupper(szString[nMatchCount]) != toupper(szWildCard[nMatchCount]))
|
if(AsciiToUpperTable[(BYTE)szString[nMatchCount]] != AsciiToUpperTable[(BYTE)szWildCard[nMatchCount]])
|
||||||
break;
|
break;
|
||||||
if(szString[nMatchCount] == 0)
|
if(szString[nMatchCount] == 0)
|
||||||
break;
|
break;
|
||||||
|
|
@ -118,7 +120,7 @@ bool CheckWildCard(const char * szString, const char * szWildCard)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If we came to the end of the string, compare it to the wildcard
|
// If we came to the end of the string, compare it to the wildcard
|
||||||
if(toupper(*szString) != toupper(*szWildCard))
|
if(AsciiToUpperTable[(BYTE)*szString] != AsciiToUpperTable[(BYTE)*szWildCard])
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// If we arrived to the end of the string, it's a match
|
// If we arrived to the end of the string, it's a match
|
||||||
|
|
@ -140,7 +142,7 @@ static DWORD GetSearchTableItems(TMPQArchive * ha)
|
||||||
while(ha != NULL)
|
while(ha != NULL)
|
||||||
{
|
{
|
||||||
// Append the number of files
|
// Append the number of files
|
||||||
dwMergeItems += (ha->pHetTable != NULL) ? ha->pHetTable->dwMaxFileCount
|
dwMergeItems += (ha->pHetTable != NULL) ? ha->pHetTable->dwEntryCount
|
||||||
: ha->pHeader->dwBlockTableSize;
|
: ha->pHeader->dwBlockTableSize;
|
||||||
// Move to the patched archive
|
// Move to the patched archive
|
||||||
ha = ha->haPatch;
|
ha = ha->haPatch;
|
||||||
|
|
@ -151,9 +153,9 @@ static DWORD GetSearchTableItems(TMPQArchive * ha)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool FileWasFoundBefore(
|
static bool FileWasFoundBefore(
|
||||||
TMPQArchive * ha,
|
TMPQArchive * ha,
|
||||||
TMPQSearch * hs,
|
TMPQSearch * hs,
|
||||||
TFileEntry * pFileEntry)
|
TFileEntry * pFileEntry)
|
||||||
{
|
{
|
||||||
TFileEntry * pEntry;
|
TFileEntry * pEntry;
|
||||||
char * szRealFileName = pFileEntry->szFileName;
|
char * szRealFileName = pFileEntry->szFileName;
|
||||||
|
|
@ -165,14 +167,14 @@ static bool FileWasFoundBefore(
|
||||||
{
|
{
|
||||||
// If we are in patch MPQ, we check if patch prefix matches
|
// If we are in patch MPQ, we check if patch prefix matches
|
||||||
// and then trim the patch prefix
|
// and then trim the patch prefix
|
||||||
if(ha->cchPatchPrefix != 0)
|
if(ha->pPatchPrefix != NULL)
|
||||||
{
|
{
|
||||||
// If the patch prefix doesn't fit, we pretend that the file
|
// If the patch prefix doesn't fit, we pretend that the file
|
||||||
// was there before and it will be skipped
|
// was there before and it will be skipped
|
||||||
if(_strnicmp(szRealFileName, ha->szPatchPrefix, ha->cchPatchPrefix))
|
if(_strnicmp(szRealFileName, ha->pPatchPrefix->szPatchPrefix, ha->pPatchPrefix->nLength))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
szRealFileName += ha->cchPatchPrefix;
|
szRealFileName += ha->pPatchPrefix->nLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the hash to the table
|
// Calculate the hash to the table
|
||||||
|
|
@ -211,6 +213,14 @@ static bool FileWasFoundBefore(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool FileEntryIsInvalid(
|
||||||
|
TMPQArchive * ha,
|
||||||
|
TFileEntry * pFileEntry)
|
||||||
|
{
|
||||||
|
// Spazzler3 protector: Some files are clearly wrong
|
||||||
|
return ((ha->dwFlags & MPQ_FLAG_MALFORMED) && (pFileEntry->dwCmpSize & 0xFFFF0000) >= 0x7FFF0000);
|
||||||
|
}
|
||||||
|
|
||||||
static TFileEntry * FindPatchEntry(TMPQArchive * ha, TFileEntry * pFileEntry)
|
static TFileEntry * FindPatchEntry(TMPQArchive * ha, TFileEntry * pFileEntry)
|
||||||
{
|
{
|
||||||
TFileEntry * pPatchEntry = NULL;
|
TFileEntry * pPatchEntry = NULL;
|
||||||
|
|
@ -223,9 +233,11 @@ static TFileEntry * FindPatchEntry(TMPQArchive * ha, TFileEntry * pFileEntry)
|
||||||
{
|
{
|
||||||
// Move to the patch archive
|
// Move to the patch archive
|
||||||
ha = ha->haPatch;
|
ha = ha->haPatch;
|
||||||
|
szFileName[0] = 0;
|
||||||
|
|
||||||
// Prepare the prefix for the file name
|
// Prepare the prefix for the file name
|
||||||
strcpy(szFileName, ha->szPatchPrefix);
|
if(ha->pPatchPrefix != NULL)
|
||||||
|
strcpy(szFileName, ha->pPatchPrefix->szPatchPrefix);
|
||||||
strcat(szFileName, pFileEntry->szFileName);
|
strcat(szFileName, pFileEntry->szFileName);
|
||||||
|
|
||||||
// Try to find the file there
|
// Try to find the file there
|
||||||
|
|
@ -259,7 +271,7 @@ static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
|
||||||
pFileEntry = ha->pFileTable + hs->dwNextIndex;
|
pFileEntry = ha->pFileTable + hs->dwNextIndex;
|
||||||
|
|
||||||
// Get the length of the patch prefix (0 if none)
|
// Get the length of the patch prefix (0 if none)
|
||||||
nPrefixLength = strlen(ha->szPatchPrefix);
|
nPrefixLength = (ha->pPatchPrefix != NULL) ? ha->pPatchPrefix->nLength : 0;
|
||||||
|
|
||||||
// Parse the file table
|
// Parse the file table
|
||||||
while(pFileEntry < pFileTableEnd)
|
while(pFileEntry < pFileTableEnd)
|
||||||
|
|
@ -267,61 +279,77 @@ static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
|
||||||
// Increment the next index for subsequent search
|
// Increment the next index for subsequent search
|
||||||
hs->dwNextIndex++;
|
hs->dwNextIndex++;
|
||||||
|
|
||||||
// Is it a file and not a patch file?
|
// Is it a file but not a patch file?
|
||||||
if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS)
|
if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS)
|
||||||
{
|
{
|
||||||
// Now we have to check if this file was not enumerated before
|
// Spazzler3 protector: Some files are clearly wrong
|
||||||
if(!FileWasFoundBefore(ha, hs, pFileEntry))
|
if(!FileEntryIsInvalid(ha, pFileEntry))
|
||||||
{
|
{
|
||||||
// Find a patch to this file
|
// Now we have to check if this file was not enumerated before
|
||||||
pPatchEntry = FindPatchEntry(ha, pFileEntry);
|
if(!FileWasFoundBefore(ha, hs, pFileEntry))
|
||||||
if(pPatchEntry == NULL)
|
|
||||||
pPatchEntry = pFileEntry;
|
|
||||||
|
|
||||||
// Prepare the block index
|
|
||||||
dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable);
|
|
||||||
|
|
||||||
// Get the file name. If it's not known, we will create pseudo-name
|
|
||||||
szFileName = pFileEntry->szFileName;
|
|
||||||
if(szFileName == NULL)
|
|
||||||
{
|
{
|
||||||
// Open the file by its pseudo-name.
|
// if(pFileEntry != NULL && !_stricmp(pFileEntry->szFileName, "TriggerLibs\\NativeLib.galaxy"))
|
||||||
// This also generates the file name with a proper extension
|
// DebugBreak();
|
||||||
sprintf(szPseudoName, "File%08u.xxx", dwBlockIndex);
|
|
||||||
if(SFileOpenFileEx((HANDLE)hs->ha, szPseudoName, SFILE_OPEN_FROM_MPQ, &hFile))
|
// Find a patch to this file
|
||||||
|
pPatchEntry = FindPatchEntry(ha, pFileEntry);
|
||||||
|
if(pPatchEntry == NULL)
|
||||||
|
pPatchEntry = pFileEntry;
|
||||||
|
|
||||||
|
// Prepare the block index
|
||||||
|
dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable);
|
||||||
|
|
||||||
|
// Get the file name. If it's not known, we will create pseudo-name
|
||||||
|
szFileName = pFileEntry->szFileName;
|
||||||
|
if(szFileName == NULL)
|
||||||
{
|
{
|
||||||
szFileName = (pFileEntry->szFileName != NULL) ? pFileEntry->szFileName : szPseudoName;
|
// Open the file by its pseudo-name.
|
||||||
SFileCloseFile(hFile);
|
// This also generates the file name with a proper extension
|
||||||
|
sprintf(szPseudoName, "File%08u.xxx", (unsigned int)dwBlockIndex);
|
||||||
|
if(SFileOpenFileEx((HANDLE)hs->ha, szPseudoName, SFILE_OPEN_BASE_FILE, &hFile))
|
||||||
|
{
|
||||||
|
szFileName = (pFileEntry->szFileName != NULL) ? pFileEntry->szFileName : szPseudoName;
|
||||||
|
SFileCloseFile(hFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the file name is still NULL, we cannot include the file to search results
|
||||||
|
if(szFileName != NULL)
|
||||||
|
{
|
||||||
|
// Check the file name against the wildcard
|
||||||
|
if(CheckWildCard(szFileName + nPrefixLength, hs->szSearchMask))
|
||||||
|
{
|
||||||
|
// Fill the found entry. hash entry and block index are taken from the base MPQ
|
||||||
|
lpFindFileData->dwHashIndex = pFileEntry->dwHashIndex;
|
||||||
|
lpFindFileData->dwBlockIndex = dwBlockIndex;
|
||||||
|
lpFindFileData->dwFileSize = pPatchEntry->dwFileSize;
|
||||||
|
lpFindFileData->dwFileFlags = pPatchEntry->dwFlags;
|
||||||
|
lpFindFileData->dwCompSize = pPatchEntry->dwCmpSize;
|
||||||
|
lpFindFileData->lcLocale = pPatchEntry->lcLocale;
|
||||||
|
|
||||||
|
// Fill the filetime
|
||||||
|
lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32);
|
||||||
|
lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime);
|
||||||
|
|
||||||
|
// Fill the file name and plain file name
|
||||||
|
strcpy(lpFindFileData->cFileName, szFileName + nPrefixLength);
|
||||||
|
lpFindFileData->szPlainName = (char *)GetPlainFileName(lpFindFileData->cFileName);
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the file name against the wildcard
|
|
||||||
if(CheckWildCard(szFileName + nPrefixLength, hs->szSearchMask))
|
|
||||||
{
|
|
||||||
// Fill the found entry
|
|
||||||
lpFindFileData->dwHashIndex = pPatchEntry->dwHashIndex;
|
|
||||||
lpFindFileData->dwBlockIndex = dwBlockIndex;
|
|
||||||
lpFindFileData->dwFileSize = pPatchEntry->dwFileSize;
|
|
||||||
lpFindFileData->dwFileFlags = pPatchEntry->dwFlags;
|
|
||||||
lpFindFileData->dwCompSize = pPatchEntry->dwCmpSize;
|
|
||||||
lpFindFileData->lcLocale = pPatchEntry->lcLocale;
|
|
||||||
|
|
||||||
// Fill the filetime
|
|
||||||
lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32);
|
|
||||||
lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime);
|
|
||||||
|
|
||||||
// Fill the file name and plain file name
|
|
||||||
strcpy(lpFindFileData->cFileName, szFileName + nPrefixLength);
|
|
||||||
lpFindFileData->szPlainName = (char *)GetPlainFileNameA(lpFindFileData->cFileName);
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pFileEntry++;
|
pFileEntry++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is no more patches in the chain, stop it.
|
||||||
|
// This also keeps hs->ha non-NULL, which is required
|
||||||
|
// for freeing the handle later
|
||||||
|
if(ha->haPatch == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
// Move to the next patch in the patch chain
|
// Move to the next patch in the patch chain
|
||||||
hs->ha = ha = ha->haPatch;
|
hs->ha = ha = ha->haPatch;
|
||||||
hs->dwNextIndex = 0;
|
hs->dwNextIndex = 0;
|
||||||
|
|
@ -353,7 +381,7 @@ HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DA
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Check for the valid parameters
|
// Check for the valid parameters
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
nError = ERROR_INVALID_HANDLE;
|
nError = ERROR_INVALID_HANDLE;
|
||||||
if(szMask == NULL || lpFindFileData == NULL)
|
if(szMask == NULL || lpFindFileData == NULL)
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
|
|
@ -406,18 +434,18 @@ HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DA
|
||||||
FreeMPQSearch(hs);
|
FreeMPQSearch(hs);
|
||||||
SetLastError(nError);
|
SetLastError(nError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the result value
|
// Return the result value
|
||||||
return (HANDLE)hs;
|
return (HANDLE)hs;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
|
bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
|
||||||
{
|
{
|
||||||
TMPQSearch * hs = (TMPQSearch *)hFind;
|
TMPQSearch * hs = IsValidSearchHandle(hFind);
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Check the parameters
|
// Check the parameters
|
||||||
if(!IsValidSearchHandle(hs))
|
if(hs == NULL)
|
||||||
nError = ERROR_INVALID_HANDLE;
|
nError = ERROR_INVALID_HANDLE;
|
||||||
if(lpFindFileData == NULL)
|
if(lpFindFileData == NULL)
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
|
|
@ -432,10 +460,10 @@ bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
|
||||||
|
|
||||||
bool WINAPI SFileFindClose(HANDLE hFind)
|
bool WINAPI SFileFindClose(HANDLE hFind)
|
||||||
{
|
{
|
||||||
TMPQSearch * hs = (TMPQSearch *)hFind;
|
TMPQSearch * hs = IsValidSearchHandle(hFind);
|
||||||
|
|
||||||
// Check the parameters
|
// Check the parameters
|
||||||
if(!IsValidSearchHandle(hs))
|
if(hs == NULL)
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_INVALID_HANDLE);
|
SetLastError(ERROR_INVALID_HANDLE);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
1001
dep/StormLib/src/SFileGetFileInfo.cpp
Normal file
1001
dep/StormLib/src/SFileGetFileInfo.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -21,85 +21,85 @@
|
||||||
struct TListFileCache
|
struct TListFileCache
|
||||||
{
|
{
|
||||||
HANDLE hFile; // Stormlib file handle
|
HANDLE hFile; // Stormlib file handle
|
||||||
char * szMask; // File mask
|
char * szMask; // Self-relative pointer to file mask
|
||||||
DWORD dwFileSize; // Total size of the cached file
|
DWORD dwFileSize; // Total size of the cached file
|
||||||
DWORD dwFilePos; // Position of the cache in the file
|
DWORD dwFilePos; // Position of the cache in the file
|
||||||
BYTE * pBegin; // The begin of the listfile cache
|
BYTE * pBegin; // The begin of the listfile cache
|
||||||
BYTE * pPos;
|
BYTE * pPos;
|
||||||
BYTE * pEnd; // The last character in the file cache
|
BYTE * pEnd; // The last character in the file cache
|
||||||
|
|
||||||
BYTE Buffer[CACHE_BUFFER_SIZE]; // Listfile cache itself
|
BYTE Buffer[CACHE_BUFFER_SIZE];
|
||||||
|
// char MaskBuff[1] // Followed by the name mask (if any)
|
||||||
};
|
};
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Local functions (cache)
|
// Local functions (cache)
|
||||||
|
|
||||||
|
static char * CopyListLine(char * szListLine, const char * szFileName)
|
||||||
|
{
|
||||||
|
// Copy the string
|
||||||
|
while(szFileName[0] != 0)
|
||||||
|
*szListLine++ = *szFileName++;
|
||||||
|
|
||||||
|
// Append the end-of-line
|
||||||
|
*szListLine++ = 0x0D;
|
||||||
|
*szListLine++ = 0x0A;
|
||||||
|
return szListLine;
|
||||||
|
}
|
||||||
|
|
||||||
static bool FreeListFileCache(TListFileCache * pCache)
|
static bool FreeListFileCache(TListFileCache * pCache)
|
||||||
{
|
{
|
||||||
// Valid parameter check
|
// Valid parameter check
|
||||||
if(pCache == NULL)
|
if(pCache != NULL)
|
||||||
return false;
|
STORM_FREE(pCache);
|
||||||
|
|
||||||
// Free all allocated buffers
|
|
||||||
if(pCache->hFile != NULL)
|
|
||||||
SFileCloseFile(pCache->hFile);
|
|
||||||
if(pCache->szMask != NULL)
|
|
||||||
STORM_FREE(pCache->szMask);
|
|
||||||
STORM_FREE(pCache);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TListFileCache * CreateListFileCache(HANDLE hListFile, const char * szMask)
|
static TListFileCache * CreateListFileCache(HANDLE hListFile, const char * szMask)
|
||||||
{
|
{
|
||||||
TListFileCache * pCache = NULL;
|
TListFileCache * pCache = NULL;
|
||||||
|
size_t nMaskLength = 0;
|
||||||
DWORD dwBytesRead = 0;
|
DWORD dwBytesRead = 0;
|
||||||
int nError = ERROR_SUCCESS;
|
DWORD dwFileSize;
|
||||||
|
|
||||||
|
// Get the amount of bytes that need to be allocated
|
||||||
|
dwFileSize = SFileGetFileSize(hListFile, NULL);
|
||||||
|
if(dwFileSize == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// Append buffer for name mask, if any
|
||||||
|
if(szMask != NULL)
|
||||||
|
nMaskLength = strlen(szMask) + 1;
|
||||||
|
|
||||||
// Allocate cache for one file block
|
// Allocate cache for one file block
|
||||||
pCache = (TListFileCache *)STORM_ALLOC(TListFileCache, 1);
|
pCache = (TListFileCache *)STORM_ALLOC(BYTE, sizeof(TListFileCache) + nMaskLength);
|
||||||
if(pCache == NULL)
|
if(pCache != NULL)
|
||||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
// Clear the entire structure
|
|
||||||
if(nError == ERROR_SUCCESS)
|
|
||||||
{
|
{
|
||||||
memset(pCache, 0, sizeof(TListFileCache));
|
// Clear the entire structure
|
||||||
pCache->hFile = hListFile;
|
memset(pCache, 0, sizeof(TListFileCache) + nMaskLength);
|
||||||
|
|
||||||
// Shall we allocate a mask?
|
// Shall we copy the mask?
|
||||||
if(szMask != NULL)
|
if(szMask != NULL)
|
||||||
{
|
{
|
||||||
pCache->szMask = STORM_ALLOC(char, strlen(szMask) + 1);
|
pCache->szMask = (char *)(pCache + 1);
|
||||||
if(pCache->szMask != NULL)
|
memcpy(pCache->szMask, szMask, nMaskLength);
|
||||||
strcpy(pCache->szMask, szMask);
|
|
||||||
else
|
|
||||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the file cache
|
// Load the file cache from the file
|
||||||
if(nError == ERROR_SUCCESS)
|
SFileReadFile(hListFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL);
|
||||||
{
|
if(dwBytesRead != 0)
|
||||||
pCache->dwFileSize = SFileGetFileSize(pCache->hFile, NULL);
|
{
|
||||||
|
// Allocate pointers
|
||||||
// Fill the cache
|
pCache->pBegin = pCache->pPos = &pCache->Buffer[0];
|
||||||
SFileReadFile(pCache->hFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL);
|
pCache->pEnd = pCache->pBegin + dwBytesRead;
|
||||||
if(dwBytesRead == 0)
|
pCache->dwFileSize = dwFileSize;
|
||||||
nError = GetLastError();
|
pCache->hFile = hListFile;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Allocate pointers
|
{
|
||||||
if(nError == ERROR_SUCCESS)
|
FreeListFileCache(pCache);
|
||||||
{
|
pCache = NULL;
|
||||||
pCache->pBegin =
|
}
|
||||||
pCache->pPos = &pCache->Buffer[0];
|
|
||||||
pCache->pEnd = pCache->pBegin + dwBytesRead;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FreeListFileCache(pCache);
|
|
||||||
SetLastError(nError);
|
|
||||||
pCache = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the cache
|
// Return the cache
|
||||||
|
|
@ -116,8 +116,6 @@ static DWORD ReloadListFileCache(TListFileCache * pCache)
|
||||||
// Only do something if the cache is empty
|
// Only do something if the cache is empty
|
||||||
if(pCache->pPos >= pCache->pEnd)
|
if(pCache->pPos >= pCache->pEnd)
|
||||||
{
|
{
|
||||||
// __TryReadBlock:
|
|
||||||
|
|
||||||
// Move the file position forward
|
// Move the file position forward
|
||||||
pCache->dwFilePos += CACHE_BUFFER_SIZE;
|
pCache->dwFilePos += CACHE_BUFFER_SIZE;
|
||||||
if(pCache->dwFilePos >= pCache->dwFileSize)
|
if(pCache->dwFilePos >= pCache->dwFileSize)
|
||||||
|
|
@ -130,7 +128,7 @@ static DWORD ReloadListFileCache(TListFileCache * pCache)
|
||||||
|
|
||||||
// Load the next data chunk to the cache
|
// Load the next data chunk to the cache
|
||||||
SFileSetFilePointer(pCache->hFile, pCache->dwFilePos, NULL, FILE_BEGIN);
|
SFileSetFilePointer(pCache->hFile, pCache->dwFilePos, NULL, FILE_BEGIN);
|
||||||
SFileReadFile(pCache->hFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL);
|
SFileReadFile(pCache->hFile, pCache->Buffer, dwBytesToRead, &dwBytesRead, NULL);
|
||||||
|
|
||||||
// If we didn't read anything, it might mean that the block
|
// If we didn't read anything, it might mean that the block
|
||||||
// of the file is not available (in case of partial MPQs).
|
// of the file is not available (in case of partial MPQs).
|
||||||
|
|
@ -141,7 +139,7 @@ static DWORD ReloadListFileCache(TListFileCache * pCache)
|
||||||
|
|
||||||
// Set the buffer pointers
|
// Set the buffer pointers
|
||||||
pCache->pBegin =
|
pCache->pBegin =
|
||||||
pCache->pPos = &pCache->Buffer[0];
|
pCache->pPos = &pCache->Buffer[0];
|
||||||
pCache->pEnd = pCache->pBegin + dwBytesRead;
|
pCache->pEnd = pCache->pBegin + dwBytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,7 +151,7 @@ static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, int nMaxC
|
||||||
char * szLineBegin = szLine;
|
char * szLineBegin = szLine;
|
||||||
char * szLineEnd = szLine + nMaxChars - 1;
|
char * szLineEnd = szLine + nMaxChars - 1;
|
||||||
char * szExtraString = NULL;
|
char * szExtraString = NULL;
|
||||||
|
|
||||||
// Skip newlines, spaces, tabs and another non-printable stuff
|
// Skip newlines, spaces, tabs and another non-printable stuff
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
|
|
@ -191,6 +189,10 @@ static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, int nMaxC
|
||||||
if(*pCache->pPos == '~')
|
if(*pCache->pPos == '~')
|
||||||
szExtraString = szLine;
|
szExtraString = szLine;
|
||||||
|
|
||||||
|
// Remember that last occurence of a slash or backslash
|
||||||
|
// if(*pCache->pPos == '\\' || *pCache->pPos == '/')
|
||||||
|
// szPlainName = szLine + 1;
|
||||||
|
|
||||||
// Copy the character
|
// Copy the character
|
||||||
*szLine++ = *pCache->pPos++;
|
*szLine++ = *pCache->pPos++;
|
||||||
}
|
}
|
||||||
|
|
@ -212,7 +214,7 @@ static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, int nMaxC
|
||||||
return (szLine - szLineBegin);
|
return (szLine - szLineBegin);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int CompareFileNodes(const void * p1, const void * p2)
|
static int CompareFileNodes(const void * p1, const void * p2)
|
||||||
{
|
{
|
||||||
char * szFileName1 = *(char **)p1;
|
char * szFileName1 = *(char **)p1;
|
||||||
char * szFileName2 = *(char **)p2;
|
char * szFileName2 = *(char **)p2;
|
||||||
|
|
@ -220,19 +222,91 @@ static int CompareFileNodes(const void * p1, const void * p2)
|
||||||
return _stricmp(szFileName1, szFileName2);
|
return _stricmp(szFileName1, szFileName2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int WriteListFileLine(
|
static LPBYTE CreateListFile(TMPQArchive * ha, DWORD * pcbListFile)
|
||||||
TMPQFile * hf,
|
|
||||||
const char * szLine)
|
|
||||||
{
|
{
|
||||||
char szNewLine[2] = {0x0D, 0x0A};
|
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||||
size_t nLength = strlen(szLine);
|
TFileEntry * pFileEntry;
|
||||||
int nError;
|
char ** SortTable = NULL;
|
||||||
|
char * szListFile = NULL;
|
||||||
|
char * szListLine;
|
||||||
|
size_t nFileNodes = 0;
|
||||||
|
size_t cbListFile = 0;
|
||||||
|
size_t nIndex0;
|
||||||
|
size_t nIndex1;
|
||||||
|
|
||||||
nError = SFileAddFile_Write(hf, szLine, (DWORD)nLength, MPQ_COMPRESSION_ZLIB);
|
// Allocate the table for sorting listfile
|
||||||
if(nError != ERROR_SUCCESS)
|
SortTable = STORM_ALLOC(char*, ha->dwFileTableSize);
|
||||||
return nError;
|
if(SortTable == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
return SFileAddFile_Write(hf, szNewLine, sizeof(szNewLine), MPQ_COMPRESSION_ZLIB);
|
// Construct the sort table
|
||||||
|
// Note: in MPQs with multiple locale versions of the same file,
|
||||||
|
// this code causes adding multiple listfile entries.
|
||||||
|
// They will get removed after the listfile sorting
|
||||||
|
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||||
|
{
|
||||||
|
// Only take existing items
|
||||||
|
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL)
|
||||||
|
{
|
||||||
|
// Ignore pseudo-names and internal names
|
||||||
|
if(!IsPseudoFileName(pFileEntry->szFileName, NULL) && !IsInternalMpqFileName(pFileEntry->szFileName))
|
||||||
|
{
|
||||||
|
SortTable[nFileNodes++] = pFileEntry->szFileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove duplicities
|
||||||
|
if(nFileNodes > 0)
|
||||||
|
{
|
||||||
|
// Sort the table
|
||||||
|
qsort(SortTable, nFileNodes, sizeof(char *), CompareFileNodes);
|
||||||
|
|
||||||
|
// Count the 0-th item
|
||||||
|
cbListFile += strlen(SortTable[0]) + 2;
|
||||||
|
|
||||||
|
// Walk through the items and only use the ones that are not duplicated
|
||||||
|
for(nIndex0 = 0, nIndex1 = 1; nIndex1 < nFileNodes; nIndex1++)
|
||||||
|
{
|
||||||
|
// If the next file node is different, we will include it to the result listfile
|
||||||
|
if(_stricmp(SortTable[nIndex1], SortTable[nIndex0]) != 0)
|
||||||
|
{
|
||||||
|
cbListFile += strlen(SortTable[nIndex1]) + 2;
|
||||||
|
nIndex0 = nIndex1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now allocate buffer for the entire listfile
|
||||||
|
szListFile = szListLine = STORM_ALLOC(char, cbListFile + 1);
|
||||||
|
if(szListFile != NULL)
|
||||||
|
{
|
||||||
|
// Copy the 0-th item
|
||||||
|
szListLine = CopyListLine(szListLine, SortTable[0]);
|
||||||
|
|
||||||
|
// Walk through the items and only use the ones that are not duplicated
|
||||||
|
for(nIndex0 = 0, nIndex1 = 1; nIndex1 < nFileNodes; nIndex1++)
|
||||||
|
{
|
||||||
|
// If the next file node is different, we will include it to the result listfile
|
||||||
|
if(_stricmp(SortTable[nIndex1], SortTable[nIndex0]) != 0)
|
||||||
|
{
|
||||||
|
// Copy the listfile line
|
||||||
|
szListLine = CopyListLine(szListLine, SortTable[nIndex1]);
|
||||||
|
nIndex0 = nIndex1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check - does the size match?
|
||||||
|
assert((size_t)(szListLine - szListFile) == cbListFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the sort table
|
||||||
|
STORM_FREE(SortTable);
|
||||||
|
|
||||||
|
// Give away the listfile
|
||||||
|
if(pcbListFile != NULL)
|
||||||
|
*pcbListFile = (DWORD)cbListFile;
|
||||||
|
return (LPBYTE)szListFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
@ -247,7 +321,6 @@ static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFil
|
||||||
TFileEntry * pFileEntry;
|
TFileEntry * pFileEntry;
|
||||||
TMPQHash * pFirstHash;
|
TMPQHash * pFirstHash;
|
||||||
TMPQHash * pHash;
|
TMPQHash * pHash;
|
||||||
bool bNameEntryCreated = false;
|
|
||||||
|
|
||||||
// If we have HET table, use that one
|
// If we have HET table, use that one
|
||||||
if(ha->pHetTable != NULL)
|
if(ha->pHetTable != NULL)
|
||||||
|
|
@ -256,15 +329,14 @@ static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFil
|
||||||
if(pFileEntry != NULL)
|
if(pFileEntry != NULL)
|
||||||
{
|
{
|
||||||
// Allocate file name for the file entry
|
// Allocate file name for the file entry
|
||||||
AllocateFileName(pFileEntry, szFileName);
|
AllocateFileName(ha, pFileEntry, szFileName);
|
||||||
bNameEntryCreated = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have hash table, we use it
|
// If we have hash table, we use it
|
||||||
if(bNameEntryCreated == false && ha->pHashTable != NULL)
|
if(ha->pHashTable != NULL)
|
||||||
{
|
{
|
||||||
// Look for the first hash table entry for the file
|
// Look for the first hash table entry for the file
|
||||||
pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
|
pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
|
||||||
|
|
@ -276,8 +348,7 @@ static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFil
|
||||||
if(pHash->dwBlockIndex < pHeader->dwBlockTableSize)
|
if(pHash->dwBlockIndex < pHeader->dwBlockTableSize)
|
||||||
{
|
{
|
||||||
// Allocate file name for the file entry
|
// Allocate file name for the file entry
|
||||||
AllocateFileName(ha->pFileTable + pHash->dwBlockIndex, szFileName);
|
AllocateFileName(ha, ha->pFileTable + pHash->dwBlockIndex, szFileName);
|
||||||
bNameEntryCreated = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now find the next language version of the file
|
// Now find the next language version of the file
|
||||||
|
|
@ -288,123 +359,68 @@ static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFil
|
||||||
return ERROR_CAN_NOT_COMPLETE;
|
return ERROR_CAN_NOT_COMPLETE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saves the whole listfile into the MPQ.
|
// Saves the whole listfile to the MPQ
|
||||||
int SListFileSaveToMpq(TMPQArchive * ha)
|
int SListFileSaveToMpq(TMPQArchive * ha)
|
||||||
{
|
{
|
||||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
|
||||||
TFileEntry * pFileEntry;
|
|
||||||
TMPQFile * hf = NULL;
|
TMPQFile * hf = NULL;
|
||||||
char * szPrevItem;
|
LPBYTE pbListFile;
|
||||||
char ** SortTable = NULL;
|
DWORD cbListFile = 0;
|
||||||
DWORD dwFileSize = 0;
|
|
||||||
size_t nFileNodes = 0;
|
|
||||||
size_t i;
|
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Allocate the table for sorting listfile
|
// Only save the listfile if we should do so
|
||||||
SortTable = STORM_ALLOC(char*, ha->dwFileTableSize);
|
if(ha->dwFileFlags1 != 0)
|
||||||
if(SortTable == NULL)
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
// Construct the sort table
|
|
||||||
// Note: in MPQs with multiple locale versions of the same file,
|
|
||||||
// this code causes adding multiple listfile entries.
|
|
||||||
// Since those MPQs were last time used in Starcraft,
|
|
||||||
// we leave it as it is.
|
|
||||||
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
|
||||||
{
|
{
|
||||||
// Only take existing items
|
// At this point, we expect to have at least one reserved entry in the file table
|
||||||
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL)
|
assert(ha->dwFlags & MPQ_FLAG_LISTFILE_INVALID);
|
||||||
|
assert(ha->dwReservedFiles >= 1);
|
||||||
|
|
||||||
|
// Create the raw data that is to be written to (listfile)
|
||||||
|
// Note: Creating the raw data before the (listfile) has been created in the MPQ
|
||||||
|
// causes that the name of the listfile will not be included in the listfile itself.
|
||||||
|
// That is OK, because (listfile) in Blizzard MPQs does not contain it either.
|
||||||
|
pbListFile = CreateListFile(ha, &cbListFile);
|
||||||
|
if(pbListFile != NULL)
|
||||||
{
|
{
|
||||||
// Ignore pseudo-names
|
// We expect it to be nonzero size
|
||||||
if(!IsPseudoFileName(pFileEntry->szFileName, NULL) && !IsInternalMpqFileName(pFileEntry->szFileName))
|
assert(cbListFile != 0);
|
||||||
|
|
||||||
|
// Determine the real flags for (listfile)
|
||||||
|
if(ha->dwFileFlags1 == MPQ_FILE_EXISTS)
|
||||||
|
ha->dwFileFlags1 = GetDefaultSpecialFileFlags(cbListFile, ha->pHeader->wFormatVersion);
|
||||||
|
|
||||||
|
// Create the listfile in the MPQ
|
||||||
|
nError = SFileAddFile_Init(ha, LISTFILE_NAME,
|
||||||
|
0,
|
||||||
|
cbListFile,
|
||||||
|
LANG_NEUTRAL,
|
||||||
|
ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING,
|
||||||
|
&hf);
|
||||||
|
|
||||||
|
// Write the listfile raw data to it
|
||||||
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
SortTable[nFileNodes++] = pFileEntry->szFileName;
|
// Write the content of the listfile to the MPQ
|
||||||
|
nError = SFileAddFile_Write(hf, pbListFile, cbListFile, MPQ_COMPRESSION_ZLIB);
|
||||||
|
SFileAddFile_Finish(hf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Free the listfile buffer
|
||||||
|
STORM_FREE(pbListFile);
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
|
||||||
// Sort the table
|
|
||||||
qsort(SortTable, nFileNodes, sizeof(char *), CompareFileNodes);
|
|
||||||
|
|
||||||
// Now parse the table of file names again - remove duplicates
|
|
||||||
// and count file size.
|
|
||||||
if(nFileNodes != 0)
|
|
||||||
{
|
|
||||||
// Count the 0-th item
|
|
||||||
dwFileSize += (DWORD)strlen(SortTable[0]) + 2;
|
|
||||||
szPrevItem = SortTable[0];
|
|
||||||
|
|
||||||
// Count all next items
|
|
||||||
for(i = 1; i < nFileNodes; i++)
|
|
||||||
{
|
{
|
||||||
// If the item is the same like the last one, skip it
|
// If the list file is empty, we assume ERROR_SUCCESS
|
||||||
if(_stricmp(SortTable[i], szPrevItem))
|
nError = (cbListFile == 0) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
|
||||||
{
|
|
||||||
dwFileSize += (DWORD)strlen(SortTable[i]) + 2;
|
|
||||||
szPrevItem = SortTable[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the flags for (listfile)
|
// If the save process succeeded, we clear the MPQ_FLAG_LISTFILE_INVALID flag
|
||||||
if(ha->dwFileFlags1 == 0)
|
|
||||||
ha->dwFileFlags1 = GetDefaultSpecialFileFlags(ha, dwFileSize);
|
|
||||||
|
|
||||||
// Create the listfile in the MPQ
|
|
||||||
nError = SFileAddFile_Init(ha, LISTFILE_NAME,
|
|
||||||
0,
|
|
||||||
dwFileSize,
|
|
||||||
LANG_NEUTRAL,
|
|
||||||
ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING,
|
|
||||||
&hf);
|
|
||||||
// Add all file names
|
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// Each name is followed by newline ("\x0D\x0A")
|
ha->dwFlags &= ~MPQ_FLAG_LISTFILE_INVALID;
|
||||||
szPrevItem = SortTable[0];
|
ha->dwReservedFiles--;
|
||||||
nError = WriteListFileLine(hf, SortTable[0]);
|
|
||||||
|
|
||||||
// Count all next items
|
|
||||||
for(i = 1; i < nFileNodes; i++)
|
|
||||||
{
|
|
||||||
// If the item is the same like the last one, skip it
|
|
||||||
if(_stricmp(SortTable[i], szPrevItem))
|
|
||||||
{
|
|
||||||
WriteListFileLine(hf, SortTable[i]);
|
|
||||||
szPrevItem = SortTable[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Create the listfile in the MPQ
|
|
||||||
dwFileSize = (DWORD)strlen(LISTFILE_NAME) + 2;
|
|
||||||
nError = SFileAddFile_Init(ha, LISTFILE_NAME,
|
|
||||||
0,
|
|
||||||
dwFileSize,
|
|
||||||
LANG_NEUTRAL,
|
|
||||||
MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING,
|
|
||||||
&hf);
|
|
||||||
|
|
||||||
// Just add "(listfile)" there
|
|
||||||
if(nError == ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
WriteListFileLine(hf, LISTFILE_NAME);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize the file in the MPQ
|
|
||||||
if(hf != NULL)
|
|
||||||
{
|
|
||||||
SFileAddFile_Finish(hf);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free buffers
|
|
||||||
if(nError == ERROR_SUCCESS)
|
|
||||||
ha->dwFlags &= ~MPQ_FLAG_INV_LISTFILE;
|
|
||||||
if(SortTable != NULL)
|
|
||||||
STORM_FREE(SortTable);
|
|
||||||
return nError;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -415,25 +431,20 @@ static int SFileAddArbitraryListFile(
|
||||||
TListFileCache * pCache = NULL;
|
TListFileCache * pCache = NULL;
|
||||||
size_t nLength;
|
size_t nLength;
|
||||||
char szFileName[MAX_PATH];
|
char szFileName[MAX_PATH];
|
||||||
int nError = ERROR_SUCCESS;
|
|
||||||
|
|
||||||
// Create the listfile cache for that file
|
// Create the listfile cache for that file
|
||||||
pCache = CreateListFileCache(hListFile, NULL);
|
pCache = CreateListFileCache(hListFile, NULL);
|
||||||
if(pCache == NULL)
|
if(pCache != NULL)
|
||||||
nError = GetLastError();
|
|
||||||
|
|
||||||
// Load the node list. Add the node for every locale in the archive
|
|
||||||
if(nError == ERROR_SUCCESS)
|
|
||||||
{
|
{
|
||||||
|
// Load the node list. Add the node for every locale in the archive
|
||||||
while((nLength = ReadListFileLine(pCache, szFileName, sizeof(szFileName))) > 0)
|
while((nLength = ReadListFileLine(pCache, szFileName, sizeof(szFileName))) > 0)
|
||||||
SListFileCreateNodeForAllLocales(ha, szFileName);
|
SListFileCreateNodeForAllLocales(ha, szFileName);
|
||||||
pCache->hFile = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete the cache
|
// Delete the cache
|
||||||
if(pCache != NULL)
|
|
||||||
FreeListFileCache(pCache);
|
FreeListFileCache(pCache);
|
||||||
return nError;
|
}
|
||||||
|
|
||||||
|
return (pCache != NULL) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int SFileAddExternalListFile(
|
static int SFileAddExternalListFile(
|
||||||
|
|
@ -445,12 +456,12 @@ static int SFileAddExternalListFile(
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Open the external list file
|
// Open the external list file
|
||||||
if(SFileOpenFileEx(hMpq, szListFile, SFILE_OPEN_LOCAL_FILE, &hListFile))
|
if(!SFileOpenFileEx(hMpq, szListFile, SFILE_OPEN_LOCAL_FILE, &hListFile))
|
||||||
{
|
return GetLastError();
|
||||||
// Add the data from the listfile to MPQ
|
|
||||||
nError = SFileAddArbitraryListFile(ha, hListFile);
|
// Add the data from the listfile to MPQ
|
||||||
SFileCloseFile(hListFile);
|
nError = SFileAddArbitraryListFile(ha, hListFile);
|
||||||
}
|
SFileCloseFile(hListFile);
|
||||||
return nError;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -480,7 +491,7 @@ static int SFileAddInternalListFile(
|
||||||
nError = SFileAddArbitraryListFile(ha, hListFile);
|
nError = SFileAddArbitraryListFile(ha, hListFile);
|
||||||
SFileCloseFile(hListFile);
|
SFileCloseFile(hListFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore the original locale
|
// Restore the original locale
|
||||||
SFileSetLocale(lcSaveLocale);
|
SFileSetLocale(lcSaveLocale);
|
||||||
|
|
||||||
|
|
@ -517,9 +528,9 @@ int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile)
|
||||||
while(ha != NULL)
|
while(ha != NULL)
|
||||||
{
|
{
|
||||||
if(szListFile != NULL)
|
if(szListFile != NULL)
|
||||||
SFileAddExternalListFile(ha, hMpq, szListFile);
|
nError = SFileAddExternalListFile(ha, hMpq, szListFile);
|
||||||
else
|
else
|
||||||
SFileAddInternalListFile(ha, hMpq);
|
nError = SFileAddInternalListFile(ha, hMpq);
|
||||||
|
|
||||||
// Also, add three special files to the listfile:
|
// Also, add three special files to the listfile:
|
||||||
// (listfile) itself, (attributes) and (signature)
|
// (listfile) itself, (attributes) and (signature)
|
||||||
|
|
@ -540,7 +551,7 @@ int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile)
|
||||||
HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData)
|
HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData)
|
||||||
{
|
{
|
||||||
TListFileCache * pCache = NULL;
|
TListFileCache * pCache = NULL;
|
||||||
HANDLE hListFile;
|
HANDLE hListFile = NULL;
|
||||||
size_t nLength = 0;
|
size_t nLength = 0;
|
||||||
DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE;
|
DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE;
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
@ -566,11 +577,57 @@ HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const
|
||||||
{
|
{
|
||||||
pCache = CreateListFileCache(hListFile, szMask);
|
pCache = CreateListFileCache(hListFile, szMask);
|
||||||
if(pCache == NULL)
|
if(pCache == NULL)
|
||||||
nError = GetLastError();
|
nError = ERROR_FILE_CORRUPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform file search
|
// Perform file search
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
// The listfile handle is in the cache now
|
||||||
|
hListFile = NULL;
|
||||||
|
|
||||||
|
// Iterate through the listfile
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
// Read the (next) line
|
||||||
|
nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
|
||||||
|
if(nLength == 0)
|
||||||
|
{
|
||||||
|
nError = ERROR_NO_MORE_FILES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If some mask entered, check it
|
||||||
|
if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup & exit
|
||||||
|
if(nError != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
if(pCache != NULL)
|
||||||
|
FreeListFileCache(pCache);
|
||||||
|
pCache = NULL;
|
||||||
|
|
||||||
|
memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
|
||||||
|
SetLastError(nError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close remaining unowned listfile handle
|
||||||
|
if(hListFile != NULL)
|
||||||
|
SFileCloseFile(hListFile);
|
||||||
|
return (HANDLE)pCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
|
||||||
|
{
|
||||||
|
TListFileCache * pCache = (TListFileCache *)hFind;
|
||||||
|
size_t nLength;
|
||||||
|
int nError = ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
// Check for parameters
|
||||||
|
if(pCache != NULL)
|
||||||
{
|
{
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
|
|
@ -584,54 +641,27 @@ HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const
|
||||||
|
|
||||||
// If some mask entered, check it
|
// If some mask entered, check it
|
||||||
if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
|
if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
|
||||||
|
{
|
||||||
|
nError = ERROR_SUCCESS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup & exit
|
|
||||||
if(nError != ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
|
|
||||||
FreeListFileCache(pCache);
|
|
||||||
|
|
||||||
SetLastError(nError);
|
|
||||||
pCache = NULL;
|
|
||||||
}
|
|
||||||
return (HANDLE)pCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
|
|
||||||
{
|
|
||||||
TListFileCache * pCache = (TListFileCache *)hFind;
|
|
||||||
size_t nLength;
|
|
||||||
bool bResult = false;
|
|
||||||
int nError = ERROR_SUCCESS;
|
|
||||||
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
// Read the (next) line
|
|
||||||
nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
|
|
||||||
if(nLength == 0)
|
|
||||||
{
|
|
||||||
nError = ERROR_NO_MORE_FILES;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If some mask entered, check it
|
|
||||||
if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
|
|
||||||
{
|
|
||||||
bResult = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
SetLastError(nError);
|
SetLastError(nError);
|
||||||
return bResult;
|
return (nError == ERROR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WINAPI SListFileFindClose(HANDLE hFind)
|
bool WINAPI SListFileFindClose(HANDLE hFind)
|
||||||
{
|
{
|
||||||
return FreeListFileCache((TListFileCache *)hFind);
|
TListFileCache * pCache = (TListFileCache *)hFind;
|
||||||
|
|
||||||
|
if(pCache == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(pCache->hFile != NULL)
|
||||||
|
SFileCloseFile(pCache->hFile);
|
||||||
|
return FreeListFileCache(pCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,43 +21,41 @@
|
||||||
/* Local functions */
|
/* Local functions */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
static bool IsAviFile(void * pvFileBegin)
|
static bool IsAviFile(DWORD * HeaderData)
|
||||||
{
|
{
|
||||||
LPDWORD AviHeader = (DWORD *)pvFileBegin;
|
DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(HeaderData[0]);
|
||||||
DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(AviHeader[0]);
|
DWORD DwordValue2 = BSWAP_INT32_UNSIGNED(HeaderData[2]);
|
||||||
DWORD DwordValue2 = BSWAP_INT32_UNSIGNED(AviHeader[2]);
|
DWORD DwordValue3 = BSWAP_INT32_UNSIGNED(HeaderData[3]);
|
||||||
DWORD DwordValue3 = BSWAP_INT32_UNSIGNED(AviHeader[3]);
|
|
||||||
|
|
||||||
// Test for 'RIFF', 'AVI ' or 'LIST'
|
// Test for 'RIFF', 'AVI ' or 'LIST'
|
||||||
return (DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C);
|
return (DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C);
|
||||||
}
|
}
|
||||||
|
|
||||||
static TFileBitmap * CreateFileBitmap(TMPQArchive * ha, TMPQBitmap * pMpqBitmap, bool bFileIsComplete)
|
static TMPQUserData * IsValidMpqUserData(ULONGLONG ByteOffset, ULONGLONG FileSize, void * pvUserData)
|
||||||
{
|
{
|
||||||
TFileBitmap * pBitmap;
|
TMPQUserData * pUserData;
|
||||||
size_t nLength;
|
|
||||||
|
|
||||||
// Calculate the length of the bitmap in blocks and in bytes
|
// BSWAP the source data and copy them to our buffer
|
||||||
nLength = (size_t)(((ha->pHeader->ArchiveSize64 - 1) / pMpqBitmap->dwBlockSize) + 1);
|
BSWAP_ARRAY32_UNSIGNED(&pvUserData, sizeof(TMPQUserData));
|
||||||
nLength = (size_t)(((nLength - 1) / 8) + 1);
|
pUserData = (TMPQUserData *)pvUserData;
|
||||||
|
|
||||||
// Allocate the file bitmap
|
// Check the sizes
|
||||||
pBitmap = (TFileBitmap *)STORM_ALLOC(BYTE, sizeof(TFileBitmap) + nLength);
|
if(pUserData->cbUserDataHeader <= pUserData->cbUserDataSize && pUserData->cbUserDataSize <= pUserData->dwHeaderOffs)
|
||||||
if(pBitmap != NULL)
|
|
||||||
{
|
{
|
||||||
// Fill the structure
|
// Move to the position given by the userdata
|
||||||
pBitmap->StartOffset = ha->MpqPos;
|
ByteOffset += pUserData->dwHeaderOffs;
|
||||||
pBitmap->EndOffset = ha->MpqPos + ha->pHeader->ArchiveSize64;
|
|
||||||
pBitmap->IsComplete = bFileIsComplete ? 1 : 0;
|
|
||||||
pBitmap->BitmapSize = (DWORD)nLength;
|
|
||||||
pBitmap->BlockSize = pMpqBitmap->dwBlockSize;
|
|
||||||
pBitmap->Reserved = 0;
|
|
||||||
|
|
||||||
// Copy the file bitmap
|
// The MPQ header should be within range of the file size
|
||||||
memcpy((pBitmap + 1), (pMpqBitmap + 1), nLength);
|
if((ByteOffset + MPQ_HEADER_SIZE_V1) < FileSize)
|
||||||
|
{
|
||||||
|
// Note: We should verify if there is the MPQ header.
|
||||||
|
// However, the header could be at any position below that
|
||||||
|
// that is multiplier of 0x200
|
||||||
|
return (TMPQUserData *)pvUserData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pBitmap;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function gets the right positions of the hash table and the block table.
|
// This function gets the right positions of the hash table and the block table.
|
||||||
|
|
@ -86,6 +84,9 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize)
|
||||||
if(pHeader->wHashTablePosHi || pHeader->dwHashTablePos)
|
if(pHeader->wHashTablePosHi || pHeader->dwHashTablePos)
|
||||||
{
|
{
|
||||||
ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos);
|
ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos);
|
||||||
|
if((pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) && (ha->dwFlags & MPQ_FLAG_MALFORMED))
|
||||||
|
ByteOffset = (DWORD)ha->MpqPos + pHeader->dwHashTablePos;
|
||||||
|
|
||||||
if(ByteOffset > FileSize)
|
if(ByteOffset > FileSize)
|
||||||
return ERROR_BAD_FORMAT;
|
return ERROR_BAD_FORMAT;
|
||||||
}
|
}
|
||||||
|
|
@ -94,6 +95,9 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize)
|
||||||
if(pHeader->wBlockTablePosHi || pHeader->dwBlockTablePos)
|
if(pHeader->wBlockTablePosHi || pHeader->dwBlockTablePos)
|
||||||
{
|
{
|
||||||
ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
|
ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
|
||||||
|
if((pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) && (ha->dwFlags & MPQ_FLAG_MALFORMED))
|
||||||
|
ByteOffset = (DWORD)ha->MpqPos + pHeader->dwBlockTablePos;
|
||||||
|
|
||||||
if(ByteOffset > FileSize)
|
if(ByteOffset > FileSize)
|
||||||
return ERROR_BAD_FORMAT;
|
return ERROR_BAD_FORMAT;
|
||||||
}
|
}
|
||||||
|
|
@ -139,16 +143,17 @@ LCID WINAPI SFileSetLocale(LCID lcNewLocale)
|
||||||
// phMpq - Pointer to store open archive handle
|
// phMpq - Pointer to store open archive handle
|
||||||
|
|
||||||
bool WINAPI SFileOpenArchive(
|
bool WINAPI SFileOpenArchive(
|
||||||
const TCHAR * szMpqName,
|
const TCHAR * szMpqName,
|
||||||
DWORD dwPriority,
|
DWORD dwPriority,
|
||||||
DWORD dwFlags,
|
DWORD dwFlags,
|
||||||
HANDLE * phMpq)
|
HANDLE * phMpq)
|
||||||
{
|
{
|
||||||
|
TMPQUserData * pUserData;
|
||||||
TFileStream * pStream = NULL; // Open file stream
|
TFileStream * pStream = NULL; // Open file stream
|
||||||
TMPQArchive * ha = NULL; // Archive handle
|
TMPQArchive * ha = NULL; // Archive handle
|
||||||
TFileEntry * pFileEntry;
|
TFileEntry * pFileEntry;
|
||||||
ULONGLONG FileSize = 0; // Size of the file
|
ULONGLONG FileSize = 0; // Size of the file
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Verify the parameters
|
// Verify the parameters
|
||||||
if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL)
|
if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL)
|
||||||
|
|
@ -161,16 +166,28 @@ bool WINAPI SFileOpenArchive(
|
||||||
// Open the MPQ archive file
|
// Open the MPQ archive file
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
|
DWORD dwStreamFlags = (dwFlags & STREAM_FLAGS_MASK);
|
||||||
|
|
||||||
|
// If not forcing MPQ v 1.0, also use file bitmap
|
||||||
|
dwStreamFlags |= (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) ? 0 : STREAM_FLAG_USE_BITMAP;
|
||||||
|
|
||||||
// Initialize the stream
|
// Initialize the stream
|
||||||
pStream = FileStream_OpenFile(szMpqName, (dwFlags & STREAM_OPTIONS_MASK));
|
pStream = FileStream_OpenFile(szMpqName, dwStreamFlags);
|
||||||
if(pStream == NULL)
|
if(pStream == NULL)
|
||||||
nError = GetLastError();
|
nError = GetLastError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check the file size. There must be at least 0x20 bytes
|
||||||
|
if(nError == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
FileStream_GetSize(pStream, &FileSize);
|
||||||
|
if(FileSize < MPQ_HEADER_SIZE_V1)
|
||||||
|
nError = ERROR_BAD_FORMAT;
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate the MPQhandle
|
// Allocate the MPQhandle
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
FileStream_GetSize(pStream, &FileSize);
|
|
||||||
if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
|
if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
|
||||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
@ -178,98 +195,129 @@ bool WINAPI SFileOpenArchive(
|
||||||
// Initialize handle structure and allocate structure for MPQ header
|
// Initialize handle structure and allocate structure for MPQ header
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
|
ULONGLONG SearchOffset = 0;
|
||||||
|
ULONGLONG EndOfSearch = FileSize;
|
||||||
|
DWORD dwStreamFlags = 0;
|
||||||
|
DWORD dwHeaderSize;
|
||||||
|
DWORD dwHeaderID;
|
||||||
|
|
||||||
memset(ha, 0, sizeof(TMPQArchive));
|
memset(ha, 0, sizeof(TMPQArchive));
|
||||||
|
ha->pfnHashString = HashString;
|
||||||
ha->pStream = pStream;
|
ha->pStream = pStream;
|
||||||
pStream = NULL;
|
pStream = NULL;
|
||||||
|
|
||||||
// Remember if the archive is open for write
|
// Set the archive read only if the stream is read-only
|
||||||
if(FileStream_IsReadOnly(ha->pStream))
|
FileStream_GetFlags(ha->pStream, &dwStreamFlags);
|
||||||
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
|
ha->dwFlags |= (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? MPQ_FLAG_READ_ONLY : 0;
|
||||||
|
|
||||||
// Also remember if we shall check sector CRCs when reading file
|
// Also remember if we shall check sector CRCs when reading file
|
||||||
if(dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC)
|
ha->dwFlags |= (dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC) ? MPQ_FLAG_CHECK_SECTOR_CRC : 0;
|
||||||
ha->dwFlags |= MPQ_FLAG_CHECK_SECTOR_CRC;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the offset of MPQ header within the file
|
// Also remember if this MPQ is a patch
|
||||||
if(nError == ERROR_SUCCESS)
|
ha->dwFlags |= (dwFlags & MPQ_OPEN_PATCH) ? MPQ_FLAG_PATCH : 0;
|
||||||
{
|
|
||||||
ULONGLONG SearchPos = 0;
|
// Limit the header searching to about 130 MB of data
|
||||||
DWORD dwHeaderID;
|
if(EndOfSearch > 0x08000000)
|
||||||
|
EndOfSearch = 0x08000000;
|
||||||
|
|
||||||
while(SearchPos < FileSize)
|
// Find the offset of MPQ header within the file
|
||||||
|
while(SearchOffset < EndOfSearch)
|
||||||
{
|
{
|
||||||
DWORD dwBytesAvailable = MPQ_HEADER_SIZE_V4;
|
DWORD dwBytesAvailable = MPQ_HEADER_SIZE_V4;
|
||||||
|
|
||||||
// Cut the bytes available, if needed
|
// Cut the bytes available, if needed
|
||||||
if((FileSize - SearchPos) < MPQ_HEADER_SIZE_V4)
|
if((FileSize - SearchOffset) < MPQ_HEADER_SIZE_V4)
|
||||||
dwBytesAvailable = (DWORD)(FileSize - SearchPos);
|
dwBytesAvailable = (DWORD)(FileSize - SearchOffset);
|
||||||
|
|
||||||
// Read the eventual MPQ header
|
// Read the eventual MPQ header
|
||||||
if(!FileStream_Read(ha->pStream, &SearchPos, ha->HeaderData, dwBytesAvailable))
|
if(!FileStream_Read(ha->pStream, &SearchOffset, ha->HeaderData, dwBytesAvailable))
|
||||||
{
|
{
|
||||||
nError = GetLastError();
|
nError = GetLastError();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// There are AVI files from Warcraft III with 'MPQ' extension.
|
// There are AVI files from Warcraft III with 'MPQ' extension.
|
||||||
if(SearchPos == 0 && IsAviFile(ha->HeaderData))
|
if(SearchOffset == 0 && IsAviFile(ha->HeaderData))
|
||||||
{
|
{
|
||||||
nError = ERROR_AVI_FILE;
|
nError = ERROR_AVI_FILE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is the MPQ user data signature, process it
|
// If there is the MPQ user data signature, process it
|
||||||
dwHeaderID = BSWAP_INT32_UNSIGNED(*(LPDWORD)ha->HeaderData);
|
dwHeaderID = BSWAP_INT32_UNSIGNED(ha->HeaderData[0]);
|
||||||
if(dwHeaderID == ID_MPQ_USERDATA && ha->pUserData == NULL)
|
if(dwHeaderID == ID_MPQ_USERDATA && ha->pUserData == NULL && (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0)
|
||||||
{
|
{
|
||||||
// Ignore the MPQ user data completely if the caller wants to open the MPQ as V1.0
|
// Verify if this looks like a valid user data
|
||||||
if((dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0)
|
pUserData = IsValidMpqUserData(SearchOffset, FileSize, ha->HeaderData);
|
||||||
|
if(pUserData != NULL)
|
||||||
{
|
{
|
||||||
// Fill the user data header
|
// Fill the user data header
|
||||||
|
ha->UserDataPos = SearchOffset;
|
||||||
ha->pUserData = &ha->UserData;
|
ha->pUserData = &ha->UserData;
|
||||||
memcpy(ha->pUserData, ha->HeaderData, sizeof(TMPQUserData));
|
memcpy(ha->pUserData, pUserData, sizeof(TMPQUserData));
|
||||||
BSWAP_TMPQUSERDATA(ha->pUserData);
|
|
||||||
|
|
||||||
// Remember the position of the user data and continue search
|
// Continue searching from that position
|
||||||
ha->UserDataPos = SearchPos;
|
SearchOffset += ha->pUserData->dwHeaderOffs;
|
||||||
SearchPos += ha->pUserData->dwHeaderOffs;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// There must be MPQ header signature
|
// There must be MPQ header signature. Note that STORM.dll from Warcraft III actually
|
||||||
if(dwHeaderID == ID_MPQ)
|
// tests the MPQ header size. It must be at least 0x20 bytes in order to load it
|
||||||
|
// Abused by Spazzler Map protector. Note that the size check is not present
|
||||||
|
// in Storm.dll v 1.00, so Diablo I code would load the MPQ anyway.
|
||||||
|
dwHeaderSize = BSWAP_INT32_UNSIGNED(ha->HeaderData[1]);
|
||||||
|
if(dwHeaderID == ID_MPQ && dwHeaderSize >= MPQ_HEADER_SIZE_V1)
|
||||||
{
|
{
|
||||||
// Save the position where the MPQ header has been found
|
|
||||||
if(ha->pUserData == NULL)
|
|
||||||
ha->UserDataPos = SearchPos;
|
|
||||||
ha->pHeader = (TMPQHeader *)ha->HeaderData;
|
|
||||||
ha->MpqPos = SearchPos;
|
|
||||||
|
|
||||||
// Now convert the header to version 4
|
// Now convert the header to version 4
|
||||||
BSWAP_TMPQHEADER(ha->pHeader);
|
nError = ConvertMpqHeaderToFormat4(ha, SearchOffset, FileSize, dwFlags);
|
||||||
nError = ConvertMpqHeaderToFormat4(ha, FileSize, dwFlags);
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for MPK archives (Longwu Online - MPQ fork)
|
||||||
|
if(dwHeaderID == ID_MPK)
|
||||||
|
{
|
||||||
|
// Now convert the MPK header to MPQ Header version 4
|
||||||
|
nError = ConvertMpkHeaderToFormat4(ha, FileSize, dwFlags);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If searching for the MPQ header is disabled, return an error
|
||||||
|
if(dwFlags & MPQ_OPEN_NO_HEADER_SEARCH)
|
||||||
|
{
|
||||||
|
nError = ERROR_NOT_SUPPORTED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move to the next possible offset
|
// Move to the next possible offset
|
||||||
SearchPos += 0x200;
|
SearchOffset += 0x200;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we haven't found MPQ header in the file, it's an error
|
// Did we identify one of the supported headers?
|
||||||
if(ha->pHeader == NULL)
|
if(nError == ERROR_SUCCESS)
|
||||||
nError = ERROR_BAD_FORMAT;
|
{
|
||||||
|
// Set the user data position to the MPQ header, if none
|
||||||
|
if(ha->pUserData == NULL)
|
||||||
|
ha->UserDataPos = SearchOffset;
|
||||||
|
|
||||||
|
// Set the position of the MPQ header
|
||||||
|
ha->pHeader = (TMPQHeader *)ha->HeaderData;
|
||||||
|
ha->MpqPos = SearchOffset;
|
||||||
|
|
||||||
|
// Sector size must be nonzero.
|
||||||
|
if(SearchOffset >= FileSize || ha->pHeader->wSectorSize == 0)
|
||||||
|
nError = ERROR_BAD_FORMAT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix table positions according to format
|
// Fix table positions according to format
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// Dump the header
|
// Dump the header
|
||||||
// DumpMpqHeader(ha->pHeader);
|
// DumpMpqHeader(ha->pHeader);
|
||||||
|
|
||||||
// W3x Map Protectors use the fact that War3's Storm.dll ignores the MPQ user data,
|
// W3x Map Protectors use the fact that War3's Storm.dll ignores the MPQ user data,
|
||||||
// and probably ignores the MPQ format version as well. The trick is to
|
// and ignores the MPQ format version as well. The trick is to
|
||||||
// fake MPQ format 2, with an improper hi-word position of hash table and block table
|
// fake MPQ format 2, with an improper hi-word position of hash table and block table
|
||||||
// We can overcome such protectors by forcing opening the archive as MPQ v 1.0
|
// We can overcome such protectors by forcing opening the archive as MPQ v 1.0
|
||||||
if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
|
if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
|
||||||
|
|
@ -291,24 +339,6 @@ bool WINAPI SFileOpenArchive(
|
||||||
nError = VerifyMpqTablePositions(ha, FileSize);
|
nError = VerifyMpqTablePositions(ha, FileSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the MPQ has data bitmap. If yes, we can verify if the MPQ is complete
|
|
||||||
if(nError == ERROR_SUCCESS && ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4)
|
|
||||||
{
|
|
||||||
TFileBitmap * pBitmap;
|
|
||||||
bool bFileIsComplete = true;
|
|
||||||
|
|
||||||
LoadMpqDataBitmap(ha, FileSize, &bFileIsComplete);
|
|
||||||
if(ha->pBitmap != NULL && bFileIsComplete == false)
|
|
||||||
{
|
|
||||||
// Convert the MPQ bitmap to the file bitmap
|
|
||||||
pBitmap = CreateFileBitmap(ha, ha->pBitmap, bFileIsComplete);
|
|
||||||
|
|
||||||
// Set the data bitmap into the file stream for additional checks
|
|
||||||
FileStream_SetBitmap(ha->pStream, pBitmap);
|
|
||||||
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the hash table. Ignore the result, as hash table is no longer required
|
// Read the hash table. Ignore the result, as hash table is no longer required
|
||||||
// Read HET table. Ignore the result, as HET table is no longer required
|
// Read HET table. Ignore the result, as HET table is no longer required
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
|
|
@ -320,14 +350,13 @@ bool WINAPI SFileOpenArchive(
|
||||||
// the block table, BET table, hi-block table, (attributes) and (listfile).
|
// the block table, BET table, hi-block table, (attributes) and (listfile).
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
nError = BuildFileTable(ha, FileSize);
|
nError = BuildFileTable(ha);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the file table, if no kind of protection was detected
|
// Verify the file table, if no kind of malformation was detected
|
||||||
if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_PROTECTED) == 0)
|
if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_MALFORMED) == 0)
|
||||||
{
|
{
|
||||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->pHeader->dwBlockTableSize;
|
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||||
// ULONGLONG ArchiveSize = 0;
|
|
||||||
ULONGLONG RawFilePos;
|
ULONGLONG RawFilePos;
|
||||||
|
|
||||||
// Parse all file entries
|
// Parse all file entries
|
||||||
|
|
@ -354,10 +383,6 @@ bool WINAPI SFileOpenArchive(
|
||||||
nError = ERROR_FILE_CORRUPT;
|
nError = ERROR_FILE_CORRUPT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also, we remember end of the file
|
|
||||||
// if(RawFilePos > ArchiveSize)
|
|
||||||
// ArchiveSize = RawFilePos;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -365,32 +390,47 @@ bool WINAPI SFileOpenArchive(
|
||||||
// Load the internal listfile and include it to the file table
|
// Load the internal listfile and include it to the file table
|
||||||
if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0)
|
if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0)
|
||||||
{
|
{
|
||||||
// Save the flags for (listfile)
|
// Quick check for (listfile)
|
||||||
pFileEntry = GetFileEntryLocale(ha, LISTFILE_NAME, LANG_NEUTRAL);
|
pFileEntry = GetFileEntryLocale(ha, LISTFILE_NAME, LANG_NEUTRAL);
|
||||||
if(pFileEntry != NULL)
|
if(pFileEntry != NULL)
|
||||||
|
{
|
||||||
|
// Ignore result of the operation. (listfile) is optional.
|
||||||
|
SFileAddListFile((HANDLE)ha, NULL);
|
||||||
ha->dwFileFlags1 = pFileEntry->dwFlags;
|
ha->dwFileFlags1 = pFileEntry->dwFlags;
|
||||||
|
}
|
||||||
// Ignore result of the operation. (listfile) is optional.
|
|
||||||
SFileAddListFile((HANDLE)ha, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the "(attributes)" file and merge it to the file table
|
// Load the "(attributes)" file and merge it to the file table
|
||||||
if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0)
|
if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0)
|
||||||
{
|
{
|
||||||
// Save the flags for (attributes)
|
// Quick check for (attributes)
|
||||||
pFileEntry = GetFileEntryLocale(ha, ATTRIBUTES_NAME, LANG_NEUTRAL);
|
pFileEntry = GetFileEntryLocale(ha, ATTRIBUTES_NAME, LANG_NEUTRAL);
|
||||||
if(pFileEntry != NULL)
|
if(pFileEntry != NULL)
|
||||||
|
{
|
||||||
|
// Ignore result of the operation. (attributes) is optional.
|
||||||
|
SAttrLoadAttributes(ha);
|
||||||
ha->dwFileFlags2 = pFileEntry->dwFlags;
|
ha->dwFileFlags2 = pFileEntry->dwFlags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ignore result of the operation. (attributes) is optional.
|
// Remember whether the archive has weak signature. Only for MPQs format 1.0.
|
||||||
SAttrLoadAttributes(ha);
|
if(nError == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
// Quick check for (signature)
|
||||||
|
pFileEntry = GetFileEntryLocale(ha, SIGNATURE_NAME, LANG_NEUTRAL);
|
||||||
|
if(pFileEntry != NULL)
|
||||||
|
{
|
||||||
|
// Just remember that the archive is weak-signed
|
||||||
|
assert(pFileEntry->dwFlags == MPQ_FILE_EXISTS);
|
||||||
|
ha->dwFileFlags3 = pFileEntry->dwFlags;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup and exit
|
// Cleanup and exit
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
FileStream_Close(pStream);
|
FileStream_Close(pStream);
|
||||||
FreeMPQArchive(ha);
|
FreeArchiveHandle(ha);
|
||||||
SetLastError(nError);
|
SetLastError(nError);
|
||||||
ha = NULL;
|
ha = NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -400,13 +440,23 @@ bool WINAPI SFileOpenArchive(
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// SFileGetArchiveBitmap
|
// bool WINAPI SFileSetDownloadCallback(HANDLE, SFILE_DOWNLOAD_CALLBACK, void *);
|
||||||
|
//
|
||||||
|
// Sets a callback that is called when content is downloaded from the master MPQ
|
||||||
|
//
|
||||||
|
|
||||||
bool WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded)
|
bool WINAPI SFileSetDownloadCallback(HANDLE hMpq, SFILE_DOWNLOAD_CALLBACK DownloadCB, void * pvUserData)
|
||||||
{
|
{
|
||||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||||
|
|
||||||
return FileStream_GetBitmap(ha->pStream, pBitmap, Length, LengthNeeded);
|
// Do nothing if 'hMpq' is bad parameter
|
||||||
|
if(!IsValidMpqHandle(hMpq))
|
||||||
|
{
|
||||||
|
SetLastError(ERROR_INVALID_HANDLE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FileStream_SetCallback(ha->pStream, DownloadCB, pvUserData);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
@ -425,14 +475,25 @@ bool WINAPI SFileFlushArchive(HANDLE hMpq)
|
||||||
int nError;
|
int nError;
|
||||||
|
|
||||||
// Do nothing if 'hMpq' is bad parameter
|
// Do nothing if 'hMpq' is bad parameter
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_INVALID_HANDLE);
|
SetLastError(ERROR_INVALID_HANDLE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Indicate that we are saving MPQ internal structures
|
||||||
|
ha->dwFlags |= MPQ_FLAG_SAVING_TABLES;
|
||||||
|
|
||||||
|
// If the (signature) has been invalidated, save it
|
||||||
|
if(ha->dwFlags & MPQ_FLAG_SIGNATURE_INVALID)
|
||||||
|
{
|
||||||
|
nError = SSignFileCreate(ha);
|
||||||
|
if(nError != ERROR_SUCCESS)
|
||||||
|
nResultError = nError;
|
||||||
|
}
|
||||||
|
|
||||||
// If the (listfile) has been invalidated, save it
|
// If the (listfile) has been invalidated, save it
|
||||||
if(ha->dwFlags & MPQ_FLAG_INV_LISTFILE)
|
if(ha->dwFlags & MPQ_FLAG_LISTFILE_INVALID)
|
||||||
{
|
{
|
||||||
nError = SListFileSaveToMpq(ha);
|
nError = SListFileSaveToMpq(ha);
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
|
|
@ -440,7 +501,7 @@ bool WINAPI SFileFlushArchive(HANDLE hMpq)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the (attributes) has been invalidated, save it
|
// If the (attributes) has been invalidated, save it
|
||||||
if(ha->dwFlags & MPQ_FLAG_INV_ATTRIBUTES)
|
if(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_INVALID)
|
||||||
{
|
{
|
||||||
nError = SAttrFileSaveToMpq(ha);
|
nError = SAttrFileSaveToMpq(ha);
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
|
|
@ -450,11 +511,23 @@ bool WINAPI SFileFlushArchive(HANDLE hMpq)
|
||||||
// Save HET table, BET table, hash table, block table, hi-block table
|
// Save HET table, BET table, hash table, block table, hi-block table
|
||||||
if(ha->dwFlags & MPQ_FLAG_CHANGED)
|
if(ha->dwFlags & MPQ_FLAG_CHANGED)
|
||||||
{
|
{
|
||||||
|
// Save all MPQ tables first
|
||||||
nError = SaveMPQTables(ha);
|
nError = SaveMPQTables(ha);
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
nResultError = nError;
|
nResultError = nError;
|
||||||
|
|
||||||
|
// If the archive has weak signature, we need to finish it
|
||||||
|
if(ha->dwFileFlags3 != 0)
|
||||||
|
{
|
||||||
|
nError = SSignFileFinish(ha);
|
||||||
|
if(nError != ERROR_SUCCESS)
|
||||||
|
nResultError = nError;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We are no longer saving internal MPQ structures
|
||||||
|
ha->dwFlags &= ~MPQ_FLAG_SAVING_TABLES;
|
||||||
|
|
||||||
// Return the error
|
// Return the error
|
||||||
if(nResultError != ERROR_SUCCESS)
|
if(nResultError != ERROR_SUCCESS)
|
||||||
SetLastError(nResultError);
|
SetLastError(nResultError);
|
||||||
|
|
@ -470,11 +543,16 @@ bool WINAPI SFileCloseArchive(HANDLE hMpq)
|
||||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||||
bool bResult;
|
bool bResult;
|
||||||
|
|
||||||
|
// Invalidate the add file callback so it won't be called
|
||||||
|
// when saving (listfile) and (attributes)
|
||||||
|
ha->pfnAddFileCB = NULL;
|
||||||
|
ha->pvAddFileUserData = NULL;
|
||||||
|
|
||||||
// Flush all unsaved data to the storage
|
// Flush all unsaved data to the storage
|
||||||
bResult = SFileFlushArchive(hMpq);
|
bResult = SFileFlushArchive(hMpq);
|
||||||
|
|
||||||
// Free all memory used by MPQ archive
|
// Free all memory used by MPQ archive
|
||||||
FreeMPQArchive(ha);
|
FreeArchiveHandle(ha);
|
||||||
return bResult;
|
return bResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,29 +16,46 @@
|
||||||
/* Local functions */
|
/* Local functions */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static const char * GetPatchFileName(TMPQArchive * ha, const char * szFileName, char * szBuffer)
|
||||||
|
{
|
||||||
|
TMPQNamePrefix * pPrefix;
|
||||||
|
|
||||||
|
// Are there patches in the current MPQ?
|
||||||
|
if(ha->dwFlags & MPQ_FLAG_PATCH)
|
||||||
|
{
|
||||||
|
// The patch prefix must be already known here
|
||||||
|
assert(ha->pPatchPrefix != NULL);
|
||||||
|
pPrefix = ha->pPatchPrefix;
|
||||||
|
|
||||||
|
// The patch name for "OldWorld\\XXX\\YYY" is "Base\\XXX\YYY"
|
||||||
|
// We need to remove the "OldWorld\\" prefix
|
||||||
|
if(!_strnicmp(szFileName, "OldWorld\\", 9))
|
||||||
|
szFileName += 9;
|
||||||
|
|
||||||
|
// Create the file name from the known patch entry
|
||||||
|
memcpy(szBuffer, pPrefix->szPatchPrefix, pPrefix->nLength);
|
||||||
|
strcpy(szBuffer + pPrefix->nLength, szFileName);
|
||||||
|
szFileName = szBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return szFileName;
|
||||||
|
}
|
||||||
|
|
||||||
static bool OpenLocalFile(const char * szFileName, HANDLE * phFile)
|
static bool OpenLocalFile(const char * szFileName, HANDLE * phFile)
|
||||||
{
|
{
|
||||||
TFileStream * pStream;
|
TFileStream * pStream;
|
||||||
TMPQFile * hf = NULL;
|
TMPQFile * hf = NULL;
|
||||||
|
|
||||||
// We have to convert the local file name to UNICODE, if needed
|
|
||||||
#ifdef _UNICODE
|
|
||||||
TCHAR szFileNameT[MAX_PATH];
|
TCHAR szFileNameT[MAX_PATH];
|
||||||
int i;
|
|
||||||
|
|
||||||
for(i = 0; szFileName[i] != 0; i++)
|
// Convert the file name to UNICODE (if needed)
|
||||||
szFileNameT[i] = szFileName[i];
|
CopyFileName(szFileNameT, szFileName, strlen(szFileName));
|
||||||
szFileNameT[i] = 0;
|
|
||||||
pStream = FileStream_OpenFile(szFileNameT, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
|
|
||||||
|
|
||||||
#else
|
|
||||||
pStream = FileStream_OpenFile(szFileName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
// Open the file and create the TMPQFile structure
|
||||||
|
pStream = FileStream_OpenFile(szFileNameT, STREAM_FLAG_READ_ONLY);
|
||||||
if(pStream != NULL)
|
if(pStream != NULL)
|
||||||
{
|
{
|
||||||
// Allocate and initialize file handle
|
// Allocate and initialize file handle
|
||||||
hf = CreateMpqFile(NULL);
|
hf = CreateFileHandle(NULL, NULL);
|
||||||
if(hf != NULL)
|
if(hf != NULL)
|
||||||
{
|
{
|
||||||
hf->pStream = pStream;
|
hf->pStream = pStream;
|
||||||
|
|
@ -55,87 +72,67 @@ static bool OpenLocalFile(const char * szFileName, HANDLE * phFile)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HANDLE * phFile)
|
bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, HANDLE * phFile)
|
||||||
{
|
{
|
||||||
|
TMPQArchive * haBase = NULL;
|
||||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||||
|
TFileEntry * pFileEntry;
|
||||||
TMPQFile * hfPatch; // Pointer to patch file
|
TMPQFile * hfPatch; // Pointer to patch file
|
||||||
TMPQFile * hfBase = NULL; // Pointer to base open file
|
TMPQFile * hfBase = NULL; // Pointer to base open file
|
||||||
TMPQFile * hfLast = NULL; // The highest file in the chain that is not patch file
|
|
||||||
TMPQFile * hf = NULL;
|
TMPQFile * hf = NULL;
|
||||||
HANDLE hPatchFile;
|
HANDLE hPatchFile;
|
||||||
char szPatchFileName[MAX_PATH];
|
char szNameBuffer[MAX_PATH];
|
||||||
|
|
||||||
// Keep this flag here for future updates
|
// First of all, find the latest archive where the file is in base version
|
||||||
dwReserved = dwReserved;
|
// (i.e. where the original, unpatched version of the file exists)
|
||||||
|
|
||||||
// First of all, try to open the original version of the file in any of the patch chain
|
|
||||||
while(ha != NULL)
|
while(ha != NULL)
|
||||||
{
|
{
|
||||||
// Construct the name of the patch file
|
// If the file is there, then we remember the archive
|
||||||
strcpy(szPatchFileName, ha->szPatchPrefix);
|
pFileEntry = GetFileEntryExact(ha, GetPatchFileName(ha, szFileName, szNameBuffer), 0);
|
||||||
strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName);
|
if(pFileEntry != NULL && (pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
|
||||||
if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, (HANDLE *)&hfBase))
|
haBase = ha;
|
||||||
{
|
|
||||||
// The file must be a base file, i.e. without MPQ_FILE_PATCH_FILE
|
|
||||||
if((hfBase->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
|
|
||||||
{
|
|
||||||
hf = hfLast = hfBase;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SFileCloseFile((HANDLE)hfBase);
|
// Move to the patch archive
|
||||||
}
|
|
||||||
|
|
||||||
// Move to the next file in the patch chain
|
|
||||||
ha = ha->haPatch;
|
ha = ha->haPatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we couldn't find the file in any of the patches, it doesn't exist
|
// If we couldn't find the base file in any of the patches, it doesn't exist
|
||||||
if(hf == NULL)
|
if((ha = haBase) == NULL)
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_FILE_NOT_FOUND);
|
SetLastError(ERROR_FILE_NOT_FOUND);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now keep going in the patch chain and open every patch file that is there
|
// Now open the base file
|
||||||
for(ha = ha->haPatch; ha != NULL; ha = ha->haPatch)
|
if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szNameBuffer), SFILE_OPEN_BASE_FILE, (HANDLE *)&hfBase))
|
||||||
{
|
{
|
||||||
// Construct patch file name
|
// The file must be a base file, i.e. without MPQ_FILE_PATCH_FILE
|
||||||
strcpy(szPatchFileName, ha->szPatchPrefix);
|
assert((hfBase->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0);
|
||||||
strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName);
|
hf = hfBase;
|
||||||
if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, &hPatchFile))
|
|
||||||
|
// Now open all patches and attach them on top of the base file
|
||||||
|
for(ha = ha->haPatch; ha != NULL; ha = ha->haPatch)
|
||||||
{
|
{
|
||||||
// Remember the new version
|
// Prepare the file name with a correct prefix
|
||||||
hfPatch = (TMPQFile *)hPatchFile;
|
if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szNameBuffer), SFILE_OPEN_BASE_FILE, &hPatchFile))
|
||||||
|
{
|
||||||
|
// Remember the new version
|
||||||
|
hfPatch = (TMPQFile *)hPatchFile;
|
||||||
|
|
||||||
// If we encountered a full replacement of the file,
|
// We should not find patch file
|
||||||
// we have to remember the highest full file
|
assert((hfPatch->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) != 0);
|
||||||
if((hfPatch->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
|
|
||||||
hfLast = hfPatch;
|
|
||||||
|
|
||||||
// Set current patch to base file and move on
|
// Attach the patch to the base file
|
||||||
hf->hfPatchFile = hfPatch;
|
hf->hfPatch = hfPatch;
|
||||||
hf = hfPatch;
|
hf = hfPatch;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we need to free all files that are below the highest unpatched version
|
|
||||||
while(hfBase != hfLast)
|
|
||||||
{
|
|
||||||
TMPQFile * hfNext = hfBase->hfPatchFile;
|
|
||||||
|
|
||||||
// Free the file below
|
|
||||||
hfBase->hfPatchFile = NULL;
|
|
||||||
FreeMPQFile(hfBase);
|
|
||||||
|
|
||||||
// Move the base to the next file
|
|
||||||
hfBase = hfNext;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give the updated base MPQ
|
// Give the updated base MPQ
|
||||||
if(phFile != NULL)
|
if(phFile != NULL)
|
||||||
*phFile = (HANDLE)hfBase;
|
*phFile = (HANDLE)hfBase;
|
||||||
return true;
|
return (hfBase != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
@ -143,17 +140,17 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// SFileEnumLocales enums all locale versions within MPQ.
|
// SFileEnumLocales enums all locale versions within MPQ.
|
||||||
// Functions fills all available language identifiers on a file into the buffer
|
// Functions fills all available language identifiers on a file into the buffer
|
||||||
// pointed by plcLocales. There must be enough entries to copy the localed,
|
// pointed by plcLocales. There must be enough entries to copy the localed,
|
||||||
// otherwise the function returns ERROR_INSUFFICIENT_BUFFER.
|
// otherwise the function returns ERROR_INSUFFICIENT_BUFFER.
|
||||||
|
|
||||||
int WINAPI SFileEnumLocales(
|
int WINAPI SFileEnumLocales(
|
||||||
HANDLE hMpq,
|
HANDLE hMpq,
|
||||||
const char * szFileName,
|
const char * szFileName,
|
||||||
LCID * plcLocales,
|
LCID * plcLocales,
|
||||||
LPDWORD pdwMaxLocales,
|
LPDWORD pdwMaxLocales,
|
||||||
DWORD dwSearchScope)
|
DWORD dwSearchScope)
|
||||||
{
|
{
|
||||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||||
TFileEntry * pFileEntry;
|
TFileEntry * pFileEntry;
|
||||||
|
|
@ -163,13 +160,13 @@ int WINAPI SFileEnumLocales(
|
||||||
DWORD dwLocales = 0;
|
DWORD dwLocales = 0;
|
||||||
|
|
||||||
// Test the parameters
|
// Test the parameters
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
return ERROR_INVALID_HANDLE;
|
return ERROR_INVALID_HANDLE;
|
||||||
if(szFileName == NULL || *szFileName == 0)
|
if(szFileName == NULL || *szFileName == 0)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
if(pdwMaxLocales == NULL)
|
if(pdwMaxLocales == NULL)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
// Keep compiler happy
|
// Keep compiler happy
|
||||||
dwSearchScope = dwSearchScope;
|
dwSearchScope = dwSearchScope;
|
||||||
|
|
||||||
|
|
@ -232,11 +229,11 @@ bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName)
|
||||||
TFileEntry * pFileEntry;
|
TFileEntry * pFileEntry;
|
||||||
DWORD dwFlagsToCheck = MPQ_FILE_EXISTS;
|
DWORD dwFlagsToCheck = MPQ_FILE_EXISTS;
|
||||||
DWORD dwFileIndex = 0;
|
DWORD dwFileIndex = 0;
|
||||||
char szPatchFileName[MAX_PATH];
|
char szPrefixBuffer[MAX_PATH];
|
||||||
bool bIsPseudoName;
|
bool bIsPseudoName;
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
nError = ERROR_INVALID_HANDLE;
|
nError = ERROR_INVALID_HANDLE;
|
||||||
if(szFileName == NULL || *szFileName == 0)
|
if(szFileName == NULL || *szFileName == 0)
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
|
|
@ -251,7 +248,7 @@ bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName)
|
||||||
while(ha != NULL)
|
while(ha != NULL)
|
||||||
{
|
{
|
||||||
// Verify presence of the file
|
// Verify presence of the file
|
||||||
pFileEntry = (bIsPseudoName == false) ? GetFileEntryLocale(ha, szFileName, lcFileLocale)
|
pFileEntry = (bIsPseudoName == false) ? GetFileEntryLocale(ha, GetPatchFileName(ha, szFileName, szPrefixBuffer), lcFileLocale)
|
||||||
: GetFileEntryByIndex(ha, dwFileIndex);
|
: GetFileEntryByIndex(ha, dwFileIndex);
|
||||||
// Verify the file flags
|
// Verify the file flags
|
||||||
if(pFileEntry != NULL && (pFileEntry->dwFlags & dwFlagsToCheck) == MPQ_FILE_EXISTS)
|
if(pFileEntry != NULL && (pFileEntry->dwFlags & dwFlagsToCheck) == MPQ_FILE_EXISTS)
|
||||||
|
|
@ -260,14 +257,6 @@ bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName)
|
||||||
// If this is patched archive, go to the patch
|
// If this is patched archive, go to the patch
|
||||||
dwFlagsToCheck = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE;
|
dwFlagsToCheck = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE;
|
||||||
ha = ha->haPatch;
|
ha = ha->haPatch;
|
||||||
|
|
||||||
// Prepare the patched file name
|
|
||||||
if(ha != NULL)
|
|
||||||
{
|
|
||||||
strcpy(szPatchFileName, ha->szPatchPrefix);
|
|
||||||
strcat(szPatchFileName, szFileName);
|
|
||||||
szFileName = szPatchFileName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not found, sorry
|
// Not found, sorry
|
||||||
|
|
@ -306,73 +295,78 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch
|
||||||
{
|
{
|
||||||
switch(dwSearchScope)
|
switch(dwSearchScope)
|
||||||
{
|
{
|
||||||
case SFILE_OPEN_PATCHED_FILE:
|
case SFILE_OPEN_FROM_MPQ:
|
||||||
|
case SFILE_OPEN_BASE_FILE:
|
||||||
|
|
||||||
|
if(!IsValidMpqHandle(hMpq))
|
||||||
|
{
|
||||||
|
nError = ERROR_INVALID_HANDLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// We want to open the updated version of the file
|
if(szFileName == NULL || *szFileName == 0)
|
||||||
return OpenPatchedFile(hMpq, szFileName, 0, phFile);
|
{
|
||||||
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case SFILE_OPEN_FROM_MPQ:
|
// Check the pseudo-file name
|
||||||
|
if(IsPseudoFileName(szFileName, &dwFileIndex))
|
||||||
if(!IsValidMpqHandle(ha))
|
{
|
||||||
{
|
pFileEntry = GetFileEntryByIndex(ha, dwFileIndex);
|
||||||
nError = ERROR_INVALID_HANDLE;
|
bOpenByIndex = true;
|
||||||
|
if(pFileEntry == NULL)
|
||||||
|
nError = ERROR_FILE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If this MPQ is a patched archive, open the file as patched
|
||||||
|
if(ha->haPatch == NULL || dwSearchScope == SFILE_OPEN_BASE_FILE)
|
||||||
|
{
|
||||||
|
// Otherwise, open the file from *this* MPQ
|
||||||
|
pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale);
|
||||||
|
if(pFileEntry == NULL)
|
||||||
|
nError = ERROR_FILE_NOT_FOUND;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return OpenPatchedFile(hMpq, szFileName, phFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if(szFileName == NULL || *szFileName == 0)
|
case SFILE_OPEN_ANY_LOCALE:
|
||||||
{
|
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// First of all, check the name as-is
|
// This open option is reserved for opening MPQ internal listfile.
|
||||||
if(!IsPseudoFileName(szFileName, &dwFileIndex))
|
// No argument validation. Tries to open file with neutral locale first,
|
||||||
{
|
// then any other available.
|
||||||
pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale);
|
pFileEntry = GetFileEntryAny(ha, szFileName);
|
||||||
if(pFileEntry == NULL)
|
if(pFileEntry == NULL)
|
||||||
nError = ERROR_FILE_NOT_FOUND;
|
nError = ERROR_FILE_NOT_FOUND;
|
||||||
}
|
break;
|
||||||
else
|
|
||||||
{
|
|
||||||
bOpenByIndex = true;
|
|
||||||
pFileEntry = GetFileEntryByIndex(ha, dwFileIndex);
|
|
||||||
if(pFileEntry == NULL)
|
|
||||||
nError = ERROR_FILE_NOT_FOUND;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SFILE_OPEN_ANY_LOCALE:
|
case SFILE_OPEN_LOCAL_FILE:
|
||||||
|
|
||||||
// This open option is reserved for opening MPQ internal listfile.
|
if(szFileName == NULL || *szFileName == 0)
|
||||||
// No argument validation. Tries to open file with neutral locale first,
|
{
|
||||||
// then any other available.
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
dwSearchScope = SFILE_OPEN_FROM_MPQ;
|
break;
|
||||||
pFileEntry = GetFileEntryAny(ha, szFileName);
|
}
|
||||||
if(pFileEntry == NULL)
|
|
||||||
nError = ERROR_FILE_NOT_FOUND;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SFILE_OPEN_LOCAL_FILE:
|
return OpenLocalFile(szFileName, phFile);
|
||||||
|
|
||||||
if(szFileName == NULL || *szFileName == 0)
|
default:
|
||||||
{
|
|
||||||
|
// Don't accept any other value
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
return OpenLocalFile(szFileName, phFile);
|
|
||||||
|
|
||||||
default:
|
|
||||||
|
|
||||||
// Don't accept any other value
|
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quick return if something failed
|
// Quick return if something failed
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
SetLastError(nError);
|
SetLastError(nError);
|
||||||
|
*phFile = NULL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -389,22 +383,14 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch
|
||||||
// Allocate file handle
|
// Allocate file handle
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
if((hf = STORM_ALLOC(TMPQFile, 1)) == NULL)
|
hf = CreateFileHandle(ha, pFileEntry);
|
||||||
|
if(hf == NULL)
|
||||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize file handle
|
// Initialize file handle
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
memset(hf, 0, sizeof(TMPQFile));
|
|
||||||
hf->pFileEntry = pFileEntry;
|
|
||||||
hf->dwMagic = ID_MPQ_FILE;
|
|
||||||
hf->ha = ha;
|
|
||||||
|
|
||||||
hf->MpqFilePos = pFileEntry->ByteOffset;
|
|
||||||
hf->RawFilePos = ha->MpqPos + hf->MpqFilePos;
|
|
||||||
hf->dwDataSize = pFileEntry->dwFileSize;
|
|
||||||
|
|
||||||
// If the MPQ has sector CRC enabled, enable if for the file
|
// If the MPQ has sector CRC enabled, enable if for the file
|
||||||
if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC)
|
if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC)
|
||||||
hf->bCheckSectorCRCs = true;
|
hf->bCheckSectorCRCs = true;
|
||||||
|
|
@ -413,7 +399,7 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch
|
||||||
if(bOpenByIndex == false)
|
if(bOpenByIndex == false)
|
||||||
{
|
{
|
||||||
// If there is no file name yet, allocate it
|
// If there is no file name yet, allocate it
|
||||||
AllocateFileName(pFileEntry, szFileName);
|
AllocateFileName(ha, pFileEntry, szFileName);
|
||||||
|
|
||||||
// If the file is encrypted, we should detect the file key
|
// If the file is encrypted, we should detect the file key
|
||||||
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
|
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
|
||||||
|
|
@ -432,22 +418,16 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the file is actually a patch file, we have to load the patch file header
|
// Cleanup and exit
|
||||||
if(nError == ERROR_SUCCESS && pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
|
|
||||||
{
|
|
||||||
assert(hf->pPatchInfo == NULL);
|
|
||||||
nError = AllocatePatchInfo(hf, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
SetLastError(nError);
|
SetLastError(nError);
|
||||||
FreeMPQFile(hf);
|
FreeFileHandle(hf);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
*phFile = hf;
|
*phFile = hf;
|
||||||
return (nError == ERROR_SUCCESS);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
@ -456,14 +436,14 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch
|
||||||
bool WINAPI SFileCloseFile(HANDLE hFile)
|
bool WINAPI SFileCloseFile(HANDLE hFile)
|
||||||
{
|
{
|
||||||
TMPQFile * hf = (TMPQFile *)hFile;
|
TMPQFile * hf = (TMPQFile *)hFile;
|
||||||
|
|
||||||
if(!IsValidFileHandle(hf))
|
if(!IsValidFileHandle(hFile))
|
||||||
{
|
{
|
||||||
SetLastError(ERROR_INVALID_HANDLE);
|
SetLastError(ERROR_INVALID_HANDLE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the structure
|
// Free the structure
|
||||||
FreeMPQFile(hf);
|
FreeFileHandle(hf);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Local structures
|
// Local structures
|
||||||
|
|
||||||
|
#define PATCH_SIGNATURE_HEADER 0x48435450
|
||||||
|
#define PATCH_SIGNATURE_MD5 0x5f35444d
|
||||||
|
#define PATCH_SIGNATURE_XFRM 0x4d524658
|
||||||
|
|
||||||
typedef struct _BLIZZARD_BSDIFF40_FILE
|
typedef struct _BLIZZARD_BSDIFF40_FILE
|
||||||
{
|
{
|
||||||
ULONGLONG Signature;
|
ULONGLONG Signature;
|
||||||
|
|
@ -24,48 +28,39 @@ typedef struct _BLIZZARD_BSDIFF40_FILE
|
||||||
} BLIZZARD_BSDIFF40_FILE, *PBLIZZARD_BSDIFF40_FILE;
|
} BLIZZARD_BSDIFF40_FILE, *PBLIZZARD_BSDIFF40_FILE;
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Local functions
|
// Local variables
|
||||||
|
|
||||||
static bool GetDefaultPatchPrefix(
|
static const char * LanguageList[] =
|
||||||
const TCHAR * szBaseMpqName,
|
|
||||||
char * szBuffer)
|
|
||||||
{
|
{
|
||||||
const TCHAR * szExtension;
|
"deDE",
|
||||||
const TCHAR * szDash;
|
"enCN",
|
||||||
|
"enGB",
|
||||||
|
"enTW",
|
||||||
|
"enUS",
|
||||||
|
"esES",
|
||||||
|
"esMX",
|
||||||
|
"frFR",
|
||||||
|
"koKR",
|
||||||
|
"ptBR",
|
||||||
|
"ptPT",
|
||||||
|
"ruRU",
|
||||||
|
"zhCN",
|
||||||
|
"zhTW",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
// Ensure that both names are plain names
|
//-----------------------------------------------------------------------------
|
||||||
szBaseMpqName = GetPlainFileNameT(szBaseMpqName);
|
// Local functions
|
||||||
|
|
||||||
// Patch prefix is for the Cataclysm MPQs, whose names
|
|
||||||
// are like "locale-enGB.MPQ" or "speech-enGB.MPQ"
|
|
||||||
szExtension = _tcsrchr(szBaseMpqName, _T('.'));
|
|
||||||
szDash = _tcsrchr(szBaseMpqName, _T('-'));
|
|
||||||
strcpy(szBuffer, "Base");
|
|
||||||
|
|
||||||
// If the length of the prefix doesn't match, use default one
|
|
||||||
if(szExtension != NULL && szDash != NULL && (szExtension - szDash) == 5)
|
|
||||||
{
|
|
||||||
// Copy the prefix
|
|
||||||
szBuffer[0] = (char)szDash[1];
|
|
||||||
szBuffer[1] = (char)szDash[2];
|
|
||||||
szBuffer[2] = (char)szDash[3];
|
|
||||||
szBuffer[3] = (char)szDash[4];
|
|
||||||
szBuffer[4] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE pbCompressed, DWORD cbCompressed)
|
static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE pbCompressed, DWORD cbCompressed)
|
||||||
{
|
{
|
||||||
LPBYTE pbDecompressedEnd = pbDecompressed + cbDecompressed;
|
LPBYTE pbDecompressedEnd = pbDecompressed + cbDecompressed;
|
||||||
LPBYTE pbCompressedEnd = pbCompressed + cbCompressed;
|
LPBYTE pbCompressedEnd = pbCompressed + cbCompressed;
|
||||||
BYTE RepeatCount;
|
BYTE RepeatCount;
|
||||||
BYTE OneByte;
|
BYTE OneByte;
|
||||||
|
|
||||||
// Cut the initial DWORD from the compressed chunk
|
// Cut the initial DWORD from the compressed chunk
|
||||||
pbCompressed += sizeof(DWORD);
|
pbCompressed += sizeof(DWORD);
|
||||||
cbCompressed -= sizeof(DWORD);
|
|
||||||
|
|
||||||
// Pre-fill decompressed buffer with zeros
|
// Pre-fill decompressed buffer with zeros
|
||||||
memset(pbDecompressed, 0, cbDecompressed);
|
memset(pbDecompressed, 0, cbDecompressed);
|
||||||
|
|
@ -74,7 +69,7 @@ static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE p
|
||||||
while(pbCompressed < pbCompressedEnd && pbDecompressed < pbDecompressedEnd)
|
while(pbCompressed < pbCompressedEnd && pbDecompressed < pbDecompressedEnd)
|
||||||
{
|
{
|
||||||
OneByte = *pbCompressed++;
|
OneByte = *pbCompressed++;
|
||||||
|
|
||||||
// Is it a repetition byte ?
|
// Is it a repetition byte ?
|
||||||
if(OneByte & 0x80)
|
if(OneByte & 0x80)
|
||||||
{
|
{
|
||||||
|
|
@ -94,7 +89,7 @@ static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int LoadMpqPatch_COPY(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
static int LoadFilePatch_COPY(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
||||||
{
|
{
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
|
@ -120,7 +115,7 @@ static int LoadMpqPatch_COPY(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
||||||
return nError;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
static int LoadFilePatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
||||||
{
|
{
|
||||||
LPBYTE pbDecompressed = NULL;
|
LPBYTE pbDecompressed = NULL;
|
||||||
LPBYTE pbCompressed = NULL;
|
LPBYTE pbCompressed = NULL;
|
||||||
|
|
@ -133,7 +128,7 @@ static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
||||||
cbCompressed = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER;
|
cbCompressed = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER;
|
||||||
pbCompressed = STORM_ALLOC(BYTE, cbCompressed);
|
pbCompressed = STORM_ALLOC(BYTE, cbCompressed);
|
||||||
if(pbCompressed == NULL)
|
if(pbCompressed == NULL)
|
||||||
nError = ERROR_SUCCESS;
|
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
|
||||||
// Read the compressed patch data
|
// Read the compressed patch data
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
|
|
@ -178,46 +173,38 @@ static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
||||||
return nError;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ApplyMpqPatch_COPY(
|
static int ApplyFilePatch_COPY(
|
||||||
TMPQFile * hf,
|
TMPQFile * hfFrom,
|
||||||
TPatchHeader * pPatchHeader)
|
TMPQFile * hf,
|
||||||
|
TPatchHeader * pPatchHeader)
|
||||||
{
|
{
|
||||||
LPBYTE pbNewFileData;
|
// Sanity checks
|
||||||
DWORD cbNewFileData;
|
assert(hf->cbFileData == (pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER));
|
||||||
|
assert(hf->pbFileData != NULL);
|
||||||
// Allocate space for new file data
|
hfFrom = hfFrom;
|
||||||
cbNewFileData = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER;
|
|
||||||
pbNewFileData = STORM_ALLOC(BYTE, cbNewFileData);
|
|
||||||
if(pbNewFileData == NULL)
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
// Copy the patch data as-is
|
// Copy the patch data as-is
|
||||||
memcpy(pbNewFileData, (LPBYTE)pPatchHeader + sizeof(TPatchHeader), cbNewFileData);
|
memcpy(hf->pbFileData, (LPBYTE)pPatchHeader + sizeof(TPatchHeader), hf->cbFileData);
|
||||||
|
|
||||||
// Free the old file data
|
|
||||||
STORM_FREE(hf->pbFileData);
|
|
||||||
|
|
||||||
// Put the new file data there
|
|
||||||
hf->pbFileData = pbNewFileData;
|
|
||||||
hf->cbFileData = cbNewFileData;
|
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ApplyMpqPatch_BSD0(
|
static int ApplyFilePatch_BSD0(
|
||||||
TMPQFile * hf,
|
TMPQFile * hfFrom,
|
||||||
TPatchHeader * pPatchHeader)
|
TMPQFile * hf,
|
||||||
|
TPatchHeader * pPatchHeader)
|
||||||
{
|
{
|
||||||
PBLIZZARD_BSDIFF40_FILE pBsdiff;
|
PBLIZZARD_BSDIFF40_FILE pBsdiff;
|
||||||
LPDWORD pCtrlBlock;
|
LPDWORD pCtrlBlock;
|
||||||
LPBYTE pbPatchData = (LPBYTE)pPatchHeader + sizeof(TPatchHeader);
|
LPBYTE pbPatchData = (LPBYTE)pPatchHeader + sizeof(TPatchHeader);
|
||||||
LPBYTE pDataBlock;
|
LPBYTE pDataBlock;
|
||||||
LPBYTE pExtraBlock;
|
LPBYTE pExtraBlock;
|
||||||
LPBYTE pbNewData = NULL;
|
LPBYTE pbOldData = hfFrom->pbFileData;
|
||||||
LPBYTE pbOldData = (LPBYTE)hf->pbFileData;
|
LPBYTE pbNewData = hf->pbFileData;
|
||||||
|
DWORD dwCombineSize;
|
||||||
DWORD dwNewOffset = 0; // Current position to patch
|
DWORD dwNewOffset = 0; // Current position to patch
|
||||||
DWORD dwOldOffset = 0; // Current source position
|
DWORD dwOldOffset = 0; // Current source position
|
||||||
DWORD dwNewSize; // Patched file size
|
DWORD dwNewSize; // Patched file size
|
||||||
DWORD dwOldSize = hf->cbFileData; // File size before patch
|
DWORD dwOldSize = hfFrom->cbFileData; // File size before patch
|
||||||
|
|
||||||
// Get pointer to the patch header
|
// Get pointer to the patch header
|
||||||
// Format of BSDIFF header corresponds to original BSDIFF, which is:
|
// Format of BSDIFF header corresponds to original BSDIFF, which is:
|
||||||
|
|
@ -245,11 +232,6 @@ static int ApplyMpqPatch_BSD0(
|
||||||
pExtraBlock = (LPBYTE)pbPatchData;
|
pExtraBlock = (LPBYTE)pbPatchData;
|
||||||
dwNewSize = (DWORD)BSWAP_INT64_UNSIGNED(pBsdiff->NewFileSize);
|
dwNewSize = (DWORD)BSWAP_INT64_UNSIGNED(pBsdiff->NewFileSize);
|
||||||
|
|
||||||
// Allocate new buffer
|
|
||||||
pbNewData = STORM_ALLOC(BYTE, dwNewSize);
|
|
||||||
if(pbNewData == NULL)
|
|
||||||
return ERROR_NOT_ENOUGH_MEMORY;
|
|
||||||
|
|
||||||
// Now patch the file
|
// Now patch the file
|
||||||
while(dwNewOffset < dwNewSize)
|
while(dwNewOffset < dwNewSize)
|
||||||
{
|
{
|
||||||
|
|
@ -260,31 +242,26 @@ static int ApplyMpqPatch_BSD0(
|
||||||
|
|
||||||
// Sanity check
|
// Sanity check
|
||||||
if((dwNewOffset + dwAddDataLength) > dwNewSize)
|
if((dwNewOffset + dwAddDataLength) > dwNewSize)
|
||||||
{
|
|
||||||
STORM_FREE(pbNewData);
|
|
||||||
return ERROR_FILE_CORRUPT;
|
return ERROR_FILE_CORRUPT;
|
||||||
}
|
|
||||||
|
|
||||||
// Read the diff string to the target buffer
|
// Read the diff string to the target buffer
|
||||||
memcpy(pbNewData + dwNewOffset, pDataBlock, dwAddDataLength);
|
memcpy(pbNewData + dwNewOffset, pDataBlock, dwAddDataLength);
|
||||||
pDataBlock += dwAddDataLength;
|
pDataBlock += dwAddDataLength;
|
||||||
|
|
||||||
// Now combine the patch data with the original file
|
// Get the longest block that we can combine
|
||||||
for(i = 0; i < dwAddDataLength; i++)
|
dwCombineSize = ((dwOldOffset + dwAddDataLength) >= dwOldSize) ? (dwOldSize - dwOldOffset) : dwAddDataLength;
|
||||||
{
|
|
||||||
if(dwOldOffset < dwOldSize)
|
|
||||||
pbNewData[dwNewOffset] = pbNewData[dwNewOffset] + pbOldData[dwOldOffset];
|
|
||||||
|
|
||||||
dwNewOffset++;
|
// Now combine the patch data with the original file
|
||||||
dwOldOffset++;
|
for(i = 0; i < dwCombineSize; i++)
|
||||||
}
|
pbNewData[dwNewOffset + i] = pbNewData[dwNewOffset + i] + pbOldData[dwOldOffset + i];
|
||||||
|
|
||||||
|
// Move the offsets
|
||||||
|
dwNewOffset += dwAddDataLength;
|
||||||
|
dwOldOffset += dwAddDataLength;
|
||||||
|
|
||||||
// Sanity check
|
// Sanity check
|
||||||
if((dwNewOffset + dwMovDataLength) > dwNewSize)
|
if((dwNewOffset + dwMovDataLength) > dwNewSize)
|
||||||
{
|
|
||||||
STORM_FREE(pbNewData);
|
|
||||||
return ERROR_FILE_CORRUPT;
|
return ERROR_FILE_CORRUPT;
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the data from the extra block in BSDIFF patch
|
// Copy the data from the extra block in BSDIFF patch
|
||||||
memcpy(pbNewData + dwNewOffset, pExtraBlock, dwMovDataLength);
|
memcpy(pbNewData + dwNewOffset, pExtraBlock, dwMovDataLength);
|
||||||
|
|
@ -298,17 +275,12 @@ static int ApplyMpqPatch_BSD0(
|
||||||
pCtrlBlock += 3;
|
pCtrlBlock += 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the old file data
|
// Success
|
||||||
STORM_FREE(hf->pbFileData);
|
|
||||||
|
|
||||||
// Put the new data to the fil structure
|
|
||||||
hf->pbFileData = pbNewData;
|
|
||||||
hf->cbFileData = dwNewSize;
|
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int LoadMpqPatch(TMPQFile * hf)
|
static int LoadFilePatch(TMPQFile * hf)
|
||||||
{
|
{
|
||||||
TPatchHeader PatchHeader;
|
TPatchHeader PatchHeader;
|
||||||
DWORD dwBytesRead;
|
DWORD dwBytesRead;
|
||||||
|
|
@ -328,7 +300,7 @@ static int LoadMpqPatch(TMPQFile * hf)
|
||||||
PatchHeader.dwXfrmBlockSize = BSWAP_INT32_UNSIGNED(PatchHeader.dwXfrmBlockSize);
|
PatchHeader.dwXfrmBlockSize = BSWAP_INT32_UNSIGNED(PatchHeader.dwXfrmBlockSize);
|
||||||
PatchHeader.dwPatchType = BSWAP_INT32_UNSIGNED(PatchHeader.dwPatchType);
|
PatchHeader.dwPatchType = BSWAP_INT32_UNSIGNED(PatchHeader.dwPatchType);
|
||||||
|
|
||||||
if(PatchHeader.dwSignature != 0x48435450 || PatchHeader.dwMD5 != 0x5f35444d || PatchHeader.dwXFRM != 0x4d524658)
|
if(PatchHeader.dwSignature != PATCH_SIGNATURE_HEADER || PatchHeader.dwMD5 != PATCH_SIGNATURE_MD5 || PatchHeader.dwXFRM != PATCH_SIGNATURE_XFRM)
|
||||||
nError = ERROR_FILE_CORRUPT;
|
nError = ERROR_FILE_CORRUPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -337,52 +309,65 @@ static int LoadMpqPatch(TMPQFile * hf)
|
||||||
{
|
{
|
||||||
switch(PatchHeader.dwPatchType)
|
switch(PatchHeader.dwPatchType)
|
||||||
{
|
{
|
||||||
case 0x59504f43: // 'COPY'
|
case 0x59504f43: // 'COPY'
|
||||||
nError = LoadMpqPatch_COPY(hf, &PatchHeader);
|
nError = LoadFilePatch_COPY(hf, &PatchHeader);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x30445342: // 'BSD0'
|
case 0x30445342: // 'BSD0'
|
||||||
nError = LoadMpqPatch_BSD0(hf, &PatchHeader);
|
nError = LoadFilePatch_BSD0(hf, &PatchHeader);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
nError = ERROR_FILE_CORRUPT;
|
nError = ERROR_FILE_CORRUPT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nError;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ApplyMpqPatch(
|
static int ApplyFilePatch(
|
||||||
TMPQFile * hf,
|
TMPQFile * hfBase, // The file in the base MPQ
|
||||||
TPatchHeader * pPatchHeader)
|
TMPQFile * hfPrev, // The file in the previous MPQ
|
||||||
|
TMPQFile * hf)
|
||||||
{
|
{
|
||||||
|
TPatchHeader * pPatchHeader = hf->pPatchHeader;
|
||||||
|
TMPQFile * hfFrom = NULL;
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Verify the original file before patching
|
// Sanity checks
|
||||||
if(pPatchHeader->dwSizeBeforePatch != 0)
|
assert(hf->pbFileData == NULL);
|
||||||
{
|
|
||||||
if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_before_patch))
|
// Either take the base version or the previous version
|
||||||
nError = ERROR_FILE_CORRUPT;
|
if(!memcmp(hfBase->FileDataMD5, pPatchHeader->md5_before_patch, MD5_DIGEST_SIZE))
|
||||||
}
|
hfFrom = hfBase;
|
||||||
|
if(!memcmp(hfPrev->FileDataMD5, pPatchHeader->md5_before_patch, MD5_DIGEST_SIZE))
|
||||||
|
hfFrom = hfPrev;
|
||||||
|
if(hfFrom == NULL)
|
||||||
|
return ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
|
// Allocate the buffer for patched file content
|
||||||
|
hf->pbFileData = STORM_ALLOC(BYTE, pPatchHeader->dwSizeAfterPatch);
|
||||||
|
hf->cbFileData = pPatchHeader->dwSizeAfterPatch;
|
||||||
|
if(hf->pbFileData == NULL)
|
||||||
|
return ERROR_NOT_ENOUGH_MEMORY;
|
||||||
|
|
||||||
// Apply the patch
|
// Apply the patch
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
switch(pPatchHeader->dwPatchType)
|
switch(pPatchHeader->dwPatchType)
|
||||||
{
|
{
|
||||||
case 0x59504f43: // 'COPY'
|
case 0x59504f43: // 'COPY'
|
||||||
nError = ApplyMpqPatch_COPY(hf, pPatchHeader);
|
nError = ApplyFilePatch_COPY(hfFrom, hf, pPatchHeader);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x30445342: // 'BSD0'
|
case 0x30445342: // 'BSD0'
|
||||||
nError = ApplyMpqPatch_BSD0(hf, pPatchHeader);
|
nError = ApplyFilePatch_BSD0(hfFrom, hf, pPatchHeader);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
nError = ERROR_FILE_CORRUPT;
|
nError = ERROR_FILE_CORRUPT;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,11 +377,267 @@ static int ApplyMpqPatch(
|
||||||
// Verify the patched file
|
// Verify the patched file
|
||||||
if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_after_patch))
|
if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_after_patch))
|
||||||
nError = ERROR_FILE_CORRUPT;
|
nError = ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
|
// Copy the MD5 of the new block
|
||||||
|
memcpy(hf->FileDataMD5, pPatchHeader->md5_after_patch, MD5_DIGEST_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nError;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void FreePatchData(TMPQFile * hf)
|
||||||
|
{
|
||||||
|
STORM_FREE(hf->pbFileData);
|
||||||
|
hf->pbFileData = NULL;
|
||||||
|
hf->cbFileData = 0;
|
||||||
|
|
||||||
|
STORM_FREE(hf->pPatchHeader);
|
||||||
|
hf->pPatchHeader = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Local functions (patch prefix matching)
|
||||||
|
|
||||||
|
static TFileEntry * FindMd5ListFile(TMPQArchive * ha)
|
||||||
|
{
|
||||||
|
TFileEntry * pFileEntry = ha->pFileTable + ha->dwFileTableSize;
|
||||||
|
char * szLstName;
|
||||||
|
size_t nTryCount = 0;
|
||||||
|
size_t nLength;
|
||||||
|
|
||||||
|
// Check every file entry for "*-md5.lst".
|
||||||
|
// Go backwards, as the entry is usually at the end of the file table
|
||||||
|
while(pFileEntry > ha->pFileTable && nTryCount < 10)
|
||||||
|
{
|
||||||
|
// The file name must be valid
|
||||||
|
if(pFileEntry->szFileName != NULL)
|
||||||
|
{
|
||||||
|
// Get the name and length
|
||||||
|
szLstName = pFileEntry->szFileName;
|
||||||
|
nLength = strlen(szLstName);
|
||||||
|
|
||||||
|
// Check for the tail name
|
||||||
|
if(!_stricmp(szLstName + nLength - 8, "-md5.lst"))
|
||||||
|
return pFileEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move back
|
||||||
|
pFileEntry--;
|
||||||
|
nTryCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not found, sorry
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreatePatchPrefix(TMPQArchive * ha, const char * szFileName, const char * szPrefixEnd)
|
||||||
|
{
|
||||||
|
TMPQNamePrefix * pNewPrefix;
|
||||||
|
size_t nLength;
|
||||||
|
|
||||||
|
// If the end of the patch prefix was not entered, find it
|
||||||
|
if(szFileName != NULL && szPrefixEnd == NULL)
|
||||||
|
szPrefixEnd = szFileName + strlen(szFileName);
|
||||||
|
|
||||||
|
// Create the patch prefix
|
||||||
|
nLength = (szPrefixEnd - szFileName);
|
||||||
|
pNewPrefix = (TMPQNamePrefix *)STORM_ALLOC(BYTE, sizeof(TMPQNamePrefix) + nLength);
|
||||||
|
if(pNewPrefix != NULL)
|
||||||
|
{
|
||||||
|
// Fill the name prefix
|
||||||
|
pNewPrefix->nLength = nLength;
|
||||||
|
pNewPrefix->szPatchPrefix[0] = 0;
|
||||||
|
|
||||||
|
// Fill the name prefix. Also add the backslash
|
||||||
|
if(szFileName && nLength)
|
||||||
|
{
|
||||||
|
memcpy(pNewPrefix->szPatchPrefix, szFileName, nLength);
|
||||||
|
pNewPrefix->szPatchPrefix[nLength] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ha->pPatchPrefix = pNewPrefix;
|
||||||
|
return (pNewPrefix != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsMatchingPatchFile(
|
||||||
|
TMPQArchive * ha,
|
||||||
|
const char * szFileName,
|
||||||
|
LPBYTE pbFileMd5)
|
||||||
|
{
|
||||||
|
TPatchHeader PatchHeader = {0};
|
||||||
|
HANDLE hFile = NULL;
|
||||||
|
DWORD dwTransferred = 0;
|
||||||
|
bool bResult = false;
|
||||||
|
|
||||||
|
// Open the file and load the patch header
|
||||||
|
if(SFileOpenFileEx((HANDLE)ha, szFileName, SFILE_OPEN_BASE_FILE, &hFile))
|
||||||
|
{
|
||||||
|
// Load the patch header
|
||||||
|
SFileReadFile(hFile, &PatchHeader, sizeof(TPatchHeader), &dwTransferred, NULL);
|
||||||
|
BSWAP_ARRAY32_UNSIGNED(pPatchHeader, sizeof(DWORD) * 6);
|
||||||
|
|
||||||
|
// If the file contains an incremental patch,
|
||||||
|
// compare the "MD5 before patching" with the base file MD5
|
||||||
|
if(dwTransferred == sizeof(TPatchHeader) && PatchHeader.dwSignature == PATCH_SIGNATURE_HEADER)
|
||||||
|
bResult = (!memcmp(PatchHeader.md5_before_patch, pbFileMd5, MD5_DIGEST_SIZE));
|
||||||
|
|
||||||
|
// Close the file
|
||||||
|
SFileCloseFile(hFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char * GetLstFileLanguage(const char * szFileName)
|
||||||
|
{
|
||||||
|
char szLstSuffix[0x80];
|
||||||
|
size_t nLength;
|
||||||
|
size_t nSuffixLength;
|
||||||
|
|
||||||
|
// Each language-dependent file ends with "xxXX-md5.lst"
|
||||||
|
nLength = strlen(szFileName);
|
||||||
|
if(nLength < 12)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// Try each and every possibility
|
||||||
|
for(size_t i = 0; LanguageList[i] != NULL; i++)
|
||||||
|
{
|
||||||
|
nSuffixLength = sprintf(szLstSuffix, "%s-md5.lst", LanguageList[i]);
|
||||||
|
assert(nSuffixLength == 12);
|
||||||
|
|
||||||
|
if(!_stricmp(szFileName + nLength - nSuffixLength, szLstSuffix))
|
||||||
|
return LanguageList[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool FindPatchPrefix_WoW_13164_13623(TMPQArchive * haBase, TMPQArchive * haPatch)
|
||||||
|
{
|
||||||
|
TFileEntry * pFileEntry;
|
||||||
|
const char * szFilePrefix = "Base";
|
||||||
|
const char * szLanguage;
|
||||||
|
char szNamePrefix[0x10];
|
||||||
|
int nLength;
|
||||||
|
|
||||||
|
// Find a *-md5.lst file in the base archive
|
||||||
|
pFileEntry = FindMd5ListFile(haBase);
|
||||||
|
if(pFileEntry == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Language-specific MPQs have the language identifier right before extension
|
||||||
|
szLanguage = GetLstFileLanguage(pFileEntry->szFileName);
|
||||||
|
if(szLanguage != NULL)
|
||||||
|
szFilePrefix = szLanguage;
|
||||||
|
|
||||||
|
// Format the name prefix
|
||||||
|
nLength = sprintf(szNamePrefix, "%s\\", szFilePrefix);
|
||||||
|
return CreatePatchPrefix(haPatch, szNamePrefix, &szNamePrefix[nLength]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Find match in Starcraft II patch MPQs
|
||||||
|
// Match a LST file in the root directory if the MPQ with any of the file in subdirectories
|
||||||
|
//
|
||||||
|
// The problem:
|
||||||
|
// Base: enGB-md5.lst
|
||||||
|
// Patch: Campaigns\Liberty.SC2Campaign\enGB.SC2Assets\enGB-md5.lst
|
||||||
|
// Campaigns\Liberty.SC2Campaign\enGB.SC2Data\enGB-md5.lst
|
||||||
|
// Campaigns\LibertyStory.SC2Campaign\enGB.SC2Data\enGB-md5.lst
|
||||||
|
// Campaigns\LibertyStory.SC2Campaign\enGB.SC2Data\enGB-md5.lst Mods\Core.SC2Mod\enGB.SC2Assets\enGB-md5.lst
|
||||||
|
// Mods\Core.SC2Mod\enGB.SC2Data\enGB-md5.lst
|
||||||
|
// Mods\Liberty.SC2Mod\enGB.SC2Assets\enGB-md5.lst
|
||||||
|
// Mods\Liberty.SC2Mod\enGB.SC2Data\enGB-md5.lst
|
||||||
|
// Mods\LibertyMulti.SC2Mod\enGB.SC2Data\enGB-md5.lst
|
||||||
|
//
|
||||||
|
// Solution:
|
||||||
|
// We need to match the file by its MD5
|
||||||
|
//
|
||||||
|
|
||||||
|
static bool FindPatchPrefix_SC2(TMPQArchive * haBase, TMPQArchive * haPatch)
|
||||||
|
{
|
||||||
|
TFileEntry * pFileTableEnd;
|
||||||
|
TFileEntry * pFileEntry;
|
||||||
|
TFileEntry * pBaseEntry;
|
||||||
|
const char * szPlainName;
|
||||||
|
char * szLstFileName;
|
||||||
|
size_t cchWorkBuffer = 0x400;
|
||||||
|
size_t cchBaseName;
|
||||||
|
size_t cchDirName;
|
||||||
|
bool bResult = false;
|
||||||
|
|
||||||
|
// Find a *-md5.lst file in the base archive
|
||||||
|
pBaseEntry = FindMd5ListFile(haBase);
|
||||||
|
if(pBaseEntry == NULL)
|
||||||
|
return false;
|
||||||
|
cchBaseName = strlen(pBaseEntry->szFileName) + 1;
|
||||||
|
|
||||||
|
// Allocate working buffer for merging LST file
|
||||||
|
szLstFileName = STORM_ALLOC(char, cchWorkBuffer);
|
||||||
|
if(szLstFileName != NULL)
|
||||||
|
{
|
||||||
|
// Find that file in the patch MPQ
|
||||||
|
pFileTableEnd = haPatch->pFileTable + haPatch->dwFileTableSize;
|
||||||
|
for(pFileEntry = haPatch->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
||||||
|
{
|
||||||
|
// Find the "(patch_metadata)" file within that folder
|
||||||
|
// Note that the file is always relatively small and contains the patch prefix
|
||||||
|
// Checking for file size greatly speeds up the search process
|
||||||
|
if(pFileEntry->szFileName && !(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) && (0 < pFileEntry->dwFileSize && pFileEntry->dwFileSize < 0x40))
|
||||||
|
{
|
||||||
|
// If the plain file name matches, we need to check its MD5
|
||||||
|
szPlainName = GetPlainFileName(pFileEntry->szFileName);
|
||||||
|
cchDirName = (size_t)(szPlainName - pFileEntry->szFileName);
|
||||||
|
|
||||||
|
// The file name must not too long and must be PATCH_METADATA_NAME
|
||||||
|
if((cchDirName + cchBaseName) < cchWorkBuffer && _stricmp(szPlainName, PATCH_METADATA_NAME) == 0)
|
||||||
|
{
|
||||||
|
// Construct the name of the eventuall LST file
|
||||||
|
memcpy(szLstFileName, pFileEntry->szFileName, cchDirName);
|
||||||
|
memcpy(szLstFileName + cchDirName, pBaseEntry->szFileName, cchBaseName);
|
||||||
|
|
||||||
|
// If there is the "*-md5.lst" file in that directory, we check its MD5
|
||||||
|
if(IsMatchingPatchFile(haPatch, szLstFileName, pBaseEntry->md5))
|
||||||
|
{
|
||||||
|
bResult = CreatePatchPrefix(haPatch, pFileEntry->szFileName, szPlainName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the work buffer
|
||||||
|
STORM_FREE(szLstFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool FindPatchPrefix(TMPQArchive * haBase, TMPQArchive * haPatch, const char * szPatchPathPrefix)
|
||||||
|
{
|
||||||
|
// If the patch prefix was explicitly entered, we use that one
|
||||||
|
if(szPatchPathPrefix != NULL)
|
||||||
|
return CreatePatchPrefix(haPatch, szPatchPathPrefix, szPatchPathPrefix + strlen(szPatchPathPrefix));
|
||||||
|
|
||||||
|
// Patches for World of Warcraft - mostly the do not use prefix.
|
||||||
|
// Those who do, they have the (patch_metadata) file present in the "base" subdirectory.
|
||||||
|
// All patches that use patch prefix have the "base\\(patch_metadata) file present
|
||||||
|
if(GetFileEntryAny(haPatch, "base\\" PATCH_METADATA_NAME))
|
||||||
|
return FindPatchPrefix_WoW_13164_13623(haBase, haPatch);
|
||||||
|
|
||||||
|
// Updates for Starcraft II
|
||||||
|
// Match: LocalizedData\GameHotkeys.txt <==> Campaigns\Liberty.SC2Campaign\enGB.SC2Data\LocalizedData\GameHotkeys.txt
|
||||||
|
// All Starcraft II base archives seem to have the file "StreamingBuckets.txt" present
|
||||||
|
if(GetFileEntryAny(haBase, "StreamingBuckets.txt"))
|
||||||
|
return FindPatchPrefix_SC2(haBase, haPatch);
|
||||||
|
|
||||||
|
// Diablo III patch MPQs don't use patch prefix
|
||||||
|
// Hearthstone MPQs don't use patch prefix
|
||||||
|
CreatePatchPrefix(haPatch, NULL, NULL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Public functions (StormLib internals)
|
// Public functions (StormLib internals)
|
||||||
|
|
||||||
|
|
@ -425,34 +666,75 @@ bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatche
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Note: The patch may either be applied to the base file or to the previous version
|
||||||
|
// In Starcraft II, Mods\Core.SC2Mod\Base.SC2Data, file StreamingBuckets.txt:
|
||||||
|
//
|
||||||
|
// Base file MD5: 31376b0344b6df59ad009d4296125539
|
||||||
|
//
|
||||||
|
// s2-update-base-23258: from 31376b0344b6df59ad009d4296125539 to 941a82683452e54bf024a8d491501824
|
||||||
|
// s2-update-base-24540: from 31376b0344b6df59ad009d4296125539 to 941a82683452e54bf024a8d491501824
|
||||||
|
// s2-update-base-26147: from 31376b0344b6df59ad009d4296125539 to d5d5253c762fac6b9761240288a0771a
|
||||||
|
// s2-update-base-28522: from 31376b0344b6df59ad009d4296125539 to 5a76c4b356920aab7afd22e0e1913d7a
|
||||||
|
// s2-update-base-30508: from 31376b0344b6df59ad009d4296125539 to 8cb0d4799893fe801cc78ae4488a3671
|
||||||
|
// s2-update-base-32283: from 31376b0344b6df59ad009d4296125539 to 8cb0d4799893fe801cc78ae4488a3671
|
||||||
|
//
|
||||||
|
// We don't keep all intermediate versions in memory, as it would cause massive
|
||||||
|
// memory usage during patching process. A prime example is the file
|
||||||
|
// DBFilesClient\\Item-Sparse.db2 from locale-enGB.MPQ (WoW 16965), which has
|
||||||
|
// 9 patches in a row, each requiring 70 MB memory (35 MB patch data + 35 MB work buffer)
|
||||||
|
//
|
||||||
|
|
||||||
int PatchFileData(TMPQFile * hf)
|
int PatchFileData(TMPQFile * hf)
|
||||||
{
|
{
|
||||||
TMPQFile * hfBase = hf;
|
TMPQFile * hfBase = hf;
|
||||||
|
TMPQFile * hfPrev = hf;
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Move to the first patch
|
// We need to calculate the MD5 of the entire file
|
||||||
hf = hf->hfPatchFile;
|
assert(hf->pbFileData != NULL);
|
||||||
|
assert(hf->cbFileData != 0);
|
||||||
|
CalculateDataBlockHash(hf->pbFileData, hf->cbFileData, hf->FileDataMD5);
|
||||||
|
|
||||||
// Now go through all patches and patch the original data
|
// Apply all patches
|
||||||
while(hf != NULL)
|
for(hf = hf->hfPatch; hf != NULL; hf = hf->hfPatch)
|
||||||
{
|
{
|
||||||
// This must be true
|
// This must be true
|
||||||
assert(hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE);
|
assert(hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE);
|
||||||
|
|
||||||
// Make sure that the patch data is loaded
|
// Make sure that the patch data is loaded
|
||||||
nError = LoadMpqPatch(hf);
|
nError = LoadFilePatch(hf);
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Apply the patch
|
// Apply the patch
|
||||||
nError = ApplyMpqPatch(hfBase, hf->pPatchHeader);
|
nError = ApplyFilePatch(hfBase, hfPrev, hf);
|
||||||
if(nError != ERROR_SUCCESS)
|
if(nError != ERROR_SUCCESS)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Move to the next patch
|
// Only keep base file version and previous version
|
||||||
hf = hf->hfPatchFile;
|
if(hfPrev != hfBase)
|
||||||
|
FreePatchData(hfPrev);
|
||||||
|
|
||||||
|
// Is this the last patch in the chain?
|
||||||
|
if(hf->hfPatch == NULL)
|
||||||
|
break;
|
||||||
|
hfPrev = hf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When done, we need to rewrite the base file data
|
||||||
|
// with the last of the patch chain
|
||||||
|
if(nError == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
// Free the base file data
|
||||||
|
STORM_FREE(hfBase->pbFileData);
|
||||||
|
|
||||||
|
// Switch the latest patched data to the base file
|
||||||
|
hfBase->pbFileData = hf->pbFileData;
|
||||||
|
hfBase->cbFileData = hf->cbFileData;
|
||||||
|
hf->pbFileData = NULL;
|
||||||
|
hf->cbFileData = 0;
|
||||||
|
}
|
||||||
return nError;
|
return nError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -478,34 +760,25 @@ int PatchFileData(TMPQFile * hf)
|
||||||
//
|
//
|
||||||
|
|
||||||
bool WINAPI SFileOpenPatchArchive(
|
bool WINAPI SFileOpenPatchArchive(
|
||||||
HANDLE hMpq,
|
HANDLE hMpq,
|
||||||
const TCHAR * szPatchMpqName,
|
const TCHAR * szPatchMpqName,
|
||||||
const char * szPatchPathPrefix,
|
const char * szPatchPathPrefix,
|
||||||
DWORD dwFlags)
|
DWORD dwFlags)
|
||||||
{
|
{
|
||||||
TMPQArchive * haPatch;
|
TMPQArchive * haPatch;
|
||||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||||
HANDLE hPatchMpq = NULL;
|
HANDLE hPatchMpq = NULL;
|
||||||
char szPatchPrefixBuff[MPQ_PATCH_PREFIX_LEN];
|
|
||||||
int nError = ERROR_SUCCESS;
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
// Keep compiler happy
|
// Keep compiler happy
|
||||||
dwFlags = dwFlags;
|
dwFlags = dwFlags;
|
||||||
|
|
||||||
// Verify input parameters
|
// Verify input parameters
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
nError = ERROR_INVALID_HANDLE;
|
nError = ERROR_INVALID_HANDLE;
|
||||||
if(szPatchMpqName == NULL || *szPatchMpqName == 0)
|
if(szPatchMpqName == NULL || *szPatchMpqName == 0)
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
nError = ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
// If the user didn't give the patch prefix, get default one
|
|
||||||
if(szPatchPathPrefix != NULL)
|
|
||||||
{
|
|
||||||
// Save length of the patch prefix
|
|
||||||
if(strlen(szPatchPathPrefix) > MPQ_PATCH_PREFIX_LEN - 2)
|
|
||||||
nError = ERROR_INVALID_PARAMETER;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// We don't allow adding patches to archives that have been open for write
|
// We don't allow adding patches to archives that have been open for write
|
||||||
//
|
//
|
||||||
|
|
@ -520,38 +793,19 @@ bool WINAPI SFileOpenPatchArchive(
|
||||||
|
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
if(!FileStream_IsReadOnly(ha->pStream))
|
if(!(ha->dwFlags & MPQ_FLAG_READ_ONLY))
|
||||||
nError = ERROR_ACCESS_DENIED;
|
nError = ERROR_ACCESS_DENIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the archive like it is normal archive
|
// Open the archive like it is normal archive
|
||||||
if(nError == ERROR_SUCCESS)
|
if(nError == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
if(!SFileOpenArchive(szPatchMpqName, 0, MPQ_OPEN_READ_ONLY, &hPatchMpq))
|
if(!SFileOpenArchive(szPatchMpqName, 0, MPQ_OPEN_READ_ONLY | MPQ_OPEN_PATCH, &hPatchMpq))
|
||||||
return false;
|
return false;
|
||||||
haPatch = (TMPQArchive *)hPatchMpq;
|
haPatch = (TMPQArchive *)hPatchMpq;
|
||||||
|
|
||||||
// Older WoW patches (build 13914) used to have
|
// We need to remember the proper patch prefix to match names of patched files
|
||||||
// several language versions in one patch file
|
FindPatchPrefix(ha, (TMPQArchive *)hPatchMpq, szPatchPathPrefix);
|
||||||
// Those patches needed to have a path prefix
|
|
||||||
// We can distinguish such patches by not having the (patch_metadata) file
|
|
||||||
if(szPatchPathPrefix == NULL)
|
|
||||||
{
|
|
||||||
if(!SFileHasFile(hPatchMpq, PATCH_METADATA_NAME))
|
|
||||||
{
|
|
||||||
GetDefaultPatchPrefix(FileStream_GetFileName(ha->pStream), szPatchPrefixBuff);
|
|
||||||
szPatchPathPrefix = szPatchPrefixBuff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the prefix for patch file names.
|
|
||||||
// Make sure that there is backslash after it
|
|
||||||
if(szPatchPathPrefix != NULL && *szPatchPathPrefix != 0)
|
|
||||||
{
|
|
||||||
strcpy(haPatch->szPatchPrefix, szPatchPathPrefix);
|
|
||||||
strcat(haPatch->szPatchPrefix, "\\");
|
|
||||||
haPatch->cchPatchPrefix = strlen(haPatch->szPatchPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now add the patch archive to the list of patches to the original MPQ
|
// Now add the patch archive to the list of patches to the original MPQ
|
||||||
while(ha != NULL)
|
while(ha != NULL)
|
||||||
|
|
@ -580,7 +834,7 @@ bool WINAPI SFileIsPatchedArchive(HANDLE hMpq)
|
||||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||||
|
|
||||||
// Verify input parameters
|
// Verify input parameters
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return (ha->haPatch != NULL);
|
return (ha->haPatch != NULL);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -20,89 +20,83 @@
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Local defines
|
// Local defines
|
||||||
|
|
||||||
#define SIGNATURE_TYPE_NONE 0
|
|
||||||
#define SIGNATURE_TYPE_WEAK 1
|
|
||||||
#define SIGNATURE_TYPE_STRONG 2
|
|
||||||
|
|
||||||
#define MPQ_DIGEST_UNIT_SIZE 0x10000
|
#define MPQ_DIGEST_UNIT_SIZE 0x10000
|
||||||
|
|
||||||
typedef struct _MPQ_SIGNATURE_INFO
|
|
||||||
{
|
|
||||||
ULONGLONG BeginMpqData; // File offset where the hashing starts
|
|
||||||
ULONGLONG BeginExclude; // Begin of the excluded area (used for (signature) file)
|
|
||||||
ULONGLONG EndExclude; // End of the excluded area (used for (signature) file)
|
|
||||||
ULONGLONG EndMpqData; // File offset where the hashing ends
|
|
||||||
ULONGLONG EndOfFile; // Size of the entire file
|
|
||||||
BYTE Signature[MPQ_STRONG_SIGNATURE_SIZE + 0x10];
|
|
||||||
DWORD cbSignatureSize; // Length of the signature
|
|
||||||
int nSignatureType; // See SIGNATURE_TYPE_XXX
|
|
||||||
|
|
||||||
} MPQ_SIGNATURE_INFO, *PMPQ_SIGNATURE_INFO;
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Known Blizzard public keys
|
// Known Blizzard public keys
|
||||||
// Created by Jean-Francois Roy using OpenSSL
|
// Created by Jean-Francois Roy using OpenSSL
|
||||||
|
|
||||||
|
static const char * szBlizzardWeakPrivateKey =
|
||||||
|
"-----BEGIN PRIVATE KEY-----"
|
||||||
|
"MIIBOQIBAAJBAJJidwS/uILMBSO5DLGsBFknIXWWjQJe2kfdfEk3G/j66w4KkhZ1"
|
||||||
|
"V61Rt4zLaMVCYpDun7FLwRjkMDSepO1q2DcCAwEAAQJANtiztVDMJh2hE1hjPDKy"
|
||||||
|
"UmEJ9U/aN3gomuKOjbQbQ/bWWcM/WfhSVHmPqtqh/bQI2UXFr0rnXngeteZHLr/b"
|
||||||
|
"8QIhAMuWriSKGMACw18/rVVfUrThs915odKBH1Alr3vMVVzZAiEAuBHPSQkgwcb6"
|
||||||
|
"L4MWaiKuOzq08mSyNqPeN8oSy18q848CIHeMn+3s+eOmu7su1UYQl6yH7OrdBd1q"
|
||||||
|
"3UxfFNEJiAbhAiAqxdCyOxHGlbM7aS3DOg3cq5ayoN2cvtV7h1R4t8OmVwIgF+5z"
|
||||||
|
"/6vkzBUsZhd8Nwyis+MeQYH0rpFpMKdTlqmPF2Q="
|
||||||
|
"-----END PRIVATE KEY-----";
|
||||||
|
|
||||||
static const char * szBlizzardWeakPublicKey =
|
static const char * szBlizzardWeakPublicKey =
|
||||||
"-----BEGIN PUBLIC KEY-----"
|
"-----BEGIN PUBLIC KEY-----"
|
||||||
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJJidwS/uILMBSO5DLGsBFknIXWWjQJe"
|
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJJidwS/uILMBSO5DLGsBFknIXWWjQJe"
|
||||||
"2kfdfEk3G/j66w4KkhZ1V61Rt4zLaMVCYpDun7FLwRjkMDSepO1q2DcCAwEAAQ=="
|
"2kfdfEk3G/j66w4KkhZ1V61Rt4zLaMVCYpDun7FLwRjkMDSepO1q2DcCAwEAAQ=="
|
||||||
"-----END PUBLIC KEY-----";
|
"-----END PUBLIC KEY-----";
|
||||||
|
|
||||||
static const char * szBlizzardStrongPublicKey =
|
static const char * szBlizzardStrongPublicKey =
|
||||||
"-----BEGIN PUBLIC KEY-----"
|
"-----BEGIN PUBLIC KEY-----"
|
||||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsQZ+ziT2h8h+J/iMQpgd"
|
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsQZ+ziT2h8h+J/iMQpgd"
|
||||||
"tH1HaJzOBE3agjU4yMPcrixaPOZoA4t8bwfey7qczfWywocYo3pleytFF+IuD4HD"
|
"tH1HaJzOBE3agjU4yMPcrixaPOZoA4t8bwfey7qczfWywocYo3pleytFF+IuD4HD"
|
||||||
"Fl9OXN1SFyupSgMx1EGZlgbFAomnbq9MQJyMqQtMhRAjFgg4TndS7YNb+JMSAEKp"
|
"Fl9OXN1SFyupSgMx1EGZlgbFAomnbq9MQJyMqQtMhRAjFgg4TndS7YNb+JMSAEKp"
|
||||||
"kXNqY28n/EVBHD5TsMuVCL579gIenbr61dI92DDEdy790IzIG0VKWLh/KOTcTJfm"
|
"kXNqY28n/EVBHD5TsMuVCL579gIenbr61dI92DDEdy790IzIG0VKWLh/KOTcTJfm"
|
||||||
"Ds/7HQTkGouVW+WUsfekuqNQo7ND9DBnhLjLjptxeFE2AZqYcA1ao3S9LN3GL1tW"
|
"Ds/7HQTkGouVW+WUsfekuqNQo7ND9DBnhLjLjptxeFE2AZqYcA1ao3S9LN3GL1tW"
|
||||||
"lVXFIX9c7fWqaVTQlZ2oNsI/ARVApOK3grNgqvwH6YoVYVXjNJEo5sQJsPsdV/hk"
|
"lVXFIX9c7fWqaVTQlZ2oNsI/ARVApOK3grNgqvwH6YoVYVXjNJEo5sQJsPsdV/hk"
|
||||||
"dwIDAQAB"
|
"dwIDAQAB"
|
||||||
"-----END PUBLIC KEY-----";
|
"-----END PUBLIC KEY-----";
|
||||||
|
|
||||||
static const char * szWarcraft3MapPublicKey =
|
static const char * szWarcraft3MapPublicKey =
|
||||||
"-----BEGIN PUBLIC KEY-----"
|
"-----BEGIN PUBLIC KEY-----"
|
||||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1BwklUUQ3UvjizOBRoF5"
|
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1BwklUUQ3UvjizOBRoF5"
|
||||||
"yyOVc7KD+oGOQH5i6eUk1yfs0luCC70kNucNrfqhmviywVtahRse1JtXCPrx2bd3"
|
"yyOVc7KD+oGOQH5i6eUk1yfs0luCC70kNucNrfqhmviywVtahRse1JtXCPrx2bd3"
|
||||||
"iN8Dx91fbkxjYIOGTsjYoHKTp0BbaFkJih776fcHgnFSb+7mJcDuJVvJOXxEH6w0"
|
"iN8Dx91fbkxjYIOGTsjYoHKTp0BbaFkJih776fcHgnFSb+7mJcDuJVvJOXxEH6w0"
|
||||||
"1vo6VtujCqj1arqbyoal+xtAaczF3us5cOEp45sR1zAWTn1+7omN7VWV4QqJPaDS"
|
"1vo6VtujCqj1arqbyoal+xtAaczF3us5cOEp45sR1zAWTn1+7omN7VWV4QqJPaDS"
|
||||||
"gBSESc0l1grO0i1VUSumayk7yBKIkb+LBvcG6WnYZHCi7VdLmaxER5m8oZfER66b"
|
"gBSESc0l1grO0i1VUSumayk7yBKIkb+LBvcG6WnYZHCi7VdLmaxER5m8oZfER66b"
|
||||||
"heHoiSQIZf9PAY6Guw2DT5BTc54j/AaLQAKf2qcRSgQLVo5kQaddF3rCpsXoB/74"
|
"heHoiSQIZf9PAY6Guw2DT5BTc54j/AaLQAKf2qcRSgQLVo5kQaddF3rCpsXoB/74"
|
||||||
"6QIDAQAB"
|
"6QIDAQAB"
|
||||||
"-----END PUBLIC KEY-----";
|
"-----END PUBLIC KEY-----";
|
||||||
|
|
||||||
static const char * szWowPatchPublicKey =
|
static const char * szWowPatchPublicKey =
|
||||||
"-----BEGIN PUBLIC KEY-----"
|
"-----BEGIN PUBLIC KEY-----"
|
||||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwOsMV0LagAWPEtEQM6b9"
|
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwOsMV0LagAWPEtEQM6b9"
|
||||||
"6FHFkUyGbbyda2/Dfc9dyl21E9QvX+Yw7qKRMAKPzA2TlQQLZKvXpnKXF/YIK5xa"
|
"6FHFkUyGbbyda2/Dfc9dyl21E9QvX+Yw7qKRMAKPzA2TlQQLZKvXpnKXF/YIK5xa"
|
||||||
"5uwg9CEHCEAYolLG4xn0FUOE0E/0PuuytI0p0ICe6rk00PifZzTr8na2wI/l/GnQ"
|
"5uwg9CEHCEAYolLG4xn0FUOE0E/0PuuytI0p0ICe6rk00PifZzTr8na2wI/l/GnQ"
|
||||||
"bvnIVF1ck6cslATpQJ5JJVMXzoFlUABS19WESw4MXuJAS3AbMhxNWdEhVv7eO51c"
|
"bvnIVF1ck6cslATpQJ5JJVMXzoFlUABS19WESw4MXuJAS3AbMhxNWdEhVv7eO51c"
|
||||||
"yGjRLy9QjogZODZTY0fSEksgBqQxNCoYVJYI/sF5K2flDsGqrIp0OdJ6teJlzg1Y"
|
"yGjRLy9QjogZODZTY0fSEksgBqQxNCoYVJYI/sF5K2flDsGqrIp0OdJ6teJlzg1Y"
|
||||||
"UjYnb6bKjlidXoHEXI2TgA/mD6O3XFIt08I9s3crOCTgICq7cgX35qrZiIVWZdRv"
|
"UjYnb6bKjlidXoHEXI2TgA/mD6O3XFIt08I9s3crOCTgICq7cgX35qrZiIVWZdRv"
|
||||||
"TwIDAQAB"
|
"TwIDAQAB"
|
||||||
"-----END PUBLIC KEY-----";
|
"-----END PUBLIC KEY-----";
|
||||||
|
|
||||||
static const char * szWowSurveyPublicKey =
|
static const char * szWowSurveyPublicKey =
|
||||||
"-----BEGIN PUBLIC KEY-----"
|
"-----BEGIN PUBLIC KEY-----"
|
||||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnIt1DR6nRyyKsy2qahHe"
|
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnIt1DR6nRyyKsy2qahHe"
|
||||||
"MKLtacatn/KxieHcwH87wLBxKy+jZ0gycTmJ7SaTdBAEMDs/V5IPIXEtoqYnid2c"
|
"MKLtacatn/KxieHcwH87wLBxKy+jZ0gycTmJ7SaTdBAEMDs/V5IPIXEtoqYnid2c"
|
||||||
"63TmfGDU92oc3Ph1PWUZ2PWxBhT06HYxRdbrgHw9/I29pNPi/607x+lzPORITOgU"
|
"63TmfGDU92oc3Ph1PWUZ2PWxBhT06HYxRdbrgHw9/I29pNPi/607x+lzPORITOgU"
|
||||||
"BR6MR8au8HsQP4bn4vkJNgnSgojh48/XQOB/cAln7As1neP61NmVimoLR4Bwi3zt"
|
"BR6MR8au8HsQP4bn4vkJNgnSgojh48/XQOB/cAln7As1neP61NmVimoLR4Bwi3zt"
|
||||||
"zfgrZaUpyeNCUrOYJmH09YIjbBySTtXOUidoPHjFrMsCWpr6xs8xbETbs7MJFL6a"
|
"zfgrZaUpyeNCUrOYJmH09YIjbBySTtXOUidoPHjFrMsCWpr6xs8xbETbs7MJFL6a"
|
||||||
"vcUfTT67qfIZ9RsuKfnXJTIrV0kwDSjjuNXiPTmWAehSsiHIsrUXX5RNcwsSjClr"
|
"vcUfTT67qfIZ9RsuKfnXJTIrV0kwDSjjuNXiPTmWAehSsiHIsrUXX5RNcwsSjClr"
|
||||||
"nQIDAQAB"
|
"nQIDAQAB"
|
||||||
"-----END PUBLIC KEY-----";
|
"-----END PUBLIC KEY-----";
|
||||||
|
|
||||||
static const char * szStarcraft2MapPublicKey =
|
static const char * szStarcraft2MapPublicKey =
|
||||||
"-----BEGIN PUBLIC KEY-----"
|
"-----BEGIN PUBLIC KEY-----"
|
||||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmk4GT8zb+ICC25a17KZB"
|
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmk4GT8zb+ICC25a17KZB"
|
||||||
"q/ygKGJ2VSO6IT5PGHJlm1KfnHBA4B6SH3xMlJ4c6eG2k7QevZv+FOhjsAHubyWq"
|
"q/ygKGJ2VSO6IT5PGHJlm1KfnHBA4B6SH3xMlJ4c6eG2k7QevZv+FOhjsAHubyWq"
|
||||||
"2VKqWbrIFKv2ILc2RfMn8J9EDVRxvcxh6slRrVL69D0w1tfVGjMiKq2Fym5yGoRT"
|
"2VKqWbrIFKv2ILc2RfMn8J9EDVRxvcxh6slRrVL69D0w1tfVGjMiKq2Fym5yGoRT"
|
||||||
"E7CRgDqbAbXP9LBsCNWHiJLwfxMGzHbk8pIl9oia5pvM7ofZamSHchxlpy6xa4GJ"
|
"E7CRgDqbAbXP9LBsCNWHiJLwfxMGzHbk8pIl9oia5pvM7ofZamSHchxlpy6xa4GJ"
|
||||||
"7xKN01YCNvklTL1D7uol3wkwcHc7vrF8QwuJizuA5bSg4poEGtH62BZOYi+UL/z0"
|
"7xKN01YCNvklTL1D7uol3wkwcHc7vrF8QwuJizuA5bSg4poEGtH62BZOYi+UL/z0"
|
||||||
"31YK+k9CbQyM0X0pJoJoYz1TK+Y5J7vBnXCZtfcTYQ/ZzN6UcxTa57dJaiOlCh9z"
|
"31YK+k9CbQyM0X0pJoJoYz1TK+Y5J7vBnXCZtfcTYQ/ZzN6UcxTa57dJaiOlCh9z"
|
||||||
"nQIDAQAB"
|
"nQIDAQAB"
|
||||||
"-----END PUBLIC KEY-----";
|
"-----END PUBLIC KEY-----";
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Local functions
|
// Local functions
|
||||||
|
|
@ -119,13 +113,6 @@ static void memrev(unsigned char *buf, size_t count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_valid_md5(void * pvMd5)
|
|
||||||
{
|
|
||||||
LPDWORD Md5 = (LPDWORD)pvMd5;
|
|
||||||
|
|
||||||
return (Md5[0] | Md5[1] | Md5[2] | Md5[3]) ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool decode_base64_key(const char * szKeyBase64, rsa_key * key)
|
static bool decode_base64_key(const char * szKeyBase64, rsa_key * key)
|
||||||
{
|
{
|
||||||
unsigned char decoded_key[0x200];
|
unsigned char decoded_key[0x200];
|
||||||
|
|
@ -153,10 +140,10 @@ static bool decode_base64_key(const char * szKeyBase64, rsa_key * key)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void GetPlainAnsiFileName(
|
static void GetPlainAnsiFileName(
|
||||||
const TCHAR * szFileName,
|
const TCHAR * szFileName,
|
||||||
char * szPlainName)
|
char * szPlainName)
|
||||||
{
|
{
|
||||||
const TCHAR * szPlainNameT = GetPlainFileNameT(szFileName);
|
const TCHAR * szPlainNameT = GetPlainFileName(szFileName);
|
||||||
|
|
||||||
// Convert the plain name to ANSI
|
// Convert the plain name to ANSI
|
||||||
while(*szPlainNameT != 0)
|
while(*szPlainNameT != 0)
|
||||||
|
|
@ -166,8 +153,8 @@ static void GetPlainAnsiFileName(
|
||||||
|
|
||||||
// Calculate begin and end of the MPQ archive
|
// Calculate begin and end of the MPQ archive
|
||||||
static void CalculateArchiveRange(
|
static void CalculateArchiveRange(
|
||||||
TMPQArchive * ha,
|
TMPQArchive * ha,
|
||||||
PMPQ_SIGNATURE_INFO pSI)
|
PMPQ_SIGNATURE_INFO pSI)
|
||||||
{
|
{
|
||||||
ULONGLONG TempPos = 0;
|
ULONGLONG TempPos = 0;
|
||||||
char szMapHeader[0x200];
|
char szMapHeader[0x200];
|
||||||
|
|
@ -186,71 +173,17 @@ static void CalculateArchiveRange(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the MPQ data end. This is stored in our MPQ header,
|
// Get the MPQ data end. This is stored in the MPQ header
|
||||||
// and it's been already prepared by SFileOpenArchive,
|
|
||||||
pSI->EndMpqData = ha->MpqPos + ha->pHeader->ArchiveSize64;
|
pSI->EndMpqData = ha->MpqPos + ha->pHeader->ArchiveSize64;
|
||||||
|
|
||||||
// Get the size of the entire file
|
// Get the size of the entire file
|
||||||
FileStream_GetSize(ha->pStream, &pSI->EndOfFile);
|
FileStream_GetSize(ha->pStream, &pSI->EndOfFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool QueryMpqSignatureInfo(
|
|
||||||
TMPQArchive * ha,
|
|
||||||
PMPQ_SIGNATURE_INFO pSI)
|
|
||||||
{
|
|
||||||
ULONGLONG ExtraBytes;
|
|
||||||
TMPQFile * hf;
|
|
||||||
HANDLE hFile;
|
|
||||||
DWORD dwFileSize;
|
|
||||||
|
|
||||||
// Calculate the range of the MPQ
|
|
||||||
CalculateArchiveRange(ha, pSI);
|
|
||||||
|
|
||||||
// If there is "(signature)" file in the MPQ, it has a weak signature
|
|
||||||
if(SFileOpenFileEx((HANDLE)ha, SIGNATURE_NAME, SFILE_OPEN_FROM_MPQ, &hFile))
|
|
||||||
{
|
|
||||||
// Get the content of the signature
|
|
||||||
SFileReadFile(hFile, pSI->Signature, sizeof(pSI->Signature), &pSI->cbSignatureSize, NULL);
|
|
||||||
|
|
||||||
// Verify the size of the signature
|
|
||||||
hf = (TMPQFile *)hFile;
|
|
||||||
|
|
||||||
// We have to exclude the signature file from the digest
|
|
||||||
pSI->BeginExclude = ha->MpqPos + hf->pFileEntry->ByteOffset;
|
|
||||||
pSI->EndExclude = pSI->BeginExclude + hf->pFileEntry->dwCmpSize;
|
|
||||||
dwFileSize = hf->dwDataSize;
|
|
||||||
|
|
||||||
// Close the file
|
|
||||||
SFileCloseFile(hFile);
|
|
||||||
pSI->nSignatureType = SIGNATURE_TYPE_WEAK;
|
|
||||||
return (dwFileSize == (MPQ_WEAK_SIGNATURE_SIZE + 8)) ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is extra bytes beyond the end of the archive,
|
|
||||||
// it's the strong signature
|
|
||||||
ExtraBytes = pSI->EndOfFile - pSI->EndMpqData;
|
|
||||||
if(ExtraBytes >= (MPQ_STRONG_SIGNATURE_SIZE + 4))
|
|
||||||
{
|
|
||||||
// Read the strong signature
|
|
||||||
if(!FileStream_Read(ha->pStream, &pSI->EndMpqData, pSI->Signature, (MPQ_STRONG_SIGNATURE_SIZE + 4)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check the signature header "NGIS"
|
|
||||||
if(pSI->Signature[0] != 'N' || pSI->Signature[1] != 'G' || pSI->Signature[2] != 'I' || pSI->Signature[3] != 'S')
|
|
||||||
return false;
|
|
||||||
|
|
||||||
pSI->nSignatureType = SIGNATURE_TYPE_STRONG;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Succeeded, but no known signature found
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool CalculateMpqHashMd5(
|
static bool CalculateMpqHashMd5(
|
||||||
TMPQArchive * ha,
|
TMPQArchive * ha,
|
||||||
PMPQ_SIGNATURE_INFO pSI,
|
PMPQ_SIGNATURE_INFO pSI,
|
||||||
LPBYTE pMd5Digest)
|
LPBYTE pMd5Digest)
|
||||||
{
|
{
|
||||||
hash_state md5_state;
|
hash_state md5_state;
|
||||||
ULONGLONG BeginBuffer;
|
ULONGLONG BeginBuffer;
|
||||||
|
|
@ -283,7 +216,7 @@ static bool CalculateMpqHashMd5(
|
||||||
if(dwToRead == 0)
|
if(dwToRead == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Read the next chunk
|
// Read the next chunk
|
||||||
if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead))
|
if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead))
|
||||||
{
|
{
|
||||||
STORM_FREE(pbDigestBuffer);
|
STORM_FREE(pbDigestBuffer);
|
||||||
|
|
@ -324,17 +257,18 @@ static bool CalculateMpqHashMd5(
|
||||||
}
|
}
|
||||||
|
|
||||||
static void AddTailToSha1(
|
static void AddTailToSha1(
|
||||||
hash_state * psha1_state,
|
hash_state * psha1_state,
|
||||||
const char * szTail)
|
const char * szTail)
|
||||||
{
|
{
|
||||||
|
unsigned char * pbTail = (unsigned char *)szTail;
|
||||||
unsigned char szUpperCase[0x200];
|
unsigned char szUpperCase[0x200];
|
||||||
unsigned long nLength = 0;
|
unsigned long nLength = 0;
|
||||||
|
|
||||||
// Convert the tail to uppercase
|
// Convert the tail to uppercase
|
||||||
// Note that we don't need to terminate the string with zero
|
// Note that we don't need to terminate the string with zero
|
||||||
while(*szTail != 0)
|
while(*pbTail != 0)
|
||||||
{
|
{
|
||||||
szUpperCase[nLength++] = (unsigned char)toupper(*szTail++);
|
szUpperCase[nLength++] = AsciiToUpperTable[*pbTail++];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append the tail to the SHA1
|
// Append the tail to the SHA1
|
||||||
|
|
@ -342,11 +276,11 @@ static void AddTailToSha1(
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CalculateMpqHashSha1(
|
static bool CalculateMpqHashSha1(
|
||||||
TMPQArchive * ha,
|
TMPQArchive * ha,
|
||||||
PMPQ_SIGNATURE_INFO pSI,
|
PMPQ_SIGNATURE_INFO pSI,
|
||||||
unsigned char * sha1_tail0,
|
unsigned char * sha1_tail0,
|
||||||
unsigned char * sha1_tail1,
|
unsigned char * sha1_tail1,
|
||||||
unsigned char * sha1_tail2)
|
unsigned char * sha1_tail2)
|
||||||
{
|
{
|
||||||
ULONGLONG BeginBuffer;
|
ULONGLONG BeginBuffer;
|
||||||
hash_state sha1_state_temp;
|
hash_state sha1_state_temp;
|
||||||
|
|
@ -378,7 +312,7 @@ static bool CalculateMpqHashSha1(
|
||||||
if(dwToRead == 0)
|
if(dwToRead == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Read the next chunk
|
// Read the next chunk
|
||||||
if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead))
|
if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead))
|
||||||
{
|
{
|
||||||
STORM_FREE(pbDigestBuffer);
|
STORM_FREE(pbDigestBuffer);
|
||||||
|
|
@ -411,9 +345,9 @@ static bool CalculateMpqHashSha1(
|
||||||
}
|
}
|
||||||
|
|
||||||
static int VerifyRawMpqData(
|
static int VerifyRawMpqData(
|
||||||
TMPQArchive * ha,
|
TMPQArchive * ha,
|
||||||
ULONGLONG ByteOffset,
|
ULONGLONG ByteOffset,
|
||||||
DWORD dwDataSize)
|
DWORD dwDataSize)
|
||||||
{
|
{
|
||||||
ULONGLONG DataOffset = ha->MpqPos + ByteOffset;
|
ULONGLONG DataOffset = ha->MpqPos + ByteOffset;
|
||||||
LPBYTE pbDataChunk;
|
LPBYTE pbDataChunk;
|
||||||
|
|
@ -499,8 +433,8 @@ static int VerifyRawMpqData(
|
||||||
}
|
}
|
||||||
|
|
||||||
static DWORD VerifyWeakSignature(
|
static DWORD VerifyWeakSignature(
|
||||||
TMPQArchive * ha,
|
TMPQArchive * ha,
|
||||||
PMPQ_SIGNATURE_INFO pSI)
|
PMPQ_SIGNATURE_INFO pSI)
|
||||||
{
|
{
|
||||||
BYTE RevSignature[MPQ_WEAK_SIGNATURE_SIZE];
|
BYTE RevSignature[MPQ_WEAK_SIGNATURE_SIZE];
|
||||||
BYTE Md5Digest[MD5_DIGEST_SIZE];
|
BYTE Md5Digest[MD5_DIGEST_SIZE];
|
||||||
|
|
@ -527,9 +461,9 @@ static DWORD VerifyWeakSignature(
|
||||||
}
|
}
|
||||||
|
|
||||||
static DWORD VerifyStrongSignatureWithKey(
|
static DWORD VerifyStrongSignatureWithKey(
|
||||||
unsigned char * reversed_signature,
|
unsigned char * reversed_signature,
|
||||||
unsigned char * padded_digest,
|
unsigned char * padded_digest,
|
||||||
const char * szPublicKey)
|
const char * szPublicKey)
|
||||||
{
|
{
|
||||||
rsa_key key;
|
rsa_key key;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
@ -544,15 +478,15 @@ static DWORD VerifyStrongSignatureWithKey(
|
||||||
// Verify the signature
|
// Verify the signature
|
||||||
if(rsa_verify_simple(reversed_signature, MPQ_STRONG_SIGNATURE_SIZE, padded_digest, MPQ_STRONG_SIGNATURE_SIZE, &result, &key) != CRYPT_OK)
|
if(rsa_verify_simple(reversed_signature, MPQ_STRONG_SIGNATURE_SIZE, padded_digest, MPQ_STRONG_SIGNATURE_SIZE, &result, &key) != CRYPT_OK)
|
||||||
return ERROR_VERIFY_FAILED;
|
return ERROR_VERIFY_FAILED;
|
||||||
|
|
||||||
// Free the key and return result
|
// Free the key and return result
|
||||||
rsa_free(&key);
|
rsa_free(&key);
|
||||||
return result ? ERROR_STRONG_SIGNATURE_OK : ERROR_STRONG_SIGNATURE_ERROR;
|
return result ? ERROR_STRONG_SIGNATURE_OK : ERROR_STRONG_SIGNATURE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DWORD VerifyStrongSignature(
|
static DWORD VerifyStrongSignature(
|
||||||
TMPQArchive * ha,
|
TMPQArchive * ha,
|
||||||
PMPQ_SIGNATURE_INFO pSI)
|
PMPQ_SIGNATURE_INFO pSI)
|
||||||
{
|
{
|
||||||
unsigned char reversed_signature[MPQ_STRONG_SIGNATURE_SIZE];
|
unsigned char reversed_signature[MPQ_STRONG_SIGNATURE_SIZE];
|
||||||
unsigned char Sha1Digest_tail0[SHA1_DIGEST_SIZE];
|
unsigned char Sha1Digest_tail0[SHA1_DIGEST_SIZE];
|
||||||
|
|
@ -614,11 +548,11 @@ static DWORD VerifyStrongSignature(
|
||||||
}
|
}
|
||||||
|
|
||||||
static DWORD VerifyFile(
|
static DWORD VerifyFile(
|
||||||
HANDLE hMpq,
|
HANDLE hMpq,
|
||||||
const char * szFileName,
|
const char * szFileName,
|
||||||
LPDWORD pdwCrc32,
|
LPDWORD pdwCrc32,
|
||||||
char * pMD5,
|
char * pMD5,
|
||||||
DWORD dwFlags)
|
DWORD dwFlags)
|
||||||
{
|
{
|
||||||
hash_state md5_state;
|
hash_state md5_state;
|
||||||
unsigned char * pFileMd5;
|
unsigned char * pFileMd5;
|
||||||
|
|
@ -628,14 +562,17 @@ static DWORD VerifyFile(
|
||||||
BYTE Buffer[0x1000];
|
BYTE Buffer[0x1000];
|
||||||
HANDLE hFile = NULL;
|
HANDLE hFile = NULL;
|
||||||
DWORD dwVerifyResult = 0;
|
DWORD dwVerifyResult = 0;
|
||||||
DWORD dwSearchScope = SFILE_OPEN_FROM_MPQ;
|
|
||||||
DWORD dwTotalBytes = 0;
|
DWORD dwTotalBytes = 0;
|
||||||
DWORD dwBytesRead;
|
DWORD dwBytesRead;
|
||||||
DWORD dwCrc32 = 0;
|
DWORD dwCrc32 = 0;
|
||||||
|
|
||||||
// Fix the open type for patched archives
|
//
|
||||||
if(SFileIsPatchedArchive(hMpq))
|
// Note: When the MPQ is patched, it will
|
||||||
dwSearchScope = SFILE_OPEN_PATCHED_FILE;
|
// automatically check the patched version of the file
|
||||||
|
//
|
||||||
|
|
||||||
|
// Make sure the md5 is initialized
|
||||||
|
memset(md5, 0, sizeof(md5));
|
||||||
|
|
||||||
// If we have to verify raw data MD5, do it before file open
|
// If we have to verify raw data MD5, do it before file open
|
||||||
if(dwFlags & SFILE_VERIFY_RAW_MD5)
|
if(dwFlags & SFILE_VERIFY_RAW_MD5)
|
||||||
|
|
@ -667,7 +604,7 @@ static DWORD VerifyFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to open the file
|
// Attempt to open the file
|
||||||
if(SFileOpenFileEx(hMpq, szFileName, dwSearchScope, &hFile))
|
if(SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_FROM_MPQ, &hFile))
|
||||||
{
|
{
|
||||||
// Get the file size
|
// Get the file size
|
||||||
hf = (TMPQFile *)hFile;
|
hf = (TMPQFile *)hFile;
|
||||||
|
|
@ -697,7 +634,7 @@ static DWORD VerifyFile(
|
||||||
// Update CRC32 value
|
// Update CRC32 value
|
||||||
if(dwFlags & SFILE_VERIFY_FILE_CRC)
|
if(dwFlags & SFILE_VERIFY_FILE_CRC)
|
||||||
dwCrc32 = crc32(dwCrc32, Buffer, dwBytesRead);
|
dwCrc32 = crc32(dwCrc32, Buffer, dwBytesRead);
|
||||||
|
|
||||||
// Update MD5 value
|
// Update MD5 value
|
||||||
if(dwFlags & SFILE_VERIFY_FILE_MD5)
|
if(dwFlags & SFILE_VERIFY_FILE_MD5)
|
||||||
md5_process(&md5_state, Buffer, dwBytesRead);
|
md5_process(&md5_state, Buffer, dwBytesRead);
|
||||||
|
|
@ -719,7 +656,7 @@ static DWORD VerifyFile(
|
||||||
if(dwTotalBytes == 0)
|
if(dwTotalBytes == 0)
|
||||||
{
|
{
|
||||||
// Check CRC32 and MD5 only if there is no patches
|
// Check CRC32 and MD5 only if there is no patches
|
||||||
if(hf->hfPatchFile == NULL)
|
if(hf->hfPatch == NULL)
|
||||||
{
|
{
|
||||||
// Check if the CRC32 matches.
|
// Check if the CRC32 matches.
|
||||||
if(dwFlags & SFILE_VERIFY_FILE_CRC)
|
if(dwFlags & SFILE_VERIFY_FILE_CRC)
|
||||||
|
|
@ -741,7 +678,7 @@ static DWORD VerifyFile(
|
||||||
md5_done(&md5_state, md5);
|
md5_done(&md5_state, md5);
|
||||||
|
|
||||||
// Only check the MD5 if it is valid
|
// Only check the MD5 if it is valid
|
||||||
if(is_valid_md5(pFileMd5))
|
if(IsValidMD5(pFileMd5))
|
||||||
{
|
{
|
||||||
dwVerifyResult |= VERIFY_FILE_HAS_MD5;
|
dwVerifyResult |= VERIFY_FILE_HAS_MD5;
|
||||||
if(memcmp(md5, pFileMd5, MD5_DIGEST_SIZE))
|
if(memcmp(md5, pFileMd5, MD5_DIGEST_SIZE))
|
||||||
|
|
@ -772,11 +709,159 @@ static DWORD VerifyFile(
|
||||||
if(pdwCrc32 != NULL)
|
if(pdwCrc32 != NULL)
|
||||||
*pdwCrc32 = dwCrc32;
|
*pdwCrc32 = dwCrc32;
|
||||||
if(pMD5 != NULL)
|
if(pMD5 != NULL)
|
||||||
memcpy(pMD5, md5, MD5_DIGEST_SIZE);
|
memcpy(pMD5, md5, MD5_DIGEST_SIZE);
|
||||||
|
|
||||||
return dwVerifyResult;
|
return dwVerifyResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used in SFileGetFileInfo
|
||||||
|
bool QueryMpqSignatureInfo(
|
||||||
|
TMPQArchive * ha,
|
||||||
|
PMPQ_SIGNATURE_INFO pSI)
|
||||||
|
{
|
||||||
|
TFileEntry * pFileEntry;
|
||||||
|
ULONGLONG ExtraBytes;
|
||||||
|
DWORD dwFileSize;
|
||||||
|
|
||||||
|
// Make sure it's all zeroed
|
||||||
|
memset(pSI, 0, sizeof(MPQ_SIGNATURE_INFO));
|
||||||
|
|
||||||
|
// Calculate the range of the MPQ
|
||||||
|
CalculateArchiveRange(ha, pSI);
|
||||||
|
|
||||||
|
// If there is "(signature)" file in the MPQ, it has a weak signature
|
||||||
|
pFileEntry = GetFileEntryLocale(ha, SIGNATURE_NAME, LANG_NEUTRAL);
|
||||||
|
if(pFileEntry != NULL)
|
||||||
|
{
|
||||||
|
// Calculate the begin and end of the signature file itself
|
||||||
|
pSI->BeginExclude = ha->MpqPos + pFileEntry->ByteOffset;
|
||||||
|
pSI->EndExclude = pSI->BeginExclude + pFileEntry->dwCmpSize;
|
||||||
|
dwFileSize = (DWORD)(pSI->EndExclude - pSI->BeginExclude);
|
||||||
|
|
||||||
|
// Does the signature have proper size?
|
||||||
|
if(dwFileSize == MPQ_SIGNATURE_FILE_SIZE)
|
||||||
|
{
|
||||||
|
// Read the weak signature
|
||||||
|
if(!FileStream_Read(ha->pStream, &pSI->BeginExclude, pSI->Signature, dwFileSize))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pSI->cbSignatureSize = dwFileSize;
|
||||||
|
pSI->SignatureTypes |= SIGNATURE_TYPE_WEAK;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is extra bytes beyond the end of the archive,
|
||||||
|
// it's the strong signature
|
||||||
|
ExtraBytes = pSI->EndOfFile - pSI->EndMpqData;
|
||||||
|
if(ExtraBytes >= (MPQ_STRONG_SIGNATURE_SIZE + 4))
|
||||||
|
{
|
||||||
|
// Read the strong signature
|
||||||
|
if(!FileStream_Read(ha->pStream, &pSI->EndMpqData, pSI->Signature, (MPQ_STRONG_SIGNATURE_SIZE + 4)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check the signature header "NGIS"
|
||||||
|
if(pSI->Signature[0] != 'N' || pSI->Signature[1] != 'G' || pSI->Signature[2] != 'I' || pSI->Signature[3] != 'S')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pSI->SignatureTypes |= SIGNATURE_TYPE_STRONG;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Succeeded, but no known signature found
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Support for weak signature
|
||||||
|
|
||||||
|
int SSignFileCreate(TMPQArchive * ha)
|
||||||
|
{
|
||||||
|
TMPQFile * hf = NULL;
|
||||||
|
BYTE EmptySignature[MPQ_SIGNATURE_FILE_SIZE];
|
||||||
|
int nError = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// Only save the signature if we should do so
|
||||||
|
if(ha->dwFileFlags3 != 0)
|
||||||
|
{
|
||||||
|
// The (signature) file must be non-encrypted and non-compressed
|
||||||
|
assert(ha->dwFileFlags3 == MPQ_FILE_EXISTS);
|
||||||
|
|
||||||
|
// Create the (signature) file file in the MPQ
|
||||||
|
// Note that the file must not be compressed or encrypted
|
||||||
|
nError = SFileAddFile_Init(ha, SIGNATURE_NAME,
|
||||||
|
0,
|
||||||
|
sizeof(EmptySignature),
|
||||||
|
LANG_NEUTRAL,
|
||||||
|
ha->dwFileFlags3 | MPQ_FILE_REPLACEEXISTING,
|
||||||
|
&hf);
|
||||||
|
|
||||||
|
// Write the empty signature file to the archive
|
||||||
|
if(nError == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
// Write the empty zeroed fiel to the MPQ
|
||||||
|
memset(EmptySignature, 0, sizeof(EmptySignature));
|
||||||
|
nError = SFileAddFile_Write(hf, EmptySignature, (DWORD)sizeof(EmptySignature), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the save process succeeded, we clear the MPQ_FLAG_ATTRIBUTE_INVALID flag
|
||||||
|
if(nError == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
ha->dwFlags &= ~MPQ_FLAG_SIGNATURE_INVALID;
|
||||||
|
ha->dwReservedFiles--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the file
|
||||||
|
if(hf != NULL)
|
||||||
|
SFileAddFile_Finish(hf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nError;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SSignFileFinish(TMPQArchive * ha)
|
||||||
|
{
|
||||||
|
MPQ_SIGNATURE_INFO si;
|
||||||
|
unsigned long signature_len = MPQ_WEAK_SIGNATURE_SIZE;
|
||||||
|
BYTE WeakSignature[MPQ_SIGNATURE_FILE_SIZE];
|
||||||
|
BYTE Md5Digest[MD5_DIGEST_SIZE];
|
||||||
|
rsa_key key;
|
||||||
|
int hash_idx = find_hash("md5");
|
||||||
|
|
||||||
|
// Sanity checks
|
||||||
|
assert((ha->dwFlags & MPQ_FLAG_CHANGED) == 0);
|
||||||
|
assert(ha->dwFileFlags3 == MPQ_FILE_EXISTS);
|
||||||
|
|
||||||
|
// Query the weak signature info
|
||||||
|
memset(&si, 0, sizeof(MPQ_SIGNATURE_INFO));
|
||||||
|
if(!QueryMpqSignatureInfo(ha, &si))
|
||||||
|
return ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
|
// There must be exactly one signature
|
||||||
|
if(si.SignatureTypes != SIGNATURE_TYPE_WEAK)
|
||||||
|
return ERROR_FILE_CORRUPT;
|
||||||
|
|
||||||
|
// Calculate MD5 of the entire archive
|
||||||
|
if(!CalculateMpqHashMd5(ha, &si, Md5Digest))
|
||||||
|
return ERROR_VERIFY_FAILED;
|
||||||
|
|
||||||
|
// Decode the private key
|
||||||
|
if(!decode_base64_key(szBlizzardWeakPrivateKey, &key))
|
||||||
|
return ERROR_VERIFY_FAILED;
|
||||||
|
|
||||||
|
// Sign the hash
|
||||||
|
memset(WeakSignature, 0, sizeof(WeakSignature));
|
||||||
|
rsa_sign_hash_ex(Md5Digest, sizeof(Md5Digest), WeakSignature + 8, &signature_len, LTC_LTC_PKCS_1_V1_5, 0, 0, hash_idx, 0, &key);
|
||||||
|
memrev(WeakSignature + 8, MPQ_WEAK_SIGNATURE_SIZE);
|
||||||
|
rsa_free(&key);
|
||||||
|
|
||||||
|
// Write the signature to the MPQ. Don't use SFile* functions, but write the hash directly
|
||||||
|
if(!FileStream_Write(ha->pStream, &si.BeginExclude, WeakSignature, MPQ_SIGNATURE_FILE_SIZE))
|
||||||
|
return GetLastError();
|
||||||
|
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Public (exported) functions
|
// Public (exported) functions
|
||||||
|
|
||||||
|
|
@ -824,7 +909,7 @@ int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * sz
|
||||||
TMPQHeader * pHeader;
|
TMPQHeader * pHeader;
|
||||||
|
|
||||||
// Verify input parameters
|
// Verify input parameters
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
pHeader = ha->pHeader;
|
pHeader = ha->pHeader;
|
||||||
|
|
||||||
|
|
@ -835,54 +920,54 @@ int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * sz
|
||||||
// If we have to verify MPQ header, do it
|
// If we have to verify MPQ header, do it
|
||||||
switch(dwWhatToVerify)
|
switch(dwWhatToVerify)
|
||||||
{
|
{
|
||||||
case SFILE_VERIFY_MPQ_HEADER:
|
case SFILE_VERIFY_MPQ_HEADER:
|
||||||
|
|
||||||
|
// Only if the header is of version 4 or newer
|
||||||
|
if(pHeader->dwHeaderSize >= (MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE))
|
||||||
|
return VerifyRawMpqData(ha, 0, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE);
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
|
||||||
// Only if the header is of version 4 or newer
|
case SFILE_VERIFY_HET_TABLE:
|
||||||
if(pHeader->dwHeaderSize >= (MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE))
|
|
||||||
return VerifyRawMpqData(ha, 0, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE);
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
|
|
||||||
case SFILE_VERIFY_HET_TABLE:
|
// Only if we have HET table
|
||||||
|
if(pHeader->HetTablePos64 && pHeader->HetTableSize64)
|
||||||
|
return VerifyRawMpqData(ha, pHeader->HetTablePos64, (DWORD)pHeader->HetTableSize64);
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
|
||||||
// Only if we have HET table
|
case SFILE_VERIFY_BET_TABLE:
|
||||||
if(pHeader->HetTablePos64 && pHeader->HetTableSize64)
|
|
||||||
return VerifyRawMpqData(ha, pHeader->HetTablePos64, (DWORD)pHeader->HetTableSize64);
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
|
|
||||||
case SFILE_VERIFY_BET_TABLE:
|
// Only if we have BET table
|
||||||
|
if(pHeader->BetTablePos64 && pHeader->BetTableSize64)
|
||||||
|
return VerifyRawMpqData(ha, pHeader->BetTablePos64, (DWORD)pHeader->BetTableSize64);
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
|
||||||
// Only if we have BET table
|
case SFILE_VERIFY_HASH_TABLE:
|
||||||
if(pHeader->BetTablePos64 && pHeader->BetTableSize64)
|
|
||||||
return VerifyRawMpqData(ha, pHeader->BetTablePos64, (DWORD)pHeader->BetTableSize64);
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
|
|
||||||
case SFILE_VERIFY_HASH_TABLE:
|
// Hash table is not protected by MD5
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
|
||||||
// Hash table is not protected by MD5
|
case SFILE_VERIFY_BLOCK_TABLE:
|
||||||
return ERROR_SUCCESS;
|
|
||||||
|
|
||||||
case SFILE_VERIFY_BLOCK_TABLE:
|
// Block table is not protected by MD5
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
|
||||||
// Block table is not protected by MD5
|
case SFILE_VERIFY_HIBLOCK_TABLE:
|
||||||
return ERROR_SUCCESS;
|
|
||||||
|
|
||||||
case SFILE_VERIFY_HIBLOCK_TABLE:
|
// It is unknown if the hi-block table is protected my MD5 or not.
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
|
||||||
// It is unknown if the hi-block table is protected my MD5 or not.
|
case SFILE_VERIFY_FILE:
|
||||||
return ERROR_SUCCESS;
|
|
||||||
|
|
||||||
case SFILE_VERIFY_FILE:
|
// Verify parameters
|
||||||
|
if(szFileName == NULL || *szFileName == 0)
|
||||||
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
// Verify parameters
|
// Get the offset of a file
|
||||||
if(szFileName == NULL || *szFileName == 0)
|
pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale);
|
||||||
return ERROR_INVALID_PARAMETER;
|
if(pFileEntry == NULL)
|
||||||
|
return ERROR_FILE_NOT_FOUND;
|
||||||
|
|
||||||
// Get the offset of a file
|
return VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize);
|
||||||
pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale);
|
|
||||||
if(pFileEntry == NULL)
|
|
||||||
return ERROR_FILE_NOT_FOUND;
|
|
||||||
|
|
||||||
return VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
|
@ -896,26 +981,73 @@ DWORD WINAPI SFileVerifyArchive(HANDLE hMpq)
|
||||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||||
|
|
||||||
// Verify input parameters
|
// Verify input parameters
|
||||||
if(!IsValidMpqHandle(ha))
|
if(!IsValidMpqHandle(hMpq))
|
||||||
return ERROR_VERIFY_FAILED;
|
return ERROR_VERIFY_FAILED;
|
||||||
|
|
||||||
|
// If the archive was modified, we need to flush it
|
||||||
|
if(ha->dwFlags & MPQ_FLAG_CHANGED)
|
||||||
|
SFileFlushArchive(hMpq);
|
||||||
|
|
||||||
// Get the MPQ signature and signature type
|
// Get the MPQ signature and signature type
|
||||||
memset(&si, 0, sizeof(MPQ_SIGNATURE_INFO));
|
memset(&si, 0, sizeof(MPQ_SIGNATURE_INFO));
|
||||||
if(!QueryMpqSignatureInfo(ha, &si))
|
if(!QueryMpqSignatureInfo(ha, &si))
|
||||||
return ERROR_VERIFY_FAILED;
|
return ERROR_VERIFY_FAILED;
|
||||||
|
|
||||||
// Verify the signature
|
// If there is no signature
|
||||||
switch(si.nSignatureType)
|
if(si.SignatureTypes == 0)
|
||||||
{
|
|
||||||
case SIGNATURE_TYPE_NONE:
|
|
||||||
return ERROR_NO_SIGNATURE;
|
return ERROR_NO_SIGNATURE;
|
||||||
|
|
||||||
case SIGNATURE_TYPE_WEAK:
|
// We haven't seen a MPQ with both signatures
|
||||||
|
assert(si.SignatureTypes == SIGNATURE_TYPE_WEAK || si.SignatureTypes == SIGNATURE_TYPE_STRONG);
|
||||||
|
|
||||||
|
// Verify the strong signature, if present
|
||||||
|
if(si.SignatureTypes & SIGNATURE_TYPE_STRONG)
|
||||||
|
return VerifyStrongSignature(ha, &si);
|
||||||
|
|
||||||
|
// Verify the weak signature, if present
|
||||||
|
if(si.SignatureTypes & SIGNATURE_TYPE_WEAK)
|
||||||
return VerifyWeakSignature(ha, &si);
|
return VerifyWeakSignature(ha, &si);
|
||||||
|
|
||||||
case SIGNATURE_TYPE_STRONG:
|
return ERROR_NO_SIGNATURE;
|
||||||
return VerifyStrongSignature(ha, &si);
|
}
|
||||||
|
|
||||||
|
// Verifies the archive against the signature
|
||||||
|
bool WINAPI SFileSignArchive(HANDLE hMpq, DWORD dwSignatureType)
|
||||||
|
{
|
||||||
|
TMPQArchive * ha;
|
||||||
|
|
||||||
|
// Verify the archive handle
|
||||||
|
ha = IsValidMpqHandle(hMpq);
|
||||||
|
if(ha == NULL)
|
||||||
|
{
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ERROR_VERIFY_FAILED;
|
// We only support weak signature, and only for MPQs version 1.0
|
||||||
|
if(dwSignatureType != SIGNATURE_TYPE_WEAK)
|
||||||
|
{
|
||||||
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The archive must not be malformed and must not be read-only
|
||||||
|
if(ha->dwFlags & (MPQ_FLAG_READ_ONLY | MPQ_FLAG_MALFORMED))
|
||||||
|
{
|
||||||
|
SetLastError(ERROR_ACCESS_DENIED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the signature is not there yet
|
||||||
|
if(ha->dwFileFlags3 == 0)
|
||||||
|
{
|
||||||
|
// Turn the signature on. The signature will
|
||||||
|
// be applied when the archive is closed
|
||||||
|
ha->dwFlags |= MPQ_FLAG_SIGNATURE_INVALID | MPQ_FLAG_CHANGED;
|
||||||
|
ha->dwFileFlags3 = MPQ_FILE_EXISTS;
|
||||||
|
ha->dwReservedFiles++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,16 +35,16 @@
|
||||||
|
|
||||||
// Include functions from zlib
|
// Include functions from zlib
|
||||||
#ifndef __SYS_ZLIB
|
#ifndef __SYS_ZLIB
|
||||||
#include "zlib/zlib.h"
|
#include "zlib/zlib.h"
|
||||||
#else
|
#else
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Include functions from bzlib
|
// Include functions from bzlib
|
||||||
#ifndef __SYS_BZLIB
|
#ifndef __SYS_BZLIB
|
||||||
#include "bzip2/bzlib.h"
|
#include "bzip2/bzlib.h"
|
||||||
#else
|
#else
|
||||||
#include <bzlib.h>
|
#include <bzlib.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
@ -61,56 +61,73 @@
|
||||||
|
|
||||||
#define ID_MPQ_FILE 0x46494c45 // Used internally for checking TMPQFile ('FILE')
|
#define ID_MPQ_FILE 0x46494c45 // Used internally for checking TMPQFile ('FILE')
|
||||||
|
|
||||||
#define MPQ_WEAK_SIGNATURE_SIZE 64
|
|
||||||
#define MPQ_STRONG_SIGNATURE_SIZE 256
|
|
||||||
|
|
||||||
// Prevent problems with CRT "min" and "max" functions,
|
// Prevent problems with CRT "min" and "max" functions,
|
||||||
// as they are not defined on all platforms
|
// as they are not defined on all platforms
|
||||||
#define STORMLIB_MIN(a, b) ((a < b) ? a : b)
|
#define STORMLIB_MIN(a, b) ((a < b) ? a : b)
|
||||||
#define STORMLIB_MAX(a, b) ((a > b) ? a : b)
|
#define STORMLIB_MAX(a, b) ((a > b) ? a : b)
|
||||||
|
#define STORMLIB_UNUSED(p) ((void)(p))
|
||||||
|
|
||||||
// Macro for building 64-bit file offset from two 32-bit
|
// Macro for building 64-bit file offset from two 32-bit
|
||||||
#define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | lo)
|
#define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | (ULONGLONG)lo)
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// MPQ signature information
|
||||||
|
|
||||||
|
// Size of each signature type
|
||||||
|
#define MPQ_WEAK_SIGNATURE_SIZE 64
|
||||||
|
#define MPQ_STRONG_SIGNATURE_SIZE 256
|
||||||
|
#define MPQ_STRONG_SIGNATURE_ID 0x5349474E // ID of the strong signature ("NGIS")
|
||||||
|
#define MPQ_SIGNATURE_FILE_SIZE (MPQ_WEAK_SIGNATURE_SIZE + 8)
|
||||||
|
|
||||||
|
// MPQ signature info
|
||||||
|
typedef struct _MPQ_SIGNATURE_INFO
|
||||||
|
{
|
||||||
|
ULONGLONG BeginMpqData; // File offset where the hashing starts
|
||||||
|
ULONGLONG BeginExclude; // Begin of the excluded area (used for (signature) file)
|
||||||
|
ULONGLONG EndExclude; // End of the excluded area (used for (signature) file)
|
||||||
|
ULONGLONG EndMpqData; // File offset where the hashing ends
|
||||||
|
ULONGLONG EndOfFile; // Size of the entire file
|
||||||
|
BYTE Signature[MPQ_STRONG_SIGNATURE_SIZE + 0x10];
|
||||||
|
DWORD cbSignatureSize; // Length of the signature
|
||||||
|
DWORD SignatureTypes; // See SIGNATURE_TYPE_XXX
|
||||||
|
|
||||||
|
} MPQ_SIGNATURE_INFO, *PMPQ_SIGNATURE_INFO;
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Memory management
|
// Memory management
|
||||||
//
|
//
|
||||||
// We use our own macros for allocating/freeing memory. If you want
|
// We use our own macros for allocating/freeing memory. If you want
|
||||||
// to redefine them, please keep the following rules
|
// to redefine them, please keep the following rules:
|
||||||
//
|
//
|
||||||
// - The memory allocation must return NULL if not enough memory
|
// - The memory allocation must return NULL if not enough memory
|
||||||
// (i.e not to throw exception)
|
// (i.e not to throw exception)
|
||||||
// - It is not necessary to fill the allocated buffer with zeros
|
// - The allocating function does not need to fill the allocated buffer with zeros
|
||||||
// - Memory freeing function doesn't have to test the pointer to NULL.
|
// - Memory freeing function doesn't have to test the pointer to NULL
|
||||||
//
|
//
|
||||||
|
|
||||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
//#if defined(_MSC_VER) && defined(_DEBUG)
|
||||||
__inline void * DebugMalloc(char * /* szFile */, int /* nLine */, size_t nSize)
|
//
|
||||||
{
|
//#define STORM_ALLOC(type, nitems) (type *)HeapAlloc(GetProcessHeap(), 0, ((nitems) * sizeof(type)))
|
||||||
// return new BYTE[nSize];
|
//#define STORM_FREE(ptr) HeapFree(GetProcessHeap(), 0, ptr)
|
||||||
return HeapAlloc(GetProcessHeap(), 0, nSize);
|
//
|
||||||
}
|
//#else
|
||||||
|
|
||||||
__inline void DebugFree(void * ptr)
|
#define STORM_ALLOC(type, nitems) (type *)malloc((nitems) * sizeof(type))
|
||||||
{
|
#define STORM_FREE(ptr) free(ptr)
|
||||||
// delete [] ptr;
|
|
||||||
HeapFree(GetProcessHeap(), 0, ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define STORM_ALLOC(type, nitems) (type *)DebugMalloc(__FILE__, __LINE__, (nitems) * sizeof(type))
|
//#endif
|
||||||
#define STORM_FREE(ptr) DebugFree(ptr)
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define STORM_ALLOC(type, nitems) (type *)malloc((nitems) * sizeof(type))
|
|
||||||
#define STORM_FREE(ptr) free(ptr)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// StormLib internal global variables
|
// StormLib internal global variables
|
||||||
|
|
||||||
extern LCID lcFileLocale; // Preferred file locale
|
extern LCID lcFileLocale; // Preferred file locale
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Conversion to uppercase/lowercase (and "/" to "\")
|
||||||
|
|
||||||
|
extern unsigned char AsciiToLowerTable[256];
|
||||||
|
extern unsigned char AsciiToUpperTable[256];
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Encryption and decryption functions
|
// Encryption and decryption functions
|
||||||
|
|
||||||
|
|
@ -118,8 +135,11 @@ extern LCID lcFileLocale; // Preferred file locale
|
||||||
#define MPQ_HASH_NAME_A 0x100
|
#define MPQ_HASH_NAME_A 0x100
|
||||||
#define MPQ_HASH_NAME_B 0x200
|
#define MPQ_HASH_NAME_B 0x200
|
||||||
#define MPQ_HASH_FILE_KEY 0x300
|
#define MPQ_HASH_FILE_KEY 0x300
|
||||||
|
#define MPQ_HASH_KEY2_MIX 0x400
|
||||||
|
|
||||||
DWORD HashString(const char * szFileName, DWORD dwHashType);
|
DWORD HashString(const char * szFileName, DWORD dwHashType);
|
||||||
|
DWORD HashStringSlash(const char * szFileName, DWORD dwHashType);
|
||||||
|
DWORD HashStringLower(const char * szFileName, DWORD dwHashType);
|
||||||
|
|
||||||
void InitializeMpqCryptography();
|
void InitializeMpqCryptography();
|
||||||
|
|
||||||
|
|
@ -128,15 +148,13 @@ DWORD GetHashTableSizeForFileCount(DWORD dwFileCount);
|
||||||
bool IsPseudoFileName(const char * szFileName, LPDWORD pdwFileIndex);
|
bool IsPseudoFileName(const char * szFileName, LPDWORD pdwFileIndex);
|
||||||
ULONGLONG HashStringJenkins(const char * szFileName);
|
ULONGLONG HashStringJenkins(const char * szFileName);
|
||||||
|
|
||||||
int ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags);
|
DWORD GetDefaultSpecialFileFlags(DWORD dwFileSize, USHORT wFormatVersion);
|
||||||
|
|
||||||
DWORD GetDefaultSpecialFileFlags(TMPQArchive * ha, DWORD dwFileSize);
|
void EncryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey);
|
||||||
|
void DecryptMpqBlock(void * pvDataBlock, DWORD dwLength, DWORD dwKey);
|
||||||
|
|
||||||
void EncryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwKey);
|
DWORD DetectFileKeyBySectorSize(LPDWORD EncryptedData, DWORD dwSectorSize, DWORD dwSectorOffsLen);
|
||||||
void DecryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwKey);
|
DWORD DetectFileKeyByContent(void * pvEncryptedData, DWORD dwSectorSize, DWORD dwFileSize);
|
||||||
|
|
||||||
DWORD DetectFileKeyBySectorSize(LPDWORD SectorOffsets, DWORD decrypted);
|
|
||||||
DWORD DetectFileKeyByContent(void * pvFileContent, DWORD dwFileSize);
|
|
||||||
DWORD DecryptFileKey(const char * szFileName, ULONGLONG MpqPos, DWORD dwFileSize, DWORD dwFlags);
|
DWORD DecryptFileKey(const char * szFileName, ULONGLONG MpqPos, DWORD dwFileSize, DWORD dwFlags);
|
||||||
|
|
||||||
bool IsValidMD5(LPBYTE pbMd5);
|
bool IsValidMD5(LPBYTE pbMd5);
|
||||||
|
|
@ -146,29 +164,38 @@ void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_ha
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Handle validation functions
|
// Handle validation functions
|
||||||
|
|
||||||
bool IsValidMpqHandle(TMPQArchive * ha);
|
TMPQArchive * IsValidMpqHandle(HANDLE hMpq);
|
||||||
bool IsValidFileHandle(TMPQFile * hf);
|
TMPQFile * IsValidFileHandle(HANDLE hFile);
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Hash table and block table manipulation
|
// Support for MPQ file tables
|
||||||
|
|
||||||
|
int ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG MpqOffset, ULONGLONG FileSize, DWORD dwFlags);
|
||||||
|
|
||||||
|
TMPQHash * FindFreeHashEntry(TMPQArchive * ha, DWORD dwStartIndex, DWORD dwName1, DWORD dwName2, LCID lcLocale);
|
||||||
TMPQHash * GetFirstHashEntry(TMPQArchive * ha, const char * szFileName);
|
TMPQHash * GetFirstHashEntry(TMPQArchive * ha, const char * szFileName);
|
||||||
TMPQHash * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * pPrevHash);
|
TMPQHash * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * pPrevHash);
|
||||||
DWORD AllocateHashEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
TMPQHash * AllocateHashEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
||||||
DWORD AllocateHetEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
|
||||||
|
|
||||||
void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pFreeSpacePos);
|
TMPQExtHeader * LoadExtTable(TMPQArchive * ha, ULONGLONG ByteOffset, size_t Size, DWORD dwSignature, DWORD dwKey);
|
||||||
|
TMPQHetTable * LoadHetTable(TMPQArchive * ha);
|
||||||
|
TMPQBetTable * LoadBetTable(TMPQArchive * ha);
|
||||||
|
|
||||||
// Functions that loads and verifies MPQ data bitmap
|
TMPQHash * LoadHashTable(TMPQArchive * ha);
|
||||||
int LoadMpqDataBitmap(TMPQArchive * ha, ULONGLONG FileSize, bool * pbFileIsComplete);
|
TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool bDontFixEntries = false);
|
||||||
|
TMPQBlock * TranslateBlockTable(TMPQArchive * ha, ULONGLONG * pcbTableSize, bool * pbNeedHiBlockTable);
|
||||||
|
|
||||||
|
ULONGLONG FindFreeMpqSpace(TMPQArchive * ha);
|
||||||
|
|
||||||
// Functions that load the HET and BET tables
|
// Functions that load the HET and BET tables
|
||||||
int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize);
|
int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize);
|
||||||
int LoadAnyHashTable(TMPQArchive * ha);
|
int LoadAnyHashTable(TMPQArchive * ha);
|
||||||
int BuildFileTable(TMPQArchive * ha, ULONGLONG FileSize);
|
int BuildFileTable(TMPQArchive * ha);
|
||||||
|
int RebuildHetTable(TMPQArchive * ha);
|
||||||
|
int RebuildFileTable(TMPQArchive * ha, DWORD dwNewHashTableSize, DWORD dwNewMaxFileCount);
|
||||||
int SaveMPQTables(TMPQArchive * ha);
|
int SaveMPQTables(TMPQArchive * ha);
|
||||||
|
|
||||||
TMPQHetTable * CreateHetTable(DWORD dwMaxFileCount, DWORD dwHashBitSize, bool bCreateEmpty);
|
TMPQHetTable * CreateHetTable(DWORD dwEntryCount, DWORD dwTotalCount, DWORD dwHashBitSize, LPBYTE pbSrcData);
|
||||||
void FreeHetTable(TMPQHetTable * pHetTable);
|
void FreeHetTable(TMPQHetTable * pHetTable);
|
||||||
|
|
||||||
TMPQBetTable * CreateBetTable(DWORD dwMaxFileCount);
|
TMPQBetTable * CreateBetTable(DWORD dwMaxFileCount);
|
||||||
|
|
@ -181,23 +208,37 @@ TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID l
|
||||||
TFileEntry * GetFileEntryByIndex(TMPQArchive * ha, DWORD dwIndex);
|
TFileEntry * GetFileEntryByIndex(TMPQArchive * ha, DWORD dwIndex);
|
||||||
|
|
||||||
// Allocates file name in the file entry
|
// Allocates file name in the file entry
|
||||||
void AllocateFileName(TFileEntry * pFileEntry, const char * szFileName);
|
void AllocateFileName(TMPQArchive * ha, TFileEntry * pFileEntry, const char * szFileName);
|
||||||
|
|
||||||
// Allocates new file entry in the MPQ tables. Reuses existing, if possible
|
// Allocates new file entry in the MPQ tables. Reuses existing, if possible
|
||||||
TFileEntry * FindFreeFileEntry(TMPQArchive * ha);
|
|
||||||
TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID lcLocale);
|
TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID lcLocale);
|
||||||
int RenameFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry, const char * szNewFileName);
|
int RenameFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry, const char * szNewFileName);
|
||||||
void ClearFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
void DeleteFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
||||||
int FreeFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
|
||||||
|
|
||||||
// Invalidates entries for (listfile) and (attributes)
|
// Invalidates entries for (listfile) and (attributes)
|
||||||
void InvalidateInternalFiles(TMPQArchive * ha);
|
void InvalidateInternalFiles(TMPQArchive * ha);
|
||||||
|
|
||||||
|
// Retrieves information about the strong signature
|
||||||
|
bool QueryMpqSignatureInfo(TMPQArchive * ha, PMPQ_SIGNATURE_INFO pSignatureInfo);
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Support for alternate file formats (SBaseSubTypes.cpp)
|
||||||
|
|
||||||
|
int ConvertSqpHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags);
|
||||||
|
TMPQHash * LoadSqpHashTable(TMPQArchive * ha);
|
||||||
|
TMPQBlock * LoadSqpBlockTable(TMPQArchive * ha);
|
||||||
|
|
||||||
|
int ConvertMpkHeaderToFormat4(TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags);
|
||||||
|
void DecryptMpkTable(void * pvMpkTable, size_t cbSize);
|
||||||
|
TMPQHash * LoadMpkHashTable(TMPQArchive * ha);
|
||||||
|
TMPQBlock * LoadMpkBlockTable(TMPQArchive * ha);
|
||||||
|
int SCompDecompressMpk(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer);
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Common functions - MPQ File
|
// Common functions - MPQ File
|
||||||
|
|
||||||
TMPQFile * CreateMpqFile(TMPQArchive * ha);
|
TMPQFile * CreateFileHandle(TMPQArchive * ha, TFileEntry * pFileEntry);
|
||||||
int LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, void * pvTable, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey);
|
void * LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey);
|
||||||
int AllocateSectorBuffer(TMPQFile * hf);
|
int AllocateSectorBuffer(TMPQFile * hf);
|
||||||
int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile);
|
int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile);
|
||||||
int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile);
|
int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile);
|
||||||
|
|
@ -208,44 +249,48 @@ int WriteSectorOffsets(TMPQFile * hf);
|
||||||
int WriteSectorChecksums(TMPQFile * hf);
|
int WriteSectorChecksums(TMPQFile * hf);
|
||||||
int WriteMemDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, void * pvRawData, DWORD dwRawDataSize, DWORD dwChunkSize, LPDWORD pcbTotalSize);
|
int WriteMemDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, void * pvRawData, DWORD dwRawDataSize, DWORD dwChunkSize, LPDWORD pcbTotalSize);
|
||||||
int WriteMpqDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, DWORD dwRawDataSize, DWORD dwChunkSize);
|
int WriteMpqDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, DWORD dwRawDataSize, DWORD dwChunkSize);
|
||||||
void FreeMPQFile(TMPQFile *& hf);
|
void FreeFileHandle(TMPQFile *& hf);
|
||||||
|
|
||||||
bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize);
|
bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize);
|
||||||
int PatchFileData(TMPQFile * hf);
|
int PatchFileData(TMPQFile * hf);
|
||||||
|
|
||||||
void FreeMPQArchive(TMPQArchive *& ha);
|
void FreeArchiveHandle(TMPQArchive *& ha);
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Utility functions
|
// Utility functions
|
||||||
|
|
||||||
bool CheckWildCard(const char * szString, const char * szWildCard);
|
bool CheckWildCard(const char * szString, const char * szWildCard);
|
||||||
const char * GetPlainFileNameA(const char * szFileName);
|
|
||||||
const TCHAR * GetPlainFileNameT(const TCHAR * szFileName);
|
|
||||||
bool IsInternalMpqFileName(const char * szFileName);
|
bool IsInternalMpqFileName(const char * szFileName);
|
||||||
|
|
||||||
|
const TCHAR * GetPlainFileName(const TCHAR * szFileName);
|
||||||
|
const char * GetPlainFileName(const char * szFileName);
|
||||||
|
|
||||||
|
void CopyFileName(TCHAR * szTarget, const char * szSource, size_t cchLength);
|
||||||
|
void CopyFileName(char * szTarget, const TCHAR * szSource, size_t cchLength);
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Support for adding files to the MPQ
|
// Support for adding files to the MPQ
|
||||||
|
|
||||||
int SFileAddFile_Init(
|
int SFileAddFile_Init(
|
||||||
TMPQArchive * ha,
|
TMPQArchive * ha,
|
||||||
const char * szArchivedName,
|
const char * szArchivedName,
|
||||||
ULONGLONG ft,
|
ULONGLONG ft,
|
||||||
DWORD dwFileSize,
|
DWORD dwFileSize,
|
||||||
LCID lcLocale,
|
LCID lcLocale,
|
||||||
DWORD dwFlags,
|
DWORD dwFlags,
|
||||||
TMPQFile ** phf
|
TMPQFile ** phf
|
||||||
);
|
);
|
||||||
|
|
||||||
int SFileAddFile_Write(
|
int SFileAddFile_Write(
|
||||||
TMPQFile * hf,
|
TMPQFile * hf,
|
||||||
const void * pvData,
|
const void * pvData,
|
||||||
DWORD dwSize,
|
DWORD dwSize,
|
||||||
DWORD dwCompression
|
DWORD dwCompression
|
||||||
);
|
);
|
||||||
|
|
||||||
int SFileAddFile_Finish(
|
int SFileAddFile_Finish(
|
||||||
TMPQFile * hf
|
TMPQFile * hf
|
||||||
);
|
);
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Attributes support
|
// Attributes support
|
||||||
|
|
@ -258,6 +303,12 @@ int SAttrFileSaveToMpq(TMPQArchive * ha);
|
||||||
|
|
||||||
int SListFileSaveToMpq(TMPQArchive * ha);
|
int SListFileSaveToMpq(TMPQArchive * ha);
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Weak signature support
|
||||||
|
|
||||||
|
int SSignFileCreate(TMPQArchive * ha);
|
||||||
|
int SSignFileFinish(TMPQArchive * ha);
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Dump data support
|
// Dump data support
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -21,221 +21,270 @@
|
||||||
/* 24.07.04 1.03 Sam Mac OS X compatibility */
|
/* 24.07.04 1.03 Sam Mac OS X compatibility */
|
||||||
/* 22.11.06 1.04 Sam Mac OS X compatibility (for StormLib 6.0) */
|
/* 22.11.06 1.04 Sam Mac OS X compatibility (for StormLib 6.0) */
|
||||||
/* 31.12.06 1.05 XPinguin Full GNU/Linux compatibility */
|
/* 31.12.06 1.05 XPinguin Full GNU/Linux compatibility */
|
||||||
|
/* 17.10.12 1.05 Lad Moved error codes so they don't overlap with errno.h */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
#ifndef __STORMPORT_H__
|
#ifndef __STORMPORT_H__
|
||||||
#define __STORMPORT_H__
|
#define __STORMPORT_H__
|
||||||
|
|
||||||
#ifndef __cplusplus
|
#ifndef __cplusplus
|
||||||
#define bool char
|
#define bool char
|
||||||
#define true 1
|
#define true 1
|
||||||
#define false 0
|
#define false 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
// Defines for Windows
|
// Defines for Windows
|
||||||
|
|
||||||
#if !defined(PLATFORM_DEFINED) && (defined(WIN32) || defined(WIN64))
|
#if !defined(PLATFORM_DEFINED) && (defined(WIN32) || defined(WIN64))
|
||||||
|
|
||||||
// In MSVC 8.0, there are some functions declared as deprecated.
|
// In MSVC 8.0, there are some functions declared as deprecated.
|
||||||
#if _MSC_VER >= 1400
|
#if _MSC_VER >= 1400
|
||||||
#define _CRT_SECURE_NO_DEPRECATE
|
#define _CRT_SECURE_NO_DEPRECATE
|
||||||
#define _CRT_NON_CONFORMING_SWPRINTFS
|
#define _CRT_NON_CONFORMING_SWPRINTFS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <wininet.h>
|
#include <wininet.h>
|
||||||
#define PLATFORM_LITTLE_ENDIAN
|
#define PLATFORM_LITTLE_ENDIAN
|
||||||
|
|
||||||
#ifdef WIN64
|
#ifdef WIN64
|
||||||
#define PLATFORM_64BIT
|
#define PLATFORM_64BIT
|
||||||
#else
|
#else
|
||||||
#define PLATFORM_32BIT
|
#define PLATFORM_32BIT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PLATFORM_WINDOWS
|
#define PLATFORM_WINDOWS
|
||||||
#define PLATFORM_DEFINED // The platform is known now
|
#define PLATFORM_DEFINED // The platform is known now
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
// Defines for Mac
|
// Defines for Mac
|
||||||
|
|
||||||
#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API
|
#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API
|
||||||
|
|
||||||
// Macintosh
|
// Macintosh
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#define PKEXPORT
|
// Support for PowerPC on Max OS X
|
||||||
#define __SYS_ZLIB
|
#if (__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1)
|
||||||
#define __SYS_BZLIB
|
#include <stdint.h>
|
||||||
|
#include <CoreFoundation/CFByteOrder.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef __BIG_ENDIAN__
|
#define PKEXPORT
|
||||||
#define PLATFORM_LITTLE_ENDIAN
|
#define __SYS_ZLIB
|
||||||
#endif
|
#define __SYS_BZLIB
|
||||||
|
|
||||||
#define PLATFORM_MAC
|
#ifndef __BIG_ENDIAN__
|
||||||
#define PLATFORM_DEFINED // The platform is known now
|
#define PLATFORM_LITTLE_ENDIAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PLATFORM_MAC
|
||||||
|
#define PLATFORM_DEFINED // The platform is known now
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
// Assumption: we are not on Windows nor Macintosh, so this must be linux *grin*
|
// Assumption: we are not on Windows nor Macintosh, so this must be linux *grin*
|
||||||
|
|
||||||
#if !defined(PLATFORM_DEFINED)
|
#if !defined(PLATFORM_DEFINED)
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#define PLATFORM_LITTLE_ENDIAN
|
#define PLATFORM_LITTLE_ENDIAN
|
||||||
#define PLATFORM_LINUX
|
#define PLATFORM_LINUX
|
||||||
#define PLATFORM_DEFINED
|
#define PLATFORM_DEFINED
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Definition of Windows-specific structures for non-Windows platforms
|
//-----------------------------------------------------------------------------
|
||||||
|
// Definition of Windows-specific types for non-Windows platforms
|
||||||
|
|
||||||
#ifndef PLATFORM_WINDOWS
|
#ifndef PLATFORM_WINDOWS
|
||||||
#if __LP64__
|
#if __LP64__
|
||||||
#define PLATFORM_64BIT
|
#define PLATFORM_64BIT
|
||||||
#else
|
#else
|
||||||
#define PLATFORM_32BIT
|
#define PLATFORM_32BIT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Typedefs for ANSI C
|
// Typedefs for ANSI C
|
||||||
typedef unsigned char BYTE;
|
typedef unsigned char BYTE;
|
||||||
typedef unsigned short USHORT;
|
typedef unsigned short USHORT;
|
||||||
typedef int LONG;
|
typedef int LONG;
|
||||||
typedef unsigned int DWORD;
|
typedef unsigned int DWORD;
|
||||||
typedef unsigned long DWORD_PTR;
|
typedef unsigned long DWORD_PTR;
|
||||||
typedef long LONG_PTR;
|
typedef long LONG_PTR;
|
||||||
typedef long INT_PTR;
|
typedef long INT_PTR;
|
||||||
typedef long long LONGLONG;
|
typedef long long LONGLONG;
|
||||||
typedef unsigned long long ULONGLONG;
|
typedef unsigned long long ULONGLONG;
|
||||||
typedef void * HANDLE;
|
typedef void * HANDLE;
|
||||||
typedef void * LPOVERLAPPED; // Unsupported on Linux and Mac
|
typedef void * LPOVERLAPPED; // Unsupported on Linux and Mac
|
||||||
typedef char TCHAR;
|
typedef char TCHAR;
|
||||||
typedef unsigned int LCID;
|
typedef unsigned int LCID;
|
||||||
typedef LONG * PLONG;
|
typedef LONG * PLONG;
|
||||||
typedef DWORD * LPDWORD;
|
typedef DWORD * LPDWORD;
|
||||||
typedef BYTE * LPBYTE;
|
typedef BYTE * LPBYTE;
|
||||||
|
|
||||||
#ifdef PLATFORM_32BIT
|
#ifdef PLATFORM_32BIT
|
||||||
#define _LZMA_UINT32_IS_ULONG
|
#define _LZMA_UINT32_IS_ULONG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Some Windows-specific defines
|
// Some Windows-specific defines
|
||||||
#ifndef MAX_PATH
|
#ifndef MAX_PATH
|
||||||
#define MAX_PATH 1024
|
#define MAX_PATH 1024
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define WINAPI
|
#define WINAPI
|
||||||
|
|
||||||
#define FILE_BEGIN SEEK_SET
|
#define FILE_BEGIN SEEK_SET
|
||||||
#define FILE_CURRENT SEEK_CUR
|
#define FILE_CURRENT SEEK_CUR
|
||||||
#define FILE_END SEEK_END
|
#define FILE_END SEEK_END
|
||||||
|
|
||||||
#define _T(x) x
|
#define _T(x) x
|
||||||
#define _tcslen strlen
|
#define _tcslen strlen
|
||||||
#define _tcscpy strcpy
|
#define _tcscpy strcpy
|
||||||
#define _tcscat strcat
|
#define _tcscat strcat
|
||||||
#define _tcsrchr strrchr
|
#define _tcschr strchr
|
||||||
#define _tprintf printf
|
#define _tcsrchr strrchr
|
||||||
#define _stprintf sprintf
|
#define _tcsstr strstr
|
||||||
#define _tremove remove
|
#define _tprintf printf
|
||||||
|
#define _stprintf sprintf
|
||||||
|
#define _tremove remove
|
||||||
|
|
||||||
#define _stricmp strcasecmp
|
#define _stricmp strcasecmp
|
||||||
#define _strnicmp strncasecmp
|
#define _strnicmp strncasecmp
|
||||||
#define _tcsnicmp strncasecmp
|
#define _tcsicmp strcasecmp
|
||||||
|
#define _tcsnicmp strncasecmp
|
||||||
|
|
||||||
#endif // !WIN32
|
#endif // !PLATFORM_WINDOWS
|
||||||
|
|
||||||
// 64-bit calls are supplied by "normal" calls on Mac
|
// 64-bit calls are supplied by "normal" calls on Mac
|
||||||
#if defined(PLATFORM_MAC)
|
#if defined(PLATFORM_MAC)
|
||||||
#define stat64 stat
|
#define stat64 stat
|
||||||
#define fstat64 fstat
|
#define fstat64 fstat
|
||||||
#define lseek64 lseek
|
#define lseek64 lseek
|
||||||
#define off64_t off_t
|
#define ftruncate64 ftruncate
|
||||||
#define O_LARGEFILE 0
|
#define off64_t off_t
|
||||||
|
#define O_LARGEFILE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Platform-specific error codes for UNIX-based platforms
|
// Platform-specific error codes for UNIX-based platforms
|
||||||
#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
|
#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
|
||||||
#define ERROR_SUCCESS 0
|
#define ERROR_SUCCESS 0
|
||||||
#define ERROR_FILE_NOT_FOUND ENOENT
|
#define ERROR_FILE_NOT_FOUND ENOENT
|
||||||
#define ERROR_ACCESS_DENIED EPERM
|
#define ERROR_ACCESS_DENIED EPERM
|
||||||
#define ERROR_INVALID_HANDLE EBADF
|
#define ERROR_INVALID_HANDLE EBADF
|
||||||
#define ERROR_NOT_ENOUGH_MEMORY ENOMEM
|
#define ERROR_NOT_ENOUGH_MEMORY ENOMEM
|
||||||
#define ERROR_BAD_FORMAT 105 // No such error code under Linux
|
#define ERROR_NOT_SUPPORTED ENOTSUP
|
||||||
#define ERROR_NO_MORE_FILES 106
|
#define ERROR_INVALID_PARAMETER EINVAL
|
||||||
#define ERROR_HANDLE_EOF 107 // No such error code under Linux
|
#define ERROR_DISK_FULL ENOSPC
|
||||||
#define ERROR_NOT_SUPPORTED ENOTSUP
|
#define ERROR_ALREADY_EXISTS EEXIST
|
||||||
#define ERROR_INVALID_PARAMETER EINVAL
|
#define ERROR_INSUFFICIENT_BUFFER ENOBUFS
|
||||||
#define ERROR_DISK_FULL ENOSPC
|
#define ERROR_BAD_FORMAT 1000 // No such error code under Linux
|
||||||
#define ERROR_ALREADY_EXISTS EEXIST
|
#define ERROR_NO_MORE_FILES 1001 // No such error code under Linux
|
||||||
#define ERROR_CAN_NOT_COMPLETE 108 // No such error code under Linux
|
#define ERROR_HANDLE_EOF 1002 // No such error code under Linux
|
||||||
#define ERROR_FILE_CORRUPT 109 // No such error code under Linux
|
#define ERROR_CAN_NOT_COMPLETE 1003 // No such error code under Linux
|
||||||
#define ERROR_INSUFFICIENT_BUFFER ENOBUFS
|
#define ERROR_FILE_CORRUPT 1004 // No such error code under Linux
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Swapping functions
|
||||||
|
|
||||||
#ifdef PLATFORM_LITTLE_ENDIAN
|
#ifdef PLATFORM_LITTLE_ENDIAN
|
||||||
#define BSWAP_INT16_UNSIGNED(a) (a)
|
#define BSWAP_INT16_UNSIGNED(a) (a)
|
||||||
#define BSWAP_INT16_SIGNED(a) (a)
|
#define BSWAP_INT16_SIGNED(a) (a)
|
||||||
#define BSWAP_INT32_UNSIGNED(a) (a)
|
#define BSWAP_INT32_UNSIGNED(a) (a)
|
||||||
#define BSWAP_INT32_SIGNED(a) (a)
|
#define BSWAP_INT32_SIGNED(a) (a)
|
||||||
#define BSWAP_INT64_SIGNED(a) (a)
|
#define BSWAP_INT64_SIGNED(a) (a)
|
||||||
#define BSWAP_INT64_UNSIGNED(a) (a)
|
#define BSWAP_INT64_UNSIGNED(a) (a)
|
||||||
#define BSWAP_ARRAY16_UNSIGNED(a,b) {}
|
#define BSWAP_ARRAY16_UNSIGNED(a,b) {}
|
||||||
#define BSWAP_ARRAY32_UNSIGNED(a,b) {}
|
#define BSWAP_ARRAY32_UNSIGNED(a,b) {}
|
||||||
#define BSWAP_ARRAY64_UNSIGNED(a,b) {}
|
#define BSWAP_ARRAY64_UNSIGNED(a,b) {}
|
||||||
#define BSWAP_PART_HEADER(a) {}
|
#define BSWAP_PART_HEADER(a) {}
|
||||||
#define BSWAP_TMPQUSERDATA(a) {}
|
#define BSWAP_TMPQHEADER(a,b) {}
|
||||||
#define BSWAP_TMPQHEADER(a) {}
|
#define BSWAP_TMPKHEADER(a) {}
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
int16_t SwapInt16(uint16_t);
|
int16_t SwapInt16(uint16_t);
|
||||||
uint16_t SwapUInt16(uint16_t);
|
uint16_t SwapUInt16(uint16_t);
|
||||||
int32_t SwapInt32(uint32_t);
|
int32_t SwapInt32(uint32_t);
|
||||||
uint32_t SwapUInt32(uint32_t);
|
uint32_t SwapUInt32(uint32_t);
|
||||||
int64_t SwapInt64(uint64_t);
|
int64_t SwapInt64(uint64_t);
|
||||||
uint64_t SwapUInt64(uint64_t);
|
uint64_t SwapUInt64(uint64_t);
|
||||||
void ConvertUInt16Buffer(void * ptr, size_t length);
|
void ConvertUInt16Buffer(void * ptr, size_t length);
|
||||||
void ConvertUInt32Buffer(void * ptr, size_t length);
|
void ConvertUInt32Buffer(void * ptr, size_t length);
|
||||||
void ConvertUInt64Buffer(void * ptr, size_t length);
|
void ConvertUInt64Buffer(void * ptr, size_t length);
|
||||||
void ConvertPartHeader(void * partHeader);
|
void ConvertTMPQUserData(void *userData);
|
||||||
void ConvertTMPQUserData(void *userData);
|
void ConvertTMPQHeader(void *header, uint16_t wPart);
|
||||||
void ConvertTMPQHeader(void *header);
|
void ConvertTMPKHeader(void *header);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#define BSWAP_INT16_SIGNED(a) SwapInt16((a))
|
#define BSWAP_INT16_SIGNED(a) SwapInt16((a))
|
||||||
#define BSWAP_INT16_UNSIGNED(a) SwapUInt16((a))
|
#define BSWAP_INT16_UNSIGNED(a) SwapUInt16((a))
|
||||||
#define BSWAP_INT32_SIGNED(a) SwapInt32((a))
|
#define BSWAP_INT32_SIGNED(a) SwapInt32((a))
|
||||||
#define BSWAP_INT32_UNSIGNED(a) SwapUInt32((a))
|
#define BSWAP_INT32_UNSIGNED(a) SwapUInt32((a))
|
||||||
#define BSWAP_INT64_SIGNED(a) SwapInt64((a))
|
#define BSWAP_INT64_SIGNED(a) SwapInt64((a))
|
||||||
#define BSWAP_INT64_UNSIGNED(a) SwapUInt64((a))
|
#define BSWAP_INT64_UNSIGNED(a) SwapUInt64((a))
|
||||||
#define BSWAP_ARRAY16_UNSIGNED(a,b) ConvertUInt16Buffer((a),(b))
|
#define BSWAP_ARRAY16_UNSIGNED(a,b) ConvertUInt16Buffer((a),(b))
|
||||||
#define BSWAP_ARRAY32_UNSIGNED(a,b) ConvertUInt32Buffer((a),(b))
|
#define BSWAP_ARRAY32_UNSIGNED(a,b) ConvertUInt32Buffer((a),(b))
|
||||||
#define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b))
|
#define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b))
|
||||||
#define BSWAP_PART_HEADER(a) ConvertPartHeader(a)
|
#define BSWAP_TMPQHEADER(a,b) ConvertTMPQHeader((a),(b))
|
||||||
#define BSWAP_TMPQUSERDATA(a) ConvertTMPQUserData((a))
|
#define BSWAP_TMPKHEADER(a) ConvertTMPKHeader((a))
|
||||||
#define BSWAP_TMPQHEADER(a) ConvertTMPQHeader((a))
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Macro for deprecated symbols
|
||||||
|
|
||||||
|
/*
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#if _MSC_FULL_VER >= 140050320
|
||||||
|
#define STORMLIB_DEPRECATED(_Text) __declspec(deprecated(_Text))
|
||||||
|
#else
|
||||||
|
#define STORMLIB_DEPRECATED(_Text) __declspec(deprecated)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define STORMLIB_DEPRECATED(_Text) __attribute__((deprecated))
|
||||||
|
#else
|
||||||
|
#define STORMLIB_DEPRECATED(_Text) __attribute__((deprecated(_Text)))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// When a flag is deprecated, use this macro
|
||||||
|
#ifndef _STORMLIB_NO_DEPRECATE
|
||||||
|
#define STORMLIB_DEPRECATED_FLAG(type, oldflag, newflag) \
|
||||||
|
const STORMLIB_DEPRECATED(#oldflag " is deprecated. Use " #newflag ". To supress this warning, define _STORMLIB_NO_DEPRECATE") static type oldflag = (type)newflag;
|
||||||
|
#else
|
||||||
|
#define STORMLIB_DEPRECATED_FLAG(type, oldflag, newflag) static type oldflag = (type)newflag;
|
||||||
|
#endif
|
||||||
|
*/
|
||||||
|
|
||||||
#endif // __STORMPORT_H__
|
#endif // __STORMPORT_H__
|
||||||
|
|
|
||||||
|
|
@ -10,349 +10,389 @@
|
||||||
/* 11.03.03 1.00 Lad Splitted from Pkware.cpp */
|
/* 11.03.03 1.00 Lad Splitted from Pkware.cpp */
|
||||||
/* 20.05.03 2.00 Lad Added compression */
|
/* 20.05.03 2.00 Lad Added compression */
|
||||||
/* 19.11.03 2.01 Dan Big endian handling */
|
/* 19.11.03 2.01 Dan Big endian handling */
|
||||||
|
/* 10.01.13 3.00 Lad Refactored, beautified, documented :-) */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#include "../StormPort.h"
|
||||||
#include "adpcm.h"
|
#include "adpcm.h"
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Structures
|
|
||||||
|
|
||||||
typedef union _BYTE_AND_WORD_PTR
|
|
||||||
{
|
|
||||||
short * pw;
|
|
||||||
unsigned char * pb;
|
|
||||||
} BYTE_AND_WORD_PTR;
|
|
||||||
|
|
||||||
typedef union _WORD_AND_BYTE_ARRAY
|
|
||||||
{
|
|
||||||
short w;
|
|
||||||
unsigned char b[2];
|
|
||||||
} WORD_AND_BYTE_ARRAY;
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Tables necessary dor decompression
|
// Tables necessary dor decompression
|
||||||
|
|
||||||
static long Table1503F120[] =
|
static int NextStepTable[] =
|
||||||
{
|
{
|
||||||
0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000006,
|
-1, 0, -1, 4, -1, 2, -1, 6,
|
||||||
0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007,
|
-1, 1, -1, 5, -1, 3, -1, 7,
|
||||||
0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007,
|
-1, 1, -1, 5, -1, 3, -1, 7,
|
||||||
0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000006, 0xFFFFFFFF, 0x00000008
|
-1, 2, -1, 4, -1, 6, -1, 8
|
||||||
};
|
};
|
||||||
|
|
||||||
static long step_table[] =
|
static int StepSizeTable[] =
|
||||||
{
|
{
|
||||||
0x00000007, 0x00000008, 0x00000009, 0x0000000A, 0x0000000B, 0x0000000C, 0x0000000D, 0x0000000E,
|
7, 8, 9, 10, 11, 12, 13, 14,
|
||||||
0x00000010, 0x00000011, 0x00000013, 0x00000015, 0x00000017, 0x00000019, 0x0000001C, 0x0000001F,
|
16, 17, 19, 21, 23, 25, 28, 31,
|
||||||
0x00000022, 0x00000025, 0x00000029, 0x0000002D, 0x00000032, 0x00000037, 0x0000003C, 0x00000042,
|
34, 37, 41, 45, 50, 55, 60, 66,
|
||||||
0x00000049, 0x00000050, 0x00000058, 0x00000061, 0x0000006B, 0x00000076, 0x00000082, 0x0000008F,
|
73, 80, 88, 97, 107, 118, 130, 143,
|
||||||
0x0000009D, 0x000000AD, 0x000000BE, 0x000000D1, 0x000000E6, 0x000000FD, 0x00000117, 0x00000133,
|
157, 173, 190, 209, 230, 253, 279, 307,
|
||||||
0x00000151, 0x00000173, 0x00000198, 0x000001C1, 0x000001EE, 0x00000220, 0x00000256, 0x00000292,
|
337, 371, 408, 449, 494, 544, 598, 658,
|
||||||
0x000002D4, 0x0000031C, 0x0000036C, 0x000003C3, 0x00000424, 0x0000048E, 0x00000502, 0x00000583,
|
724, 796, 876, 963, 1060, 1166, 1282, 1411,
|
||||||
0x00000610, 0x000006AB, 0x00000756, 0x00000812, 0x000008E0, 0x000009C3, 0x00000ABD, 0x00000BD0,
|
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
|
||||||
0x00000CFF, 0x00000E4C, 0x00000FBA, 0x0000114C, 0x00001307, 0x000014EE, 0x00001706, 0x00001954,
|
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
|
||||||
0x00001BDC, 0x00001EA5, 0x000021B6, 0x00002515, 0x000028CA, 0x00002CDF, 0x0000315B, 0x0000364B,
|
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||||
0x00003BB9, 0x000041B2, 0x00004844, 0x00004F7E, 0x00005771, 0x0000602F, 0x000069CE, 0x00007462,
|
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
|
||||||
0x00007FFF
|
32767
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Helper class for writing output ADPCM data
|
||||||
|
|
||||||
|
class TADPCMStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
TADPCMStream(void * pvBuffer, size_t cbBuffer)
|
||||||
|
{
|
||||||
|
pbBufferEnd = (unsigned char *)pvBuffer + cbBuffer;
|
||||||
|
pbBuffer = (unsigned char *)pvBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadByteSample(unsigned char & ByteSample)
|
||||||
|
{
|
||||||
|
// Check if there is enough space in the buffer
|
||||||
|
if(pbBuffer >= pbBufferEnd)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ByteSample = *pbBuffer++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WriteByteSample(unsigned char ByteSample)
|
||||||
|
{
|
||||||
|
// Check if there is enough space in the buffer
|
||||||
|
if(pbBuffer >= pbBufferEnd)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*pbBuffer++ = ByteSample;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadWordSample(short & OneSample)
|
||||||
|
{
|
||||||
|
// Check if we have enough space in the output buffer
|
||||||
|
if((size_t)(pbBufferEnd - pbBuffer) < sizeof(short))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Write the sample
|
||||||
|
OneSample = pbBuffer[0] + (((short)pbBuffer[1]) << 0x08);
|
||||||
|
pbBuffer += sizeof(short);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WriteWordSample(short OneSample)
|
||||||
|
{
|
||||||
|
// Check if we have enough space in the output buffer
|
||||||
|
if((size_t)(pbBufferEnd - pbBuffer) < sizeof(short))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Write the sample
|
||||||
|
*pbBuffer++ = (unsigned char)(OneSample & 0xFF);
|
||||||
|
*pbBuffer++ = (unsigned char)(OneSample >> 0x08);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LengthProcessed(void * pvBuffer)
|
||||||
|
{
|
||||||
|
return pbBuffer - (unsigned char *)pvBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char * pbBufferEnd;
|
||||||
|
unsigned char * pbBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// CompressWave
|
// Local functions
|
||||||
|
|
||||||
// 1500EF70
|
static inline short GetNextStepIndex(int StepIndex, unsigned int EncodedSample)
|
||||||
int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nChannels, int nCmpLevel)
|
|
||||||
// ECX EDX
|
|
||||||
{
|
{
|
||||||
WORD_AND_BYTE_ARRAY Wcmp;
|
// Get the next step index
|
||||||
BYTE_AND_WORD_PTR out; // Pointer to the output buffer
|
StepIndex = StepIndex + NextStepTable[EncodedSample & 0x1F];
|
||||||
long SInt32Array1[2];
|
|
||||||
long SInt32Array2[2];
|
|
||||||
long SInt32Array3[2];
|
|
||||||
long nBytesRemains = dwOutLength; // Number of bytes remaining
|
|
||||||
long nWordsRemains; // Number of words remaining
|
|
||||||
// unsigned char * pbSaveOutBuffer; // Copy of output buffer (actually not used)
|
|
||||||
unsigned long dwBitBuff;
|
|
||||||
unsigned long dwStopBit;
|
|
||||||
unsigned long dwBit;
|
|
||||||
unsigned long ebx;
|
|
||||||
unsigned long esi;
|
|
||||||
long nTableValue;
|
|
||||||
long nOneWord;
|
|
||||||
long var_1C;
|
|
||||||
long var_2C;
|
|
||||||
int nLength;
|
|
||||||
int nIndex;
|
|
||||||
int nValue;
|
|
||||||
int i, chnl;
|
|
||||||
|
|
||||||
// If less than 2 bytes remain, don't decompress anything
|
// Don't make the step index overflow
|
||||||
// pbSaveOutBuffer = pbOutBuffer;
|
if(StepIndex < 0)
|
||||||
out.pb = pbOutBuffer;
|
StepIndex = 0;
|
||||||
if(nBytesRemains < 2)
|
else if(StepIndex > 88)
|
||||||
return 2;
|
StepIndex = 88;
|
||||||
|
|
||||||
Wcmp.b[1] = (unsigned char)(nCmpLevel - 1);
|
return (short)StepIndex;
|
||||||
Wcmp.b[0] = (unsigned char)0;
|
}
|
||||||
|
|
||||||
*out.pw++ = BSWAP_INT16_SIGNED(Wcmp.w);
|
static inline int UpdatePredictedSample(int PredictedSample, int EncodedSample, int Difference)
|
||||||
if((out.pb - pbOutBuffer + (nChannels * 2)) > nBytesRemains)
|
{
|
||||||
return (int)(out.pb - pbOutBuffer + (nChannels * 2));
|
// Is the sign bit set?
|
||||||
|
if(EncodedSample & 0x40)
|
||||||
SInt32Array1[0] = SInt32Array1[1] = 0x2C;
|
|
||||||
|
|
||||||
for(i = 0; i < nChannels; i++)
|
|
||||||
{
|
{
|
||||||
nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++);
|
PredictedSample -= Difference;
|
||||||
*out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord);
|
if(PredictedSample <= -32768)
|
||||||
SInt32Array2[i] = nOneWord;
|
PredictedSample = -32768;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PredictedSample += Difference;
|
||||||
|
if(PredictedSample >= 32767)
|
||||||
|
PredictedSample = 32767;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Weird. But it's there
|
return PredictedSample;
|
||||||
nLength = dwInLength;
|
}
|
||||||
if(nLength < 0) // mov eax, dwInLength; cdq; sub eax, edx;
|
|
||||||
nLength++;
|
|
||||||
|
|
||||||
nLength = (nLength / 2) - (int)(out.pb - pbOutBuffer);
|
static inline int DecodeSample(int PredictedSample, int EncodedSample, int StepSize, int Difference)
|
||||||
nLength = (nLength < 0) ? 0 : nLength;
|
{
|
||||||
|
if(EncodedSample & 0x01)
|
||||||
|
Difference += (StepSize >> 0);
|
||||||
|
|
||||||
nIndex = nChannels - 1; // edi
|
if(EncodedSample & 0x02)
|
||||||
nWordsRemains = dwInLength / 2; // eax
|
Difference += (StepSize >> 1);
|
||||||
|
|
||||||
// ebx - nChannels
|
if(EncodedSample & 0x04)
|
||||||
// ecx - pwOutPos
|
Difference += (StepSize >> 2);
|
||||||
for(chnl = nChannels; chnl < nWordsRemains; chnl++)
|
|
||||||
{
|
|
||||||
// 1500F030
|
|
||||||
if((out.pb - pbOutBuffer + 2) > nBytesRemains)
|
|
||||||
return (int)(out.pb - pbOutBuffer + 2);
|
|
||||||
|
|
||||||
// Switch index
|
if(EncodedSample & 0x08)
|
||||||
if(nChannels == 2)
|
Difference += (StepSize >> 3);
|
||||||
nIndex = (nIndex == 0) ? 1 : 0;
|
|
||||||
|
|
||||||
// Load one word from the input stream
|
if(EncodedSample & 0x10)
|
||||||
nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++); // ecx - nOneWord
|
Difference += (StepSize >> 4);
|
||||||
SInt32Array3[nIndex] = nOneWord;
|
|
||||||
|
|
||||||
// esi - SInt32Array2[nIndex]
|
if(EncodedSample & 0x20)
|
||||||
// eax - nValue
|
Difference += (StepSize >> 5);
|
||||||
nValue = nOneWord - SInt32Array2[nIndex];
|
|
||||||
nValue = (nValue < 0) ? ((nValue ^ 0xFFFFFFFF) + 1) : nValue;
|
|
||||||
|
|
||||||
ebx = (nOneWord >= SInt32Array2[nIndex]) ? 0 : 0x40;
|
return UpdatePredictedSample(PredictedSample, EncodedSample, Difference);
|
||||||
|
|
||||||
// esi - SInt32Array2[nIndex]
|
|
||||||
// edx - step_table[SInt32Array2[nIndex]]
|
|
||||||
// edi - (step_table[SInt32Array1[nIndex]] >> nCmpLevel)
|
|
||||||
nTableValue = step_table[SInt32Array1[nIndex]];
|
|
||||||
dwStopBit = (unsigned long)nCmpLevel;
|
|
||||||
|
|
||||||
// edi - nIndex;
|
|
||||||
if(nValue < (nTableValue >> nCmpLevel))
|
|
||||||
{
|
|
||||||
if(SInt32Array1[nIndex] != 0)
|
|
||||||
SInt32Array1[nIndex]--;
|
|
||||||
*out.pb++ = 0x80;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while(nValue > nTableValue * 2)
|
|
||||||
{
|
|
||||||
if(SInt32Array1[nIndex] >= 0x58 || nLength == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
SInt32Array1[nIndex] += 8;
|
|
||||||
if(SInt32Array1[nIndex] > 0x58)
|
|
||||||
SInt32Array1[nIndex] = 0x58;
|
|
||||||
|
|
||||||
nTableValue = step_table[SInt32Array1[nIndex]];
|
|
||||||
*out.pb++ = 0x81;
|
|
||||||
nLength--;
|
|
||||||
}
|
|
||||||
|
|
||||||
var_2C = nTableValue >> Wcmp.b[1];
|
|
||||||
dwBitBuff = 0;
|
|
||||||
|
|
||||||
esi = (1 << (dwStopBit - 2));
|
|
||||||
dwStopBit = (esi <= 0x20) ? esi : 0x20;
|
|
||||||
|
|
||||||
for(var_1C = 0, dwBit = 1; ; dwBit <<= 1)
|
|
||||||
{
|
|
||||||
// esi = var_1C + nTableValue;
|
|
||||||
if((var_1C + nTableValue) <= nValue)
|
|
||||||
{
|
|
||||||
var_1C += nTableValue;
|
|
||||||
dwBitBuff |= dwBit;
|
|
||||||
}
|
|
||||||
if(dwBit == dwStopBit)
|
|
||||||
break;
|
|
||||||
|
|
||||||
nTableValue >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
nValue = SInt32Array2[nIndex];
|
|
||||||
if(ebx != 0)
|
|
||||||
{
|
|
||||||
nValue -= (var_1C + var_2C);
|
|
||||||
if(nValue < -32768)
|
|
||||||
nValue = -32768;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
nValue += (var_1C + var_2C);
|
|
||||||
if(nValue > 32767)
|
|
||||||
nValue = 32767;
|
|
||||||
}
|
|
||||||
|
|
||||||
SInt32Array2[nIndex] = nValue;
|
|
||||||
*out.pb++ = (unsigned char)(dwBitBuff | ebx);
|
|
||||||
nTableValue = Table1503F120[dwBitBuff & 0x1F];
|
|
||||||
SInt32Array1[nIndex] = SInt32Array1[nIndex] + nTableValue;
|
|
||||||
if(SInt32Array1[nIndex] < 0)
|
|
||||||
SInt32Array1[nIndex] = 0;
|
|
||||||
else if(SInt32Array1[nIndex] > 0x58)
|
|
||||||
SInt32Array1[nIndex] = 0x58;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int)(out.pb - pbOutBuffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
// DecompressADPCM
|
// Compression routine
|
||||||
|
|
||||||
// 1500F230
|
int CompressADPCM(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount, int CompressionLevel)
|
||||||
int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels)
|
|
||||||
{
|
{
|
||||||
BYTE_AND_WORD_PTR out; // Output buffer
|
TADPCMStream os(pvOutBuffer, cbOutBuffer); // The output stream
|
||||||
BYTE_AND_WORD_PTR in;
|
TADPCMStream is(pvInBuffer, cbInBuffer); // The input stream
|
||||||
unsigned char * pbInBufferEnd = (pbInBuffer + dwInLength);
|
unsigned char BitShift = (unsigned char)(CompressionLevel - 1);
|
||||||
long SInt32Array1[2];
|
short PredictedSamples[MAX_ADPCM_CHANNEL_COUNT];// Predicted samples for each channel
|
||||||
long SInt32Array2[2];
|
short StepIndexes[MAX_ADPCM_CHANNEL_COUNT]; // Step indexes for each channel
|
||||||
long nOneWord;
|
short InputSample; // Input sample for the current channel
|
||||||
int nIndex;
|
int TotalStepSize;
|
||||||
int i;
|
int ChannelIndex;
|
||||||
|
int AbsDifference;
|
||||||
|
int Difference;
|
||||||
|
int MaxBitMask;
|
||||||
|
int StepSize;
|
||||||
|
|
||||||
SInt32Array1[0] = SInt32Array1[1] = 0x2C;
|
// _tprintf(_T("== CMPR Started ==============\n"));
|
||||||
out.pb = pbOutBuffer;
|
|
||||||
in.pb = pbInBuffer;
|
|
||||||
in.pw++;
|
|
||||||
|
|
||||||
// Fill the Uint32Array2 array by channel values.
|
// First byte in the output stream contains zero. The second one contains the compression level
|
||||||
for(i = 0; i < nChannels; i++)
|
os.WriteByteSample(0);
|
||||||
|
if(!os.WriteByteSample(BitShift))
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
// Set the initial step index for each channel
|
||||||
|
StepIndexes[0] = StepIndexes[1] = INITIAL_ADPCM_STEP_INDEX;
|
||||||
|
|
||||||
|
// Next, InitialSample value for each channel follows
|
||||||
|
for(int i = 0; i < ChannelCount; i++)
|
||||||
{
|
{
|
||||||
nOneWord = BSWAP_INT16_SIGNED(*in.pw++);
|
// Get the initial sample from the input stream
|
||||||
SInt32Array2[i] = nOneWord;
|
if(!is.ReadWordSample(InputSample))
|
||||||
if(dwOutLength < 2)
|
return os.LengthProcessed(pvOutBuffer);
|
||||||
return (int)(out.pb - pbOutBuffer);
|
|
||||||
|
|
||||||
*out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord);
|
// Store the initial sample to our sample array
|
||||||
dwOutLength -= sizeof(short);
|
PredictedSamples[i] = InputSample;
|
||||||
|
|
||||||
|
// Also store the loaded sample to the output stream
|
||||||
|
if(!os.WriteWordSample(InputSample))
|
||||||
|
return os.LengthProcessed(pvOutBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the initial index
|
// Get the initial index
|
||||||
nIndex = nChannels - 1;
|
ChannelIndex = ChannelCount - 1;
|
||||||
|
|
||||||
// Perform the decompression
|
// Now keep reading the input data as long as there is something in the input buffer
|
||||||
while(in.pb < pbInBufferEnd)
|
while(is.ReadWordSample(InputSample))
|
||||||
{
|
{
|
||||||
unsigned char nOneByte = *in.pb++;
|
int EncodedSample = 0;
|
||||||
|
|
||||||
// Switch index
|
// If we have two channels, we need to flip the channel index
|
||||||
if(nChannels == 2)
|
ChannelIndex = (ChannelIndex + 1) % ChannelCount;
|
||||||
nIndex = (nIndex == 0) ? 1 : 0;
|
|
||||||
|
|
||||||
// 1500F2A2: Get one byte from input buffer
|
// Get the difference from the previous sample.
|
||||||
if(nOneByte & 0x80)
|
// If the difference is negative, set the sign bit to the encoded sample
|
||||||
|
AbsDifference = InputSample - PredictedSamples[ChannelIndex];
|
||||||
|
if(AbsDifference < 0)
|
||||||
{
|
{
|
||||||
switch(nOneByte & 0x7F)
|
AbsDifference = -AbsDifference;
|
||||||
{
|
EncodedSample |= 0x40;
|
||||||
case 0: // 1500F315
|
}
|
||||||
if(SInt32Array1[nIndex] != 0)
|
|
||||||
SInt32Array1[nIndex]--;
|
|
||||||
|
|
||||||
if(dwOutLength < 2)
|
// If the difference is too low (higher that difference treshold),
|
||||||
return (int)(out.pb - pbOutBuffer);
|
// write a step index modifier marker
|
||||||
|
StepSize = StepSizeTable[StepIndexes[ChannelIndex]];
|
||||||
*out.pw++ = BSWAP_INT16_SIGNED((unsigned short)SInt32Array2[nIndex]);
|
if(AbsDifference < (StepSize >> CompressionLevel))
|
||||||
dwOutLength -= sizeof(unsigned short);
|
{
|
||||||
break;
|
if(StepIndexes[ChannelIndex] != 0)
|
||||||
|
StepIndexes[ChannelIndex]--;
|
||||||
case 1: // 1500F2E8
|
|
||||||
SInt32Array1[nIndex] += 8;
|
os.WriteByteSample(0x80);
|
||||||
if(SInt32Array1[nIndex] > 0x58)
|
|
||||||
SInt32Array1[nIndex] = 0x58;
|
|
||||||
|
|
||||||
if(nChannels == 2)
|
|
||||||
nIndex = (nIndex == 0) ? 1 : 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: // 1500F41E
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: // 1500F2C4
|
|
||||||
SInt32Array1[nIndex] -= 8;
|
|
||||||
if(SInt32Array1[nIndex] < 0)
|
|
||||||
SInt32Array1[nIndex] = 0;
|
|
||||||
|
|
||||||
if(nChannels == 2)
|
|
||||||
nIndex = (nIndex == 0) ? 1 : 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 1500F349
|
// If the difference is too high, write marker that
|
||||||
long temp1 = step_table[SInt32Array1[nIndex]]; // EDI
|
// indicates increase in step size
|
||||||
long temp2 = temp1 >> pbInBuffer[1]; // ESI
|
while(AbsDifference > (StepSize << 1))
|
||||||
long temp3 = SInt32Array2[nIndex]; // ECX
|
|
||||||
|
|
||||||
if(nOneByte & 0x01) // EBX = nOneByte
|
|
||||||
temp2 += (temp1 >> 0);
|
|
||||||
|
|
||||||
if(nOneByte & 0x02)
|
|
||||||
temp2 += (temp1 >> 1);
|
|
||||||
|
|
||||||
if(nOneByte & 0x04)
|
|
||||||
temp2 += (temp1 >> 2);
|
|
||||||
|
|
||||||
if(nOneByte & 0x08)
|
|
||||||
temp2 += (temp1 >> 3);
|
|
||||||
|
|
||||||
if(nOneByte & 0x10)
|
|
||||||
temp2 += (temp1 >> 4);
|
|
||||||
|
|
||||||
if(nOneByte & 0x20)
|
|
||||||
temp2 += (temp1 >> 5);
|
|
||||||
|
|
||||||
if(nOneByte & 0x40)
|
|
||||||
{
|
{
|
||||||
temp3 = temp3 - temp2;
|
if(StepIndexes[ChannelIndex] >= 0x58)
|
||||||
if(temp3 <= -32768)
|
break;
|
||||||
temp3 = -32768;
|
|
||||||
}
|
// Modify the step index
|
||||||
else
|
StepIndexes[ChannelIndex] += 8;
|
||||||
{
|
if(StepIndexes[ChannelIndex] > 0x58)
|
||||||
temp3 = temp3 + temp2;
|
StepIndexes[ChannelIndex] = 0x58;
|
||||||
if(temp3 >= 32767)
|
|
||||||
temp3 = 32767;
|
// Write the "modify step index" marker
|
||||||
|
StepSize = StepSizeTable[StepIndexes[ChannelIndex]];
|
||||||
|
os.WriteByteSample(0x81);
|
||||||
}
|
}
|
||||||
|
|
||||||
SInt32Array2[nIndex] = temp3;
|
// Get the limit bit value
|
||||||
if(dwOutLength < 2)
|
MaxBitMask = (1 << (BitShift - 1));
|
||||||
|
MaxBitMask = (MaxBitMask > 0x20) ? 0x20 : MaxBitMask;
|
||||||
|
Difference = StepSize >> BitShift;
|
||||||
|
TotalStepSize = 0;
|
||||||
|
|
||||||
|
for(int BitVal = 0x01; BitVal <= MaxBitMask; BitVal <<= 1)
|
||||||
|
{
|
||||||
|
if((TotalStepSize + StepSize) <= AbsDifference)
|
||||||
|
{
|
||||||
|
TotalStepSize += StepSize;
|
||||||
|
EncodedSample |= BitVal;
|
||||||
|
}
|
||||||
|
StepSize >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PredictedSamples[ChannelIndex] = (short)UpdatePredictedSample(PredictedSamples[ChannelIndex],
|
||||||
|
EncodedSample,
|
||||||
|
Difference + TotalStepSize);
|
||||||
|
// Write the encoded sample to the output stream
|
||||||
|
if(!os.WriteByteSample((unsigned char)EncodedSample))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Store the output 16-bit value
|
// Calculates the step index to use for the next encode
|
||||||
*out.pw++ = BSWAP_INT16_SIGNED((short)SInt32Array2[nIndex]);
|
StepIndexes[ChannelIndex] = GetNextStepIndex(StepIndexes[ChannelIndex], EncodedSample);
|
||||||
dwOutLength -= 2;
|
|
||||||
|
|
||||||
SInt32Array1[nIndex] += Table1503F120[nOneByte & 0x1F];
|
|
||||||
|
|
||||||
if(SInt32Array1[nIndex] < 0)
|
|
||||||
SInt32Array1[nIndex] = 0;
|
|
||||||
else if(SInt32Array1[nIndex] > 0x58)
|
|
||||||
SInt32Array1[nIndex] = 0x58;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (int)(out.pb - pbOutBuffer);
|
|
||||||
|
// _tprintf(_T("== CMPR Ended ================\n"));
|
||||||
|
return os.LengthProcessed(pvOutBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// Decompression routine
|
||||||
|
|
||||||
|
int DecompressADPCM(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount)
|
||||||
|
{
|
||||||
|
TADPCMStream os(pvOutBuffer, cbOutBuffer); // Output stream
|
||||||
|
TADPCMStream is(pvInBuffer, cbInBuffer); // Input stream
|
||||||
|
unsigned char EncodedSample;
|
||||||
|
unsigned char BitShift;
|
||||||
|
short PredictedSamples[MAX_ADPCM_CHANNEL_COUNT]; // Predicted sample for each channel
|
||||||
|
short StepIndexes[MAX_ADPCM_CHANNEL_COUNT]; // Predicted step index for each channel
|
||||||
|
int ChannelIndex; // Current channel index
|
||||||
|
|
||||||
|
// Initialize the StepIndex for each channel
|
||||||
|
StepIndexes[0] = StepIndexes[1] = INITIAL_ADPCM_STEP_INDEX;
|
||||||
|
|
||||||
|
// _tprintf(_T("== DCMP Started ==============\n"));
|
||||||
|
|
||||||
|
// The first byte is always zero, the second one contains bit shift (compression level - 1)
|
||||||
|
is.ReadByteSample(BitShift);
|
||||||
|
is.ReadByteSample(BitShift);
|
||||||
|
// _tprintf(_T("DCMP: BitShift = %u\n"), (unsigned int)(unsigned char)BitShift);
|
||||||
|
|
||||||
|
// Next, InitialSample value for each channel follows
|
||||||
|
for(int i = 0; i < ChannelCount; i++)
|
||||||
|
{
|
||||||
|
// Get the initial sample from the input stream
|
||||||
|
short InitialSample;
|
||||||
|
|
||||||
|
// Attempt to read the initial sample
|
||||||
|
if(!is.ReadWordSample(InitialSample))
|
||||||
|
return os.LengthProcessed(pvOutBuffer);
|
||||||
|
|
||||||
|
// _tprintf(_T("DCMP: Loaded InitialSample[%u]: %04X\n"), i, (unsigned int)(unsigned short)InitialSample);
|
||||||
|
|
||||||
|
// Store the initial sample to our sample array
|
||||||
|
PredictedSamples[i] = InitialSample;
|
||||||
|
|
||||||
|
// Also store the loaded sample to the output stream
|
||||||
|
if(!os.WriteWordSample(InitialSample))
|
||||||
|
return os.LengthProcessed(pvOutBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the initial index
|
||||||
|
ChannelIndex = ChannelCount - 1;
|
||||||
|
|
||||||
|
// Keep reading as long as there is something in the input buffer
|
||||||
|
while(is.ReadByteSample(EncodedSample))
|
||||||
|
{
|
||||||
|
// _tprintf(_T("DCMP: Loaded Encoded Sample: %02X\n"), (unsigned int)(unsigned char)EncodedSample);
|
||||||
|
|
||||||
|
// If we have two channels, we need to flip the channel index
|
||||||
|
ChannelIndex = (ChannelIndex + 1) % ChannelCount;
|
||||||
|
|
||||||
|
if(EncodedSample == 0x80)
|
||||||
|
{
|
||||||
|
if(StepIndexes[ChannelIndex] != 0)
|
||||||
|
StepIndexes[ChannelIndex]--;
|
||||||
|
|
||||||
|
// _tprintf(_T("DCMP: Writing Decoded Sample: %04lX\n"), (unsigned int)(unsigned short)PredictedSamples[ChannelIndex]);
|
||||||
|
if(!os.WriteWordSample(PredictedSamples[ChannelIndex]))
|
||||||
|
return os.LengthProcessed(pvOutBuffer);
|
||||||
|
}
|
||||||
|
else if(EncodedSample == 0x81)
|
||||||
|
{
|
||||||
|
// Modify the step index
|
||||||
|
StepIndexes[ChannelIndex] += 8;
|
||||||
|
if(StepIndexes[ChannelIndex] > 0x58)
|
||||||
|
StepIndexes[ChannelIndex] = 0x58;
|
||||||
|
|
||||||
|
// _tprintf(_T("DCMP: New value of StepIndex: %04lX\n"), (unsigned int)(unsigned short)StepIndexes[ChannelIndex]);
|
||||||
|
|
||||||
|
// Next pass, keep going on the same channel
|
||||||
|
ChannelIndex = (ChannelIndex + 1) % ChannelCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int StepIndex = StepIndexes[ChannelIndex];
|
||||||
|
int StepSize = StepSizeTable[StepIndex];
|
||||||
|
|
||||||
|
// Encode one sample
|
||||||
|
PredictedSamples[ChannelIndex] = (short)DecodeSample(PredictedSamples[ChannelIndex],
|
||||||
|
EncodedSample,
|
||||||
|
StepSize,
|
||||||
|
StepSize >> BitShift);
|
||||||
|
|
||||||
|
// _tprintf(_T("DCMP: Writing decoded sample: %04X\n"), (unsigned int)(unsigned short)PredictedSamples[ChannelIndex]);
|
||||||
|
|
||||||
|
// Write the decoded sample to the output stream
|
||||||
|
if(!os.WriteWordSample(PredictedSamples[ChannelIndex]))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Calculates the step index to use for the next encode
|
||||||
|
StepIndexes[ChannelIndex] = GetNextStepIndex(StepIndex, EncodedSample);
|
||||||
|
// _tprintf(_T("DCMP: New step index: %04X\n"), (unsigned int)(unsigned short)StepIndexes[ChannelIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _tprintf(_T("DCMP: Total length written: %u\n"), (unsigned int)os.LengthProcessed(pvOutBuffer));
|
||||||
|
// _tprintf(_T("== DCMP Ended ================\n"));
|
||||||
|
|
||||||
|
// Return total bytes written since beginning of the output buffer
|
||||||
|
return os.LengthProcessed(pvOutBuffer);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,15 @@
|
||||||
#define __ADPCM_H__
|
#define __ADPCM_H__
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Functions
|
// Defines
|
||||||
|
|
||||||
#include "../StormPort.h"
|
#define MAX_ADPCM_CHANNEL_COUNT 2
|
||||||
|
#define INITIAL_ADPCM_STEP_INDEX 0x2C
|
||||||
|
|
||||||
int CompressADPCM (unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nCmpType, int nChannels);
|
//-----------------------------------------------------------------------------
|
||||||
int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels);
|
// Public functions
|
||||||
|
|
||||||
|
int CompressADPCM (void * pvOutBuffer, int dwOutLength, void * pvInBuffer, int dwInLength, int nCmpType, int ChannelCount);
|
||||||
|
int DecompressADPCM(void * pvOutBuffer, int dwOutLength, void * pvInBuffer, int dwInLength, int ChannelCount);
|
||||||
|
|
||||||
#endif // __ADPCM_H__
|
#endif // __ADPCM_H__
|
||||||
|
|
|
||||||
358
dep/StormLib/src/adpcm/adpcm_old.cpp
Normal file
358
dep/StormLib/src/adpcm/adpcm_old.cpp
Normal file
|
|
@ -0,0 +1,358 @@
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* adpcm.cpp Copyright (c) Ladislav Zezula 2003 */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* This module contains implementation of adpcm decompression method used by */
|
||||||
|
/* Storm.dll to decompress WAVE files. Thanks to Tom Amigo for releasing */
|
||||||
|
/* his sources. */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Date Ver Who Comment */
|
||||||
|
/* -------- ---- --- ------- */
|
||||||
|
/* 11.03.03 1.00 Lad Splitted from Pkware.cpp */
|
||||||
|
/* 20.05.03 2.00 Lad Added compression */
|
||||||
|
/* 19.11.03 2.01 Dan Big endian handling */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#include "adpcm.h"
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Structures
|
||||||
|
|
||||||
|
typedef union _BYTE_AND_WORD_PTR
|
||||||
|
{
|
||||||
|
short * pw;
|
||||||
|
unsigned char * pb;
|
||||||
|
} BYTE_AND_WORD_PTR;
|
||||||
|
|
||||||
|
typedef union _WORD_AND_BYTE_ARRAY
|
||||||
|
{
|
||||||
|
short w;
|
||||||
|
unsigned char b[2];
|
||||||
|
} WORD_AND_BYTE_ARRAY;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Tables necessary dor decompression
|
||||||
|
|
||||||
|
static long Table1503F120[] =
|
||||||
|
{
|
||||||
|
0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000006,
|
||||||
|
0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007,
|
||||||
|
0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, 0x00000005, 0xFFFFFFFF, 0x00000003, 0xFFFFFFFF, 0x00000007,
|
||||||
|
0xFFFFFFFF, 0x00000002, 0xFFFFFFFF, 0x00000004, 0xFFFFFFFF, 0x00000006, 0xFFFFFFFF, 0x00000008
|
||||||
|
};
|
||||||
|
|
||||||
|
static long step_table[] =
|
||||||
|
{
|
||||||
|
0x00000007, 0x00000008, 0x00000009, 0x0000000A, 0x0000000B, 0x0000000C, 0x0000000D, 0x0000000E,
|
||||||
|
0x00000010, 0x00000011, 0x00000013, 0x00000015, 0x00000017, 0x00000019, 0x0000001C, 0x0000001F,
|
||||||
|
0x00000022, 0x00000025, 0x00000029, 0x0000002D, 0x00000032, 0x00000037, 0x0000003C, 0x00000042,
|
||||||
|
0x00000049, 0x00000050, 0x00000058, 0x00000061, 0x0000006B, 0x00000076, 0x00000082, 0x0000008F,
|
||||||
|
0x0000009D, 0x000000AD, 0x000000BE, 0x000000D1, 0x000000E6, 0x000000FD, 0x00000117, 0x00000133,
|
||||||
|
0x00000151, 0x00000173, 0x00000198, 0x000001C1, 0x000001EE, 0x00000220, 0x00000256, 0x00000292,
|
||||||
|
0x000002D4, 0x0000031C, 0x0000036C, 0x000003C3, 0x00000424, 0x0000048E, 0x00000502, 0x00000583,
|
||||||
|
0x00000610, 0x000006AB, 0x00000756, 0x00000812, 0x000008E0, 0x000009C3, 0x00000ABD, 0x00000BD0,
|
||||||
|
0x00000CFF, 0x00000E4C, 0x00000FBA, 0x0000114C, 0x00001307, 0x000014EE, 0x00001706, 0x00001954,
|
||||||
|
0x00001BDC, 0x00001EA5, 0x000021B6, 0x00002515, 0x000028CA, 0x00002CDF, 0x0000315B, 0x0000364B,
|
||||||
|
0x00003BB9, 0x000041B2, 0x00004844, 0x00004F7E, 0x00005771, 0x0000602F, 0x000069CE, 0x00007462,
|
||||||
|
0x00007FFF
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// CompressWave
|
||||||
|
|
||||||
|
// 1500EF70
|
||||||
|
int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nChannels, int nCmpLevel)
|
||||||
|
// ECX EDX
|
||||||
|
{
|
||||||
|
WORD_AND_BYTE_ARRAY Wcmp;
|
||||||
|
BYTE_AND_WORD_PTR out; // Pointer to the output buffer
|
||||||
|
long SInt32Array1[2];
|
||||||
|
long SInt32Array2[2];
|
||||||
|
long SInt32Array3[2];
|
||||||
|
long nBytesRemains = dwOutLength; // Number of bytes remaining
|
||||||
|
long nWordsRemains; // Number of words remaining
|
||||||
|
// unsigned char * pbSaveOutBuffer; // Copy of output buffer (actually not used)
|
||||||
|
unsigned long dwBitBuff;
|
||||||
|
unsigned long dwStopBit;
|
||||||
|
unsigned long dwBit;
|
||||||
|
unsigned long ebx;
|
||||||
|
unsigned long esi;
|
||||||
|
long nTableValue;
|
||||||
|
long nOneWord;
|
||||||
|
long var_1C;
|
||||||
|
long var_2C;
|
||||||
|
int nLength;
|
||||||
|
int nIndex;
|
||||||
|
int nValue;
|
||||||
|
int i, chnl;
|
||||||
|
|
||||||
|
// If less than 2 bytes remain, don't decompress anything
|
||||||
|
// pbSaveOutBuffer = pbOutBuffer;
|
||||||
|
out.pb = pbOutBuffer;
|
||||||
|
if(nBytesRemains < 2)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
Wcmp.b[1] = (unsigned char)(nCmpLevel - 1);
|
||||||
|
Wcmp.b[0] = (unsigned char)0;
|
||||||
|
|
||||||
|
*out.pw++ = BSWAP_INT16_SIGNED(Wcmp.w);
|
||||||
|
if((out.pb - pbOutBuffer + (nChannels * 2)) > nBytesRemains)
|
||||||
|
return (int)(out.pb - pbOutBuffer + (nChannels * 2));
|
||||||
|
|
||||||
|
SInt32Array1[0] = SInt32Array1[1] = 0x2C;
|
||||||
|
|
||||||
|
for(i = 0; i < nChannels; i++)
|
||||||
|
{
|
||||||
|
nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++);
|
||||||
|
*out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord);
|
||||||
|
SInt32Array2[i] = nOneWord;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weird. But it's there
|
||||||
|
nLength = dwInLength;
|
||||||
|
if(nLength < 0) // mov eax, dwInLength; cdq; sub eax, edx;
|
||||||
|
nLength++;
|
||||||
|
|
||||||
|
nLength = (nLength / 2) - (int)(out.pb - pbOutBuffer);
|
||||||
|
nLength = (nLength < 0) ? 0 : nLength;
|
||||||
|
|
||||||
|
nIndex = nChannels - 1; // edi
|
||||||
|
nWordsRemains = dwInLength / 2; // eax
|
||||||
|
|
||||||
|
// ebx - nChannels
|
||||||
|
// ecx - pwOutPos
|
||||||
|
for(chnl = nChannels; chnl < nWordsRemains; chnl++)
|
||||||
|
{
|
||||||
|
// 1500F030
|
||||||
|
if((out.pb - pbOutBuffer + 2) > nBytesRemains)
|
||||||
|
return (int)(out.pb - pbOutBuffer + 2);
|
||||||
|
|
||||||
|
// Switch index
|
||||||
|
if(nChannels == 2)
|
||||||
|
nIndex = (nIndex == 0) ? 1 : 0;
|
||||||
|
|
||||||
|
// Load one word from the input stream
|
||||||
|
nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++); // ecx - nOneWord
|
||||||
|
SInt32Array3[nIndex] = nOneWord;
|
||||||
|
|
||||||
|
// esi - SInt32Array2[nIndex]
|
||||||
|
// eax - nValue
|
||||||
|
nValue = nOneWord - SInt32Array2[nIndex];
|
||||||
|
nValue = (nValue < 0) ? ((nValue ^ 0xFFFFFFFF) + 1) : nValue;
|
||||||
|
|
||||||
|
ebx = (nOneWord >= SInt32Array2[nIndex]) ? 0 : 0x40;
|
||||||
|
|
||||||
|
// esi - SInt32Array2[nIndex]
|
||||||
|
// edx - step_table[SInt32Array2[nIndex]]
|
||||||
|
// edi - (step_table[SInt32Array1[nIndex]] >> nCmpLevel)
|
||||||
|
nTableValue = step_table[SInt32Array1[nIndex]];
|
||||||
|
dwStopBit = (unsigned long)nCmpLevel;
|
||||||
|
|
||||||
|
// edi - nIndex;
|
||||||
|
if(nValue < (nTableValue >> nCmpLevel))
|
||||||
|
{
|
||||||
|
if(SInt32Array1[nIndex] != 0)
|
||||||
|
SInt32Array1[nIndex]--;
|
||||||
|
*out.pb++ = 0x80;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while(nValue > nTableValue * 2)
|
||||||
|
{
|
||||||
|
if(SInt32Array1[nIndex] >= 0x58 || nLength == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
SInt32Array1[nIndex] += 8;
|
||||||
|
if(SInt32Array1[nIndex] > 0x58)
|
||||||
|
SInt32Array1[nIndex] = 0x58;
|
||||||
|
|
||||||
|
nTableValue = step_table[SInt32Array1[nIndex]];
|
||||||
|
*out.pb++ = 0x81;
|
||||||
|
nLength--;
|
||||||
|
}
|
||||||
|
|
||||||
|
var_2C = nTableValue >> Wcmp.b[1];
|
||||||
|
dwBitBuff = 0;
|
||||||
|
|
||||||
|
esi = (1 << (dwStopBit - 2));
|
||||||
|
dwStopBit = (esi <= 0x20) ? esi : 0x20;
|
||||||
|
|
||||||
|
for(var_1C = 0, dwBit = 1; ; dwBit <<= 1)
|
||||||
|
{
|
||||||
|
// esi = var_1C + nTableValue;
|
||||||
|
if((var_1C + nTableValue) <= nValue)
|
||||||
|
{
|
||||||
|
var_1C += nTableValue;
|
||||||
|
dwBitBuff |= dwBit;
|
||||||
|
}
|
||||||
|
if(dwBit == dwStopBit)
|
||||||
|
break;
|
||||||
|
|
||||||
|
nTableValue >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nValue = SInt32Array2[nIndex];
|
||||||
|
if(ebx != 0)
|
||||||
|
{
|
||||||
|
nValue -= (var_1C + var_2C);
|
||||||
|
if(nValue < -32768)
|
||||||
|
nValue = -32768;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nValue += (var_1C + var_2C);
|
||||||
|
if(nValue > 32767)
|
||||||
|
nValue = 32767;
|
||||||
|
}
|
||||||
|
|
||||||
|
SInt32Array2[nIndex] = nValue;
|
||||||
|
*out.pb++ = (unsigned char)(dwBitBuff | ebx);
|
||||||
|
nTableValue = Table1503F120[dwBitBuff & 0x1F];
|
||||||
|
SInt32Array1[nIndex] = SInt32Array1[nIndex] + nTableValue;
|
||||||
|
if(SInt32Array1[nIndex] < 0)
|
||||||
|
SInt32Array1[nIndex] = 0;
|
||||||
|
else if(SInt32Array1[nIndex] > 0x58)
|
||||||
|
SInt32Array1[nIndex] = 0x58;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)(out.pb - pbOutBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// DecompressADPCM
|
||||||
|
|
||||||
|
// 1500F230
|
||||||
|
int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels)
|
||||||
|
{
|
||||||
|
BYTE_AND_WORD_PTR out; // Output buffer
|
||||||
|
BYTE_AND_WORD_PTR in;
|
||||||
|
unsigned char * pbInBufferEnd = (pbInBuffer + dwInLength);
|
||||||
|
long SInt32Array1[2];
|
||||||
|
long SInt32Array2[2];
|
||||||
|
long nOneWord;
|
||||||
|
int nIndex;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
SInt32Array1[0] = SInt32Array1[1] = 0x2C;
|
||||||
|
out.pb = pbOutBuffer;
|
||||||
|
in.pb = pbInBuffer;
|
||||||
|
in.pw++;
|
||||||
|
|
||||||
|
// Fill the Uint32Array2 array by channel values.
|
||||||
|
for(i = 0; i < nChannels; i++)
|
||||||
|
{
|
||||||
|
nOneWord = BSWAP_INT16_SIGNED(*in.pw++);
|
||||||
|
SInt32Array2[i] = nOneWord;
|
||||||
|
if(dwOutLength < 2)
|
||||||
|
return (int)(out.pb - pbOutBuffer);
|
||||||
|
|
||||||
|
*out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord);
|
||||||
|
dwOutLength -= sizeof(short);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the initial index
|
||||||
|
nIndex = nChannels - 1;
|
||||||
|
|
||||||
|
// Perform the decompression
|
||||||
|
while(in.pb < pbInBufferEnd)
|
||||||
|
{
|
||||||
|
unsigned char nOneByte = *in.pb++;
|
||||||
|
|
||||||
|
// Switch index
|
||||||
|
if(nChannels == 2)
|
||||||
|
nIndex = (nIndex == 0) ? 1 : 0;
|
||||||
|
|
||||||
|
// 1500F2A2: Get one byte from input buffer
|
||||||
|
if(nOneByte & 0x80)
|
||||||
|
{
|
||||||
|
switch(nOneByte & 0x7F)
|
||||||
|
{
|
||||||
|
case 0: // 1500F315
|
||||||
|
if(SInt32Array1[nIndex] != 0)
|
||||||
|
SInt32Array1[nIndex]--;
|
||||||
|
|
||||||
|
if(dwOutLength < 2)
|
||||||
|
return (int)(out.pb - pbOutBuffer);
|
||||||
|
|
||||||
|
*out.pw++ = BSWAP_INT16_SIGNED((unsigned short)SInt32Array2[nIndex]);
|
||||||
|
dwOutLength -= sizeof(unsigned short);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: // 1500F2E8
|
||||||
|
SInt32Array1[nIndex] += 8;
|
||||||
|
if(SInt32Array1[nIndex] > 0x58)
|
||||||
|
SInt32Array1[nIndex] = 0x58;
|
||||||
|
|
||||||
|
if(nChannels == 2)
|
||||||
|
nIndex = (nIndex == 0) ? 1 : 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: // 1500F41E
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // 1500F2C4
|
||||||
|
SInt32Array1[nIndex] -= 8;
|
||||||
|
if(SInt32Array1[nIndex] < 0)
|
||||||
|
SInt32Array1[nIndex] = 0;
|
||||||
|
|
||||||
|
if(nChannels == 2)
|
||||||
|
nIndex = (nIndex == 0) ? 1 : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 1500F349
|
||||||
|
long temp1 = step_table[SInt32Array1[nIndex]]; // EDI
|
||||||
|
long temp2 = temp1 >> pbInBuffer[1]; // ESI
|
||||||
|
long temp3 = SInt32Array2[nIndex]; // ECX
|
||||||
|
|
||||||
|
if(nOneByte & 0x01) // EBX = nOneByte
|
||||||
|
temp2 += (temp1 >> 0);
|
||||||
|
|
||||||
|
if(nOneByte & 0x02)
|
||||||
|
temp2 += (temp1 >> 1);
|
||||||
|
|
||||||
|
if(nOneByte & 0x04)
|
||||||
|
temp2 += (temp1 >> 2);
|
||||||
|
|
||||||
|
if(nOneByte & 0x08)
|
||||||
|
temp2 += (temp1 >> 3);
|
||||||
|
|
||||||
|
if(nOneByte & 0x10)
|
||||||
|
temp2 += (temp1 >> 4);
|
||||||
|
|
||||||
|
if(nOneByte & 0x20)
|
||||||
|
temp2 += (temp1 >> 5);
|
||||||
|
|
||||||
|
if(nOneByte & 0x40)
|
||||||
|
{
|
||||||
|
temp3 = temp3 - temp2;
|
||||||
|
if(temp3 <= -32768)
|
||||||
|
temp3 = -32768;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temp3 = temp3 + temp2;
|
||||||
|
if(temp3 >= 32767)
|
||||||
|
temp3 = 32767;
|
||||||
|
}
|
||||||
|
|
||||||
|
SInt32Array2[nIndex] = temp3;
|
||||||
|
if(dwOutLength < 2)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Store the output 16-bit value
|
||||||
|
*out.pw++ = BSWAP_INT16_SIGNED((short)SInt32Array2[nIndex]);
|
||||||
|
dwOutLength -= 2;
|
||||||
|
|
||||||
|
SInt32Array1[nIndex] += Table1503F120[nOneByte & 0x1F];
|
||||||
|
|
||||||
|
if(SInt32Array1[nIndex] < 0)
|
||||||
|
SInt32Array1[nIndex] = 0;
|
||||||
|
else if(SInt32Array1[nIndex] > 0x58)
|
||||||
|
SInt32Array1[nIndex] = 0x58;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (int)(out.pb - pbOutBuffer);
|
||||||
|
}
|
||||||
22
dep/StormLib/src/adpcm/adpcm_old.h
Normal file
22
dep/StormLib/src/adpcm/adpcm_old.h
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* adpcm.h Copyright (c) Ladislav Zezula 2003 */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Header file for adpcm decompress functions */
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
/* Date Ver Who Comment */
|
||||||
|
/* -------- ---- --- ------- */
|
||||||
|
/* 31.03.03 1.00 Lad The first version of adpcm.h */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef __ADPCM_H__
|
||||||
|
#define __ADPCM_H__
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Functions
|
||||||
|
|
||||||
|
#include "../StormPort.h"
|
||||||
|
|
||||||
|
int CompressADPCM (unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nCmpType, int nChannels);
|
||||||
|
int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels);
|
||||||
|
|
||||||
|
#endif // __ADPCM_H__
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -9,25 +9,15 @@
|
||||||
/* 03.05.03 2.00 Lad Added compression */
|
/* 03.05.03 2.00 Lad Added compression */
|
||||||
/* 08.12.03 2.01 Dan High-memory handling (> 0x80000000) */
|
/* 08.12.03 2.01 Dan High-memory handling (> 0x80000000) */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
#ifndef __HUFFMAN_H__
|
#ifndef __HUFFMAN_H__
|
||||||
#define __HUFFMAN_H__
|
#define __HUFFMAN_H__
|
||||||
|
|
||||||
#include "../StormPort.h"
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Defines
|
// Defines
|
||||||
|
|
||||||
#define INSERT_ITEM 1
|
#define HUFF_ITEM_COUNT 0x203 // Number of items in the item pool
|
||||||
#define SWITCH_ITEMS 2 // Switch the item1 and item2
|
#define LINK_ITEM_COUNT 0x80 // Maximum number of quick-link items
|
||||||
|
|
||||||
#define PTR_NOT(ptr) (THTreeItem *)(~(DWORD_PTR)(ptr))
|
|
||||||
#define PTR_PTR(ptr) ((THTreeItem *)(ptr))
|
|
||||||
#define PTR_INT(ptr) (INT_PTR)(ptr)
|
|
||||||
|
|
||||||
#ifndef NULL
|
|
||||||
#define NULL 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Structures and classes
|
// Structures and classes
|
||||||
|
|
@ -37,65 +27,76 @@ class TInputStream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
unsigned long GetBit();
|
TInputStream(void * pvInBuffer, size_t cbInBuffer);
|
||||||
unsigned long Get7Bits();
|
unsigned int Get1Bit();
|
||||||
unsigned long Get8Bits();
|
unsigned int Peek7Bits();
|
||||||
|
unsigned int Get8Bits();
|
||||||
void SkipBits(unsigned int BitCount);
|
void SkipBits(unsigned int BitCount);
|
||||||
|
|
||||||
unsigned char * pbInBuffer; // Input data
|
unsigned char * pbInBufferEnd; // End position in the the input buffer
|
||||||
unsigned char * pbInBufferEnd; // End of the input buffer
|
unsigned char * pbInBuffer; // Current position in the the input buffer
|
||||||
unsigned long BitBuffer; // Input bit buffer
|
unsigned int BitBuffer; // Input bit buffer
|
||||||
unsigned int BitCount; // Number of bits remaining in 'dwBitBuff'
|
unsigned int BitCount; // Number of bits remaining in 'dwBitBuff'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Output stream for Huffmann compression
|
// Output stream for Huffmann compression
|
||||||
class TOutputStream
|
class TOutputStream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void PutBits(unsigned long dwBuff, unsigned int nPutBits);
|
TOutputStream(void * pvOutBuffer, size_t cbOutLength);
|
||||||
|
void PutBits(unsigned int dwValue, unsigned int nBitCount);
|
||||||
unsigned char * pbOutBuffer; // 00 : Output buffer
|
void Flush();
|
||||||
unsigned long cbOutSize; // 04 : Size of output buffer
|
|
||||||
unsigned char * pbOutPos; // 08 : Current output position
|
unsigned char * pbOutBufferEnd; // End position in the output buffer
|
||||||
unsigned long dwBitBuff; // 0C : Bit buffer
|
unsigned char * pbOutBuffer; // Current position in the output buffer
|
||||||
unsigned long nBits; // 10 : Number of bits in the bit buffer
|
unsigned int BitBuffer; // Bit buffer
|
||||||
|
unsigned int BitCount; // Number of bits in the bit buffer
|
||||||
};
|
};
|
||||||
|
|
||||||
// Huffmann tree item (?)
|
// A virtual tree item that represents the head of the item list
|
||||||
|
#define LIST_HEAD() ((THTreeItem *)(&pFirst))
|
||||||
|
|
||||||
|
enum TInsertPoint
|
||||||
|
{
|
||||||
|
InsertAfter = 1,
|
||||||
|
InsertBefore = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
// Huffmann tree item
|
||||||
struct THTreeItem
|
struct THTreeItem
|
||||||
{
|
{
|
||||||
THTreeItem * Call1501DB70(THTreeItem * pLast);
|
THTreeItem() { pPrev = pNext = NULL;}
|
||||||
THTreeItem * GetPrevItem(LONG_PTR value);
|
// ~THTreeItem() { RemoveItem(); }
|
||||||
void ClearItemLinks();
|
|
||||||
void RemoveItem();
|
|
||||||
|
|
||||||
THTreeItem * next; // 00 - Pointer to next THTreeItem
|
void RemoveItem();
|
||||||
THTreeItem * prev; // 04 - Pointer to prev THTreeItem (< 0 if none)
|
// void RemoveEntry();
|
||||||
unsigned long dcmpByte; // 08 - Index of this item in item pointer array, decompressed byte value
|
|
||||||
unsigned long byteValue; // 0C - Some byte value
|
THTreeItem * pNext; // Pointer to lower-weight tree item
|
||||||
THTreeItem * parent; // 10 - Pointer to parent THTreeItem (NULL if none)
|
THTreeItem * pPrev; // Pointer to higher-weight item
|
||||||
THTreeItem * child; // 14 - Pointer to child THTreeItem
|
unsigned int DecompressedValue; // 08 - Decompressed byte value (also index in the array)
|
||||||
int addressMultiplier; // -1 if object on negative address (>0x80000000), +1 if positive
|
unsigned int Weight; // 0C - Weight
|
||||||
|
THTreeItem * pParent; // 10 - Pointer to parent item (NULL if none)
|
||||||
|
THTreeItem * pChildLo; // 14 - Pointer to the child with lower-weight child ("left child")
|
||||||
};
|
};
|
||||||
|
|
||||||
// Structure used for quick decompress. The 'bitCount' contains number of bits
|
|
||||||
// and byte value contains result decompressed byte value.
|
// Structure used for quick navigating in the huffmann tree.
|
||||||
// After each walk through Huffman tree are filled all entries which are
|
// Allows skipping up to 7 bits in the compressed stream, thus
|
||||||
// multiplies of number of bits loaded from input stream. These entries
|
// decompressing a bit faster. Sometimes it can even get the decompressed
|
||||||
// contain number of bits and result value. At the next 7 bits is tested this
|
// byte directly.
|
||||||
// structure first. If corresponding entry found, decompression routine will
|
struct TQuickLink
|
||||||
// not walk through Huffman tree and directly stores output byte to output stream.
|
{
|
||||||
struct TQDecompress
|
unsigned int ValidValue; // If greater than THuffmannTree::MinValidValue, the entry is valid
|
||||||
{
|
unsigned int ValidBits; // Number of bits that are valid for this item link
|
||||||
unsigned long offs00; // 00 - 1 if resolved
|
|
||||||
unsigned long nBits; // 04 - Bit count
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
unsigned long dcmpByte; // 08 - Byte value for decompress (if bitCount <= 7)
|
THTreeItem * pItem; // Pointer to the item within the Huffmann tree
|
||||||
THTreeItem * pItem; // 08 - THTreeItem (if number of bits is greater than 7
|
unsigned int DecompressedValue; // Value for direct decompression
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Structure for Huffman tree (Size 0x3674 bytes). Because I'm not expert
|
// Structure for Huffman tree (Size 0x3674 bytes). Because I'm not expert
|
||||||
// for the decompression, I do not know actually if the class is really a Hufmann
|
// for the decompression, I do not know actually if the class is really a Hufmann
|
||||||
|
|
@ -103,40 +104,40 @@ struct TQDecompress
|
||||||
class THuffmannTree
|
class THuffmannTree
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
THuffmannTree(bool bCompression);
|
||||||
|
~THuffmannTree();
|
||||||
|
|
||||||
THuffmannTree();
|
void LinkTwoItems(THTreeItem * pItem1, THTreeItem * pItem2);
|
||||||
void InitTree(bool bCompression);
|
void InsertItem(THTreeItem * item, TInsertPoint InsertPoint, THTreeItem * item2);
|
||||||
void BuildTree(unsigned int nCmpType);
|
|
||||||
// void ModifyTree(unsigned long dwIndex);
|
|
||||||
// void UninitTree();
|
|
||||||
|
|
||||||
// void Call15007010(Bit32 dwInLength, THTreeItem * item);
|
THTreeItem * FindHigherOrEqualItem(THTreeItem * pItem, unsigned int Weight);
|
||||||
THTreeItem * Call1500E740(unsigned int nValue);
|
THTreeItem * CreateNewItem(unsigned int DecompressedValue, unsigned int Weight, TInsertPoint InsertPoint);
|
||||||
void Call1500E820(THTreeItem * pItem);
|
|
||||||
unsigned int DoCompression(TOutputStream * os, unsigned char * pbInBuffer, int nInLength, int nCmpType);
|
|
||||||
unsigned int DoDecompression(unsigned char * pbOutBuffer, unsigned int dwOutLength, TInputStream * is);
|
|
||||||
|
|
||||||
unsigned long bIsCmp0; // 0000 - 1 if compression type 0
|
unsigned int FixupItemPosByWeight(THTreeItem * pItem, unsigned int MaxWeight);
|
||||||
unsigned long offs0004; // 0004 - Some flag
|
void BuildTree(unsigned int CompressionType);
|
||||||
THTreeItem items0008[0x203]; // 0008 - HTree items
|
|
||||||
|
|
||||||
//- Sometimes used as HTree item -----------
|
void IncWeightsAndRebalance(THTreeItem * pItem);
|
||||||
THTreeItem * pItem3050; // 3050 - Always NULL (?)
|
void InsertNewBranchAndRebalance(unsigned int Value1, unsigned int Value2);
|
||||||
THTreeItem * pItem3054; // 3054 - Pointer to Huffman tree item
|
|
||||||
THTreeItem * pItem3058; // 3058 - Pointer to Huffman tree item (< 0 if invalid)
|
|
||||||
|
|
||||||
//- Sometimes used as HTree item -----------
|
void EncodeOneByte(TOutputStream * os, THTreeItem * pItem);
|
||||||
THTreeItem * pItem305C; // 305C - Usually NULL
|
unsigned int DecodeOneByte(TInputStream * is);
|
||||||
THTreeItem * pFirst; // 3060 - Pointer to top (first) Huffman tree item
|
|
||||||
THTreeItem * pLast; // 3064 - Pointer to bottom (last) Huffman tree item (< 0 if invalid)
|
|
||||||
unsigned long nItems; // 3068 - Number of used HTree items
|
|
||||||
|
|
||||||
//-------------------------------------------
|
unsigned int Compress(TOutputStream * os, void * pvInBuffer, int cbInBuffer, int nCmpType);
|
||||||
THTreeItem * items306C[0x102]; // 306C - THTreeItem pointer array
|
unsigned int Decompress(void * pvOutBuffer, unsigned int cbOutLength, TInputStream * is);
|
||||||
TQDecompress qd3474[0x80]; // 3474 - Array for quick decompression
|
|
||||||
int addressMultiplier; // -1 if object on negative address (>0x80000000), +1 if positive
|
THTreeItem ItemBuffer[HUFF_ITEM_COUNT]; // Buffer for tree items. No memory allocation is needed
|
||||||
|
unsigned int ItemsUsed; // Number of tree items used from ItemBuffer
|
||||||
|
|
||||||
|
// Head of the linear item list
|
||||||
|
THTreeItem * pFirst; // Pointer to the highest weight item
|
||||||
|
THTreeItem * pLast; // Pointer to the lowest weight item
|
||||||
|
|
||||||
static unsigned char Table1502A630[];// Some table
|
THTreeItem * ItemsByByte[0x102]; // Array of item pointers, one for each possible byte value
|
||||||
|
TQuickLink QuickLinks[LINK_ITEM_COUNT]; // Array of quick-link items
|
||||||
|
|
||||||
|
unsigned int MinValidValue; // A minimum value of TQDecompress::ValidValue to be considered valid
|
||||||
|
unsigned int bIsCmp0; // 1 if compression type 0
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __HUFFMAN_H__
|
#endif // __HUFFMAN_H__
|
||||||
|
|
|
||||||
|
|
@ -292,7 +292,7 @@ uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
|
||||||
u.ptr = key;
|
u.ptr = key;
|
||||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||||
const uint8_t *k8;
|
//const uint8_t *k8;
|
||||||
|
|
||||||
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||||
while (length > 12)
|
while (length > 12)
|
||||||
|
|
@ -336,7 +336,7 @@ uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
|
||||||
|
|
||||||
#else /* make valgrind happy */
|
#else /* make valgrind happy */
|
||||||
|
|
||||||
k8 = (const uint8_t *)k;
|
const uint8_t *k8 = (const uint8_t *)k;
|
||||||
switch(length)
|
switch(length)
|
||||||
{
|
{
|
||||||
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||||
|
|
@ -358,7 +358,7 @@ uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
|
||||||
|
|
||||||
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||||
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||||
const uint8_t *k8;
|
//const uint8_t *k8;
|
||||||
|
|
||||||
/*--------------- all but last block: aligned reads and different mixing */
|
/*--------------- all but last block: aligned reads and different mixing */
|
||||||
while (length > 12)
|
while (length > 12)
|
||||||
|
|
@ -372,7 +372,7 @@ uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*----------------------------- handle the last (probably partial) block */
|
/*----------------------------- handle the last (probably partial) block */
|
||||||
k8 = (const uint8_t *)k;
|
const uint8_t *k8 = (const uint8_t *)k;
|
||||||
switch(length)
|
switch(length)
|
||||||
{
|
{
|
||||||
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
|
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
|
||||||
|
|
@ -477,7 +477,7 @@ void hashlittle2(
|
||||||
u.ptr = key;
|
u.ptr = key;
|
||||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||||
const uint8_t *k8;
|
//const uint8_t *k8;
|
||||||
|
|
||||||
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||||
while (length > 12)
|
while (length > 12)
|
||||||
|
|
@ -521,7 +521,7 @@ void hashlittle2(
|
||||||
|
|
||||||
#else /* make valgrind happy */
|
#else /* make valgrind happy */
|
||||||
|
|
||||||
k8 = (const uint8_t *)k;
|
const uint8_t *k8 = (const uint8_t *)k;
|
||||||
switch(length)
|
switch(length)
|
||||||
{
|
{
|
||||||
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||||
|
|
@ -654,7 +654,7 @@ uint32_t hashbig( const void *key, size_t length, uint32_t initval)
|
||||||
u.ptr = key;
|
u.ptr = key;
|
||||||
if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
|
if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||||
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||||
const uint8_t *k8;
|
//const uint8_t *k8;
|
||||||
|
|
||||||
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||||
while (length > 12)
|
while (length > 12)
|
||||||
|
|
@ -698,7 +698,7 @@ uint32_t hashbig( const void *key, size_t length, uint32_t initval)
|
||||||
|
|
||||||
#else /* make valgrind happy */
|
#else /* make valgrind happy */
|
||||||
|
|
||||||
k8 = (const uint8_t *)k;
|
const uint8_t *k8 = (const uint8_t *)k;
|
||||||
switch(length) /* all the case statements fall through */
|
switch(length) /* all the case statements fall through */
|
||||||
{
|
{
|
||||||
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@
|
||||||
*/
|
*/
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file hash_memory.c
|
@file hash_memory.c
|
||||||
Hash memory helper, Tom St Denis
|
Hash memory helper, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Hash a block of memory and store the digest.
|
Hash a block of memory and store the digest.
|
||||||
@param hash The index of the hash you wish to use
|
@param hash The index of the hash you wish to use
|
||||||
@param in The data you wish to hash
|
@param in The data you wish to hash
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file md5.c
|
@file md5.c
|
||||||
LTC_MD5 hash function by Tom St Denis
|
LTC_MD5 hash function by Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
@ -225,7 +225,7 @@ static int md5_compress(hash_state *md, unsigned char *buf)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Initialize the hash state
|
Initialize the hash state
|
||||||
@param md The hash state you wish to initialize
|
@param md The hash state you wish to initialize
|
||||||
@return CRYPT_OK if successful
|
@return CRYPT_OK if successful
|
||||||
|
|
@ -242,7 +242,7 @@ int md5_init(hash_state * md)
|
||||||
return CRYPT_OK;
|
return CRYPT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Process a block of memory though the hash
|
Process a block of memory though the hash
|
||||||
@param md The hash state
|
@param md The hash state
|
||||||
@param in The data to hash
|
@param in The data to hash
|
||||||
|
|
@ -251,7 +251,7 @@ int md5_init(hash_state * md)
|
||||||
*/
|
*/
|
||||||
HASH_PROCESS(md5_process, md5_compress, md5, 64)
|
HASH_PROCESS(md5_process, md5_compress, md5, 64)
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Terminate the hash to get the digest
|
Terminate the hash to get the digest
|
||||||
@param md The hash state
|
@param md The hash state
|
||||||
@param out [out] The destination of the hash (16 bytes)
|
@param out [out] The destination of the hash (16 bytes)
|
||||||
|
|
@ -306,7 +306,7 @@ int md5_done(hash_state * md, unsigned char *out)
|
||||||
return CRYPT_OK;
|
return CRYPT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Self-test the hash
|
Self-test the hash
|
||||||
@return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
|
@return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file sha1.c
|
@file sha1.c
|
||||||
LTC_SHA1 code by Tom St Denis
|
LTC_SHA1 code by Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
@ -157,7 +157,7 @@ static int sha1_compress(hash_state *md, unsigned char *buf)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Initialize the hash state
|
Initialize the hash state
|
||||||
@param md The hash state you wish to initialize
|
@param md The hash state you wish to initialize
|
||||||
@return CRYPT_OK if successful
|
@return CRYPT_OK if successful
|
||||||
|
|
@ -175,7 +175,7 @@ int sha1_init(hash_state * md)
|
||||||
return CRYPT_OK;
|
return CRYPT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Process a block of memory though the hash
|
Process a block of memory though the hash
|
||||||
@param md The hash state
|
@param md The hash state
|
||||||
@param in The data to hash
|
@param in The data to hash
|
||||||
|
|
@ -184,7 +184,7 @@ int sha1_init(hash_state * md)
|
||||||
*/
|
*/
|
||||||
HASH_PROCESS(sha1_process, sha1_compress, sha1, 64)
|
HASH_PROCESS(sha1_process, sha1_compress, sha1, 64)
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Terminate the hash to get the digest
|
Terminate the hash to get the digest
|
||||||
@param md The hash state
|
@param md The hash state
|
||||||
@param out [out] The destination of the hash (20 bytes)
|
@param out [out] The destination of the hash (20 bytes)
|
||||||
|
|
@ -238,7 +238,7 @@ int sha1_done(hash_state * md, unsigned char *out)
|
||||||
return CRYPT_OK;
|
return CRYPT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Self-test the hash
|
Self-test the hash
|
||||||
@return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
|
@return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ static const struct {
|
||||||
{ MP_VAL , CRYPT_INVALID_ARG},
|
{ MP_VAL , CRYPT_INVALID_ARG},
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Convert a MPI error to a LTC error (Possibly the most powerful function ever! Oh wait... no)
|
Convert a MPI error to a LTC error (Possibly the most powerful function ever! Oh wait... no)
|
||||||
@param err The error to convert
|
@param err The error to convert
|
||||||
@return The equivalent LTC error code or CRYPT_ERROR if none found
|
@return The equivalent LTC error code or CRYPT_ERROR if none found
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file rand_prime.c
|
@file rand_prime.c
|
||||||
Generate a random prime, Tom St Denis
|
Generate a random prime, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file base64_decode.c
|
@file base64_decode.c
|
||||||
Compliant base64 code donated by Wayne Scott (wscott@bitmover.com)
|
Compliant base64 code donated by Wayne Scott (wscott@bitmover.com)
|
||||||
*/
|
*/
|
||||||
|
|
@ -42,7 +42,7 @@ static const unsigned char map[256] = {
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
255, 255, 255, 255 };
|
255, 255, 255, 255 };
|
||||||
|
|
||||||
/*
|
/**
|
||||||
base64 decode a block of memory
|
base64 decode a block of memory
|
||||||
@param in The base64 data to decode
|
@param in The base64 data to decode
|
||||||
@param inlen The length of the base64 data
|
@param inlen The length of the base64 data
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file crypt_argchk.c
|
@file crypt_argchk.c
|
||||||
Perform argument checking, Tom St Denis
|
Perform argument checking, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@
|
||||||
*/
|
*/
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file crypt_find_hash.c
|
@file crypt_find_hash.c
|
||||||
Find a hash, Tom St Denis
|
Find a hash, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Find a registered hash by name
|
Find a registered hash by name
|
||||||
@param name The name of the hash to look for
|
@param name The name of the hash to look for
|
||||||
@return >= 0 if found, -1 if not present
|
@return >= 0 if found, -1 if not present
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@
|
||||||
*/
|
*/
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file crypt_find_prng.c
|
@file crypt_find_prng.c
|
||||||
Find a PRNG, Tom St Denis
|
Find a PRNG, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Find a registered PRNG by name
|
Find a registered PRNG by name
|
||||||
@param name The name of the PRNG to look for
|
@param name The name of the PRNG to look for
|
||||||
@return >= 0 if found, -1 if not present
|
@return >= 0 if found, -1 if not present
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file crypt_hash_descriptor.c
|
@file crypt_hash_descriptor.c
|
||||||
Stores the hash descriptor table, Tom St Denis
|
Stores the hash descriptor table, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file crypt_hash_is_valid.c
|
@file crypt_hash_is_valid.c
|
||||||
Determine if hash is valid, Tom St Denis
|
Determine if hash is valid, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file crypt_prng_descriptor.c
|
@file crypt_prng_descriptor.c
|
||||||
Stores the PRNG descriptors, Tom St Denis
|
Stores the PRNG descriptors, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file crypt_prng_is_valid.c
|
@file crypt_prng_is_valid.c
|
||||||
Determine if PRNG is valid, Tom St Denis
|
Determine if PRNG is valid, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@
|
||||||
*/
|
*/
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file crypt_register_hash.c
|
@file crypt_register_hash.c
|
||||||
Register a HASH, Tom St Denis
|
Register a HASH, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Register a hash with the descriptor table
|
Register a hash with the descriptor table
|
||||||
@param hash The hash you wish to register
|
@param hash The hash you wish to register
|
||||||
@return value >= 0 if successfully added (or already present), -1 if unsuccessful
|
@return value >= 0 if successfully added (or already present), -1 if unsuccessful
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@
|
||||||
*/
|
*/
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file crypt_register_prng.c
|
@file crypt_register_prng.c
|
||||||
Register a PRNG, Tom St Denis
|
Register a PRNG, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Register a PRNG with the descriptor table
|
Register a PRNG with the descriptor table
|
||||||
@param prng The PRNG you wish to register
|
@param prng The PRNG you wish to register
|
||||||
@return value >= 0 if successfully added (or already present), -1 if unsuccessful
|
@return value >= 0 if successfully added (or already present), -1 if unsuccessful
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@
|
||||||
*/
|
*/
|
||||||
#include "../headers/tomcrypt.h"
|
#include "../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file zeromem.c
|
@file zeromem.c
|
||||||
Zero a block of memory, Tom St Denis
|
Zero a block of memory, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Zero a block of memory
|
Zero a block of memory
|
||||||
@param out The destination of the area to zero
|
@param out The destination of the area to zero
|
||||||
@param outlen The length of the area to zero (octets)
|
@param outlen The length of the area to zero (octets)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../../headers/tomcrypt.h"
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_bit_string.c
|
@file der_decode_bit_string.c
|
||||||
ASN.1 DER, encode a BIT STRING, Tom St Denis
|
ASN.1 DER, encode a BIT STRING, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
#ifdef LTC_DER
|
#ifdef LTC_DER
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Store a BIT STRING
|
Store a BIT STRING
|
||||||
@param in The DER encoded BIT STRING
|
@param in The DER encoded BIT STRING
|
||||||
@param inlen The size of the DER BIT STRING
|
@param inlen The size of the DER BIT STRING
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../../headers/tomcrypt.h"
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_boolean.c
|
@file der_decode_boolean.c
|
||||||
ASN.1 DER, decode a BOOLEAN, Tom St Denis
|
ASN.1 DER, decode a BOOLEAN, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
#ifdef LTC_DER
|
#ifdef LTC_DER
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Read a BOOLEAN
|
Read a BOOLEAN
|
||||||
@param in The destination for the DER encoded BOOLEAN
|
@param in The destination for the DER encoded BOOLEAN
|
||||||
@param inlen The size of the DER BOOLEAN
|
@param inlen The size of the DER BOOLEAN
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,14 @@
|
||||||
*/
|
*/
|
||||||
#include "../../headers/tomcrypt.h"
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_choice.c
|
@file der_decode_choice.c
|
||||||
ASN.1 DER, decode a CHOICE, Tom St Denis
|
ASN.1 DER, decode a CHOICE, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef LTC_DER
|
#ifdef LTC_DER
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Decode a CHOICE
|
Decode a CHOICE
|
||||||
@param in The DER encoded input
|
@param in The DER encoded input
|
||||||
@param inlen [in/out] The size of the input and resulting size of read type
|
@param inlen [in/out] The size of the input and resulting size of read type
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../../headers/tomcrypt.h"
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_ia5_string.c
|
@file der_decode_ia5_string.c
|
||||||
ASN.1 DER, encode a IA5 STRING, Tom St Denis
|
ASN.1 DER, encode a IA5 STRING, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
#ifdef LTC_DER
|
#ifdef LTC_DER
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Store a IA5 STRING
|
Store a IA5 STRING
|
||||||
@param in The DER encoded IA5 STRING
|
@param in The DER encoded IA5 STRING
|
||||||
@param inlen The size of the DER IA5 STRING
|
@param inlen The size of the DER IA5 STRING
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../../headers/tomcrypt.h"
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_integer.c
|
@file der_decode_integer.c
|
||||||
ASN.1 DER, decode an integer, Tom St Denis
|
ASN.1 DER, decode an integer, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
#ifdef LTC_DER
|
#ifdef LTC_DER
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Read a mp_int integer
|
Read a mp_int integer
|
||||||
@param in The DER encoded data
|
@param in The DER encoded data
|
||||||
@param inlen Size of DER encoded data
|
@param inlen Size of DER encoded data
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@
|
||||||
*/
|
*/
|
||||||
#include "../../headers/tomcrypt.h"
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_object_identifier.c
|
@file der_decode_object_identifier.c
|
||||||
ASN.1 DER, Decode Object Identifier, Tom St Denis
|
ASN.1 DER, Decode Object Identifier, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef LTC_DER
|
#ifdef LTC_DER
|
||||||
/*
|
/**
|
||||||
Decode OID data and store the array of integers in words
|
Decode OID data and store the array of integers in words
|
||||||
@param in The OID DER encoded data
|
@param in The OID DER encoded data
|
||||||
@param inlen The length of the OID data
|
@param inlen The length of the OID data
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../../headers/tomcrypt.h"
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_octet_string.c
|
@file der_decode_octet_string.c
|
||||||
ASN.1 DER, encode a OCTET STRING, Tom St Denis
|
ASN.1 DER, encode a OCTET STRING, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
#ifdef LTC_DER
|
#ifdef LTC_DER
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Store a OCTET STRING
|
Store a OCTET STRING
|
||||||
@param in The DER encoded OCTET STRING
|
@param in The DER encoded OCTET STRING
|
||||||
@param inlen The size of the DER OCTET STRING
|
@param inlen The size of the DER OCTET STRING
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../../headers/tomcrypt.h"
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_printable_string.c
|
@file der_decode_printable_string.c
|
||||||
ASN.1 DER, encode a printable STRING, Tom St Denis
|
ASN.1 DER, encode a printable STRING, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
#ifdef LTC_DER
|
#ifdef LTC_DER
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Store a printable STRING
|
Store a printable STRING
|
||||||
@param in The DER encoded printable STRING
|
@param in The DER encoded printable STRING
|
||||||
@param inlen The size of the DER printable STRING
|
@param inlen The size of the DER printable STRING
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,14 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_sequence_ex.c
|
@file der_decode_sequence_ex.c
|
||||||
ASN.1 DER, decode a SEQUENCE, Tom St Denis
|
ASN.1 DER, decode a SEQUENCE, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef LTC_DER
|
#ifdef LTC_DER
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Decode a SEQUENCE
|
Decode a SEQUENCE
|
||||||
@param in The DER encoded input
|
@param in The DER encoded input
|
||||||
@param inlen The size of the input
|
@param inlen The size of the input
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../../headers/tomcrypt.h"
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_sequence_flexi.c
|
@file der_decode_sequence_flexi.c
|
||||||
ASN.1 DER, decode an array of ASN.1 types with a flexi parser, Tom St Denis
|
ASN.1 DER, decode an array of ASN.1 types with a flexi parser, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,14 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_sequence_multi.c
|
@file der_decode_sequence_multi.c
|
||||||
ASN.1 DER, decode a SEQUENCE, Tom St Denis
|
ASN.1 DER, decode a SEQUENCE, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef LTC_DER
|
#ifdef LTC_DER
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Decode a SEQUENCE type using a VA list
|
Decode a SEQUENCE type using a VA list
|
||||||
@param in Input buffer
|
@param in Input buffer
|
||||||
@param inlen Length of input in octets
|
@param inlen Length of input in octets
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../../headers/tomcrypt.h"
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_short_integer.c
|
@file der_decode_short_integer.c
|
||||||
ASN.1 DER, decode an integer, Tom St Denis
|
ASN.1 DER, decode an integer, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
#ifdef LTC_DER
|
#ifdef LTC_DER
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Read a short integer
|
Read a short integer
|
||||||
@param in The DER encoded data
|
@param in The DER encoded data
|
||||||
@param inlen Size of data
|
@param inlen Size of data
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../../headers/tomcrypt.h"
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_utctime.c
|
@file der_decode_utctime.c
|
||||||
ASN.1 DER, decode a UTCTIME, Tom St Denis
|
ASN.1 DER, decode a UTCTIME, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
@ -39,7 +39,7 @@ static int char_to_int(unsigned char x)
|
||||||
if (y >= max) return CRYPT_INVALID_PACKET; \
|
if (y >= max) return CRYPT_INVALID_PACKET; \
|
||||||
x += 2;
|
x += 2;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Decodes a UTC time structure in DER format (reads all 6 valid encoding formats)
|
Decodes a UTC time structure in DER format (reads all 6 valid encoding formats)
|
||||||
@param in Input buffer
|
@param in Input buffer
|
||||||
@param inlen Length of input buffer in octets
|
@param inlen Length of input buffer in octets
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include "../../headers/tomcrypt.h"
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
/*
|
/**
|
||||||
@file der_decode_utf8_string.c
|
@file der_decode_utf8_string.c
|
||||||
ASN.1 DER, encode a UTF8 STRING, Tom St Denis
|
ASN.1 DER, encode a UTF8 STRING, Tom St Denis
|
||||||
*/
|
*/
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
#ifdef LTC_DER
|
#ifdef LTC_DER
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Store a UTF8 STRING
|
Store a UTF8 STRING
|
||||||
@param in The DER encoded UTF8 STRING
|
@param in The DER encoded UTF8 STRING
|
||||||
@param inlen The size of the DER UTF8 STRING
|
@param inlen The size of the DER UTF8 STRING
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
|
||||||
|
*
|
||||||
|
* LibTomCrypt is a library that provides various cryptographic
|
||||||
|
* algorithms in a highly modular and flexible manner.
|
||||||
|
*
|
||||||
|
* The library is free for all purposes without any express
|
||||||
|
* guarantee it works.
|
||||||
|
*
|
||||||
|
* Tom St Denis, tomstdenis@gmail.com, http://libtom.org
|
||||||
|
*/
|
||||||
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
@file der_encode_bit_string.c
|
||||||
|
ASN.1 DER, encode a BIT STRING, Tom St Denis
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LTC_DER
|
||||||
|
|
||||||
|
/**
|
||||||
|
Store a BIT STRING
|
||||||
|
@param in The array of bits to store (one per char)
|
||||||
|
@param inlen The number of bits tostore
|
||||||
|
@param out [out] The destination for the DER encoded BIT STRING
|
||||||
|
@param outlen [in/out] The max size and resulting size of the DER BIT STRING
|
||||||
|
@return CRYPT_OK if successful
|
||||||
|
*/
|
||||||
|
int der_encode_bit_string(const unsigned char *in, unsigned long inlen,
|
||||||
|
unsigned char *out, unsigned long *outlen)
|
||||||
|
{
|
||||||
|
unsigned long len, x, y;
|
||||||
|
unsigned char buf;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
LTC_ARGCHK(in != NULL);
|
||||||
|
LTC_ARGCHK(out != NULL);
|
||||||
|
LTC_ARGCHK(outlen != NULL);
|
||||||
|
|
||||||
|
/* avoid overflows */
|
||||||
|
if ((err = der_length_bit_string(inlen, &len)) != CRYPT_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > *outlen) {
|
||||||
|
*outlen = len;
|
||||||
|
return CRYPT_BUFFER_OVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store header (include bit padding count in length) */
|
||||||
|
x = 0;
|
||||||
|
y = (inlen >> 3) + ((inlen&7) ? 1 : 0) + 1;
|
||||||
|
|
||||||
|
out[x++] = 0x03;
|
||||||
|
if (y < 128) {
|
||||||
|
out[x++] = (unsigned char)y;
|
||||||
|
} else if (y < 256) {
|
||||||
|
out[x++] = 0x81;
|
||||||
|
out[x++] = (unsigned char)y;
|
||||||
|
} else if (y < 65536) {
|
||||||
|
out[x++] = 0x82;
|
||||||
|
out[x++] = (unsigned char)((y>>8)&255);
|
||||||
|
out[x++] = (unsigned char)(y&255);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store number of zero padding bits */
|
||||||
|
out[x++] = (unsigned char)((8 - inlen) & 7);
|
||||||
|
|
||||||
|
/* store the bits in big endian format */
|
||||||
|
for (y = buf = 0; y < inlen; y++) {
|
||||||
|
buf |= (in[y] ? 1 : 0) << (7 - (y & 7));
|
||||||
|
if ((y & 7) == 7) {
|
||||||
|
out[x++] = buf;
|
||||||
|
buf = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* store last byte */
|
||||||
|
if (inlen & 7) {
|
||||||
|
out[x++] = buf;
|
||||||
|
}
|
||||||
|
*outlen = x;
|
||||||
|
return CRYPT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_encode_bit_string.c,v $ */
|
||||||
|
/* $Revision: 1.5 $ */
|
||||||
|
/* $Date: 2006/12/28 01:27:24 $ */
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
|
||||||
|
*
|
||||||
|
* LibTomCrypt is a library that provides various cryptographic
|
||||||
|
* algorithms in a highly modular and flexible manner.
|
||||||
|
*
|
||||||
|
* The library is free for all purposes without any express
|
||||||
|
* guarantee it works.
|
||||||
|
*
|
||||||
|
* Tom St Denis, tomstdenis@gmail.com, http://libtom.org
|
||||||
|
*/
|
||||||
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
@file der_encode_boolean.c
|
||||||
|
ASN.1 DER, encode a BOOLEAN, Tom St Denis
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LTC_DER
|
||||||
|
|
||||||
|
/**
|
||||||
|
Store a BOOLEAN
|
||||||
|
@param in The boolean to encode
|
||||||
|
@param out [out] The destination for the DER encoded BOOLEAN
|
||||||
|
@param outlen [in/out] The max size and resulting size of the DER BOOLEAN
|
||||||
|
@return CRYPT_OK if successful
|
||||||
|
*/
|
||||||
|
int der_encode_boolean(int in,
|
||||||
|
unsigned char *out, unsigned long *outlen)
|
||||||
|
{
|
||||||
|
LTC_ARGCHK(outlen != NULL);
|
||||||
|
LTC_ARGCHK(out != NULL);
|
||||||
|
|
||||||
|
if (*outlen < 3) {
|
||||||
|
*outlen = 3;
|
||||||
|
return CRYPT_BUFFER_OVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
*outlen = 3;
|
||||||
|
out[0] = 0x01;
|
||||||
|
out[1] = 0x01;
|
||||||
|
out[2] = in ? 0xFF : 0x00;
|
||||||
|
|
||||||
|
return CRYPT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_encode_boolean.c,v $ */
|
||||||
|
/* $Revision: 1.4 $ */
|
||||||
|
/* $Date: 2006/12/28 01:27:24 $ */
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
|
||||||
|
*
|
||||||
|
* LibTomCrypt is a library that provides various cryptographic
|
||||||
|
* algorithms in a highly modular and flexible manner.
|
||||||
|
*
|
||||||
|
* The library is free for all purposes without any express
|
||||||
|
* guarantee it works.
|
||||||
|
*
|
||||||
|
* Tom St Denis, tomstdenis@gmail.com, http://libtom.org
|
||||||
|
*/
|
||||||
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
@file der_encode_ia5_string.c
|
||||||
|
ASN.1 DER, encode a IA5 STRING, Tom St Denis
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef LTC_DER
|
||||||
|
|
||||||
|
/**
|
||||||
|
Store an IA5 STRING
|
||||||
|
@param in The array of IA5 to store (one per char)
|
||||||
|
@param inlen The number of IA5 to store
|
||||||
|
@param out [out] The destination for the DER encoded IA5 STRING
|
||||||
|
@param outlen [in/out] The max size and resulting size of the DER IA5 STRING
|
||||||
|
@return CRYPT_OK if successful
|
||||||
|
*/
|
||||||
|
int der_encode_ia5_string(const unsigned char *in, unsigned long inlen,
|
||||||
|
unsigned char *out, unsigned long *outlen)
|
||||||
|
{
|
||||||
|
unsigned long x, y, len;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
LTC_ARGCHK(in != NULL);
|
||||||
|
LTC_ARGCHK(out != NULL);
|
||||||
|
LTC_ARGCHK(outlen != NULL);
|
||||||
|
|
||||||
|
/* get the size */
|
||||||
|
if ((err = der_length_ia5_string(in, inlen, &len)) != CRYPT_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* too big? */
|
||||||
|
if (len > *outlen) {
|
||||||
|
*outlen = len;
|
||||||
|
return CRYPT_BUFFER_OVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* encode the header+len */
|
||||||
|
x = 0;
|
||||||
|
out[x++] = 0x16;
|
||||||
|
if (inlen < 128) {
|
||||||
|
out[x++] = (unsigned char)inlen;
|
||||||
|
} else if (inlen < 256) {
|
||||||
|
out[x++] = 0x81;
|
||||||
|
out[x++] = (unsigned char)inlen;
|
||||||
|
} else if (inlen < 65536UL) {
|
||||||
|
out[x++] = 0x82;
|
||||||
|
out[x++] = (unsigned char)((inlen>>8)&255);
|
||||||
|
out[x++] = (unsigned char)(inlen&255);
|
||||||
|
} else if (inlen < 16777216UL) {
|
||||||
|
out[x++] = 0x83;
|
||||||
|
out[x++] = (unsigned char)((inlen>>16)&255);
|
||||||
|
out[x++] = (unsigned char)((inlen>>8)&255);
|
||||||
|
out[x++] = (unsigned char)(inlen&255);
|
||||||
|
} else {
|
||||||
|
return CRYPT_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store octets */
|
||||||
|
for (y = 0; y < inlen; y++) {
|
||||||
|
out[x++] = der_ia5_char_encode(in[y]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* retun length */
|
||||||
|
*outlen = x;
|
||||||
|
|
||||||
|
return CRYPT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_encode_ia5_string.c,v $ */
|
||||||
|
/* $Revision: 1.5 $ */
|
||||||
|
/* $Date: 2006/12/28 01:27:24 $ */
|
||||||
130
dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_integer.c
Normal file
130
dep/StormLib/src/libtomcrypt/src/pk/asn1/der_encode_integer.c
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
|
||||||
|
*
|
||||||
|
* LibTomCrypt is a library that provides various cryptographic
|
||||||
|
* algorithms in a highly modular and flexible manner.
|
||||||
|
*
|
||||||
|
* The library is free for all purposes without any express
|
||||||
|
* guarantee it works.
|
||||||
|
*
|
||||||
|
* Tom St Denis, tomstdenis@gmail.com, http://libtom.org
|
||||||
|
*/
|
||||||
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
@file der_encode_integer.c
|
||||||
|
ASN.1 DER, encode an integer, Tom St Denis
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LTC_DER
|
||||||
|
|
||||||
|
/* Exports a positive bignum as DER format (upto 2^32 bytes in size) */
|
||||||
|
/**
|
||||||
|
Store a mp_int integer
|
||||||
|
@param num The first mp_int to encode
|
||||||
|
@param out [out] The destination for the DER encoded integers
|
||||||
|
@param outlen [in/out] The max size and resulting size of the DER encoded integers
|
||||||
|
@return CRYPT_OK if successful
|
||||||
|
*/
|
||||||
|
int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen)
|
||||||
|
{
|
||||||
|
unsigned long tmplen, y;
|
||||||
|
int err, leading_zero;
|
||||||
|
|
||||||
|
LTC_ARGCHK(num != NULL);
|
||||||
|
LTC_ARGCHK(out != NULL);
|
||||||
|
LTC_ARGCHK(outlen != NULL);
|
||||||
|
|
||||||
|
/* find out how big this will be */
|
||||||
|
if ((err = der_length_integer(num, &tmplen)) != CRYPT_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*outlen < tmplen) {
|
||||||
|
*outlen = tmplen;
|
||||||
|
return CRYPT_BUFFER_OVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mp_cmp_d(num, 0) != LTC_MP_LT) {
|
||||||
|
/* we only need a leading zero if the msb of the first byte is one */
|
||||||
|
if ((mp_count_bits(num) & 7) == 0 || mp_iszero(num) == LTC_MP_YES) {
|
||||||
|
leading_zero = 1;
|
||||||
|
} else {
|
||||||
|
leading_zero = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get length of num in bytes (plus 1 since we force the msbyte to zero) */
|
||||||
|
y = mp_unsigned_bin_size(num) + leading_zero;
|
||||||
|
} else {
|
||||||
|
leading_zero = 0;
|
||||||
|
y = mp_count_bits(num);
|
||||||
|
y = y + (8 - (y & 7));
|
||||||
|
y = y >> 3;
|
||||||
|
if (((mp_cnt_lsb(num)+1)==mp_count_bits(num)) && ((mp_count_bits(num)&7)==0)) --y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now store initial data */
|
||||||
|
*out++ = 0x02;
|
||||||
|
if (y < 128) {
|
||||||
|
/* short form */
|
||||||
|
*out++ = (unsigned char)y;
|
||||||
|
} else if (y < 256) {
|
||||||
|
*out++ = 0x81;
|
||||||
|
*out++ = (unsigned char)y;
|
||||||
|
} else if (y < 65536UL) {
|
||||||
|
*out++ = 0x82;
|
||||||
|
*out++ = (unsigned char)((y>>8)&255);
|
||||||
|
*out++ = (unsigned char)y;
|
||||||
|
} else if (y < 16777216UL) {
|
||||||
|
*out++ = 0x83;
|
||||||
|
*out++ = (unsigned char)((y>>16)&255);
|
||||||
|
*out++ = (unsigned char)((y>>8)&255);
|
||||||
|
*out++ = (unsigned char)y;
|
||||||
|
} else {
|
||||||
|
return CRYPT_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now store msbyte of zero if num is non-zero */
|
||||||
|
if (leading_zero) {
|
||||||
|
*out++ = 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if it's not zero store it as big endian */
|
||||||
|
if (mp_cmp_d(num, 0) == LTC_MP_GT) {
|
||||||
|
/* now store the mpint */
|
||||||
|
if ((err = mp_to_unsigned_bin(num, out)) != CRYPT_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
} else if (mp_iszero(num) != LTC_MP_YES) {
|
||||||
|
void *tmp;
|
||||||
|
|
||||||
|
/* negative */
|
||||||
|
if (mp_init(&tmp) != CRYPT_OK) {
|
||||||
|
return CRYPT_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2^roundup and subtract */
|
||||||
|
y = mp_count_bits(num);
|
||||||
|
y = y + (8 - (y & 7));
|
||||||
|
if (((mp_cnt_lsb(num)+1)==mp_count_bits(num)) && ((mp_count_bits(num)&7)==0)) y -= 8;
|
||||||
|
if (mp_2expt(tmp, y) != CRYPT_OK || mp_add(tmp, num, tmp) != CRYPT_OK) {
|
||||||
|
mp_clear(tmp);
|
||||||
|
return CRYPT_MEM;
|
||||||
|
}
|
||||||
|
if ((err = mp_to_unsigned_bin(tmp, out)) != CRYPT_OK) {
|
||||||
|
mp_clear(tmp);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
mp_clear(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we good */
|
||||||
|
*outlen = tmplen;
|
||||||
|
return CRYPT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_encode_integer.c,v $ */
|
||||||
|
/* $Revision: 1.9 $ */
|
||||||
|
/* $Date: 2006/12/28 01:27:24 $ */
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
|
||||||
|
*
|
||||||
|
* LibTomCrypt is a library that provides various cryptographic
|
||||||
|
* algorithms in a highly modular and flexible manner.
|
||||||
|
*
|
||||||
|
* The library is free for all purposes without any express
|
||||||
|
* guarantee it works.
|
||||||
|
*
|
||||||
|
* Tom St Denis, tomstdenis@gmail.com, http://libtom.org
|
||||||
|
*/
|
||||||
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
@file der_encode_object_identifier.c
|
||||||
|
ASN.1 DER, Encode Object Identifier, Tom St Denis
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef LTC_DER
|
||||||
|
/**
|
||||||
|
Encode an OID
|
||||||
|
@param words The words to encode (upto 32-bits each)
|
||||||
|
@param nwords The number of words in the OID
|
||||||
|
@param out [out] Destination of OID data
|
||||||
|
@param outlen [in/out] The max and resulting size of the OID
|
||||||
|
@return CRYPT_OK if successful
|
||||||
|
*/
|
||||||
|
int der_encode_object_identifier(unsigned long *words, unsigned long nwords,
|
||||||
|
unsigned char *out, unsigned long *outlen)
|
||||||
|
{
|
||||||
|
unsigned long i, x, y, z, t, mask, wordbuf;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
LTC_ARGCHK(words != NULL);
|
||||||
|
LTC_ARGCHK(out != NULL);
|
||||||
|
LTC_ARGCHK(outlen != NULL);
|
||||||
|
|
||||||
|
/* check length */
|
||||||
|
if ((err = der_length_object_identifier(words, nwords, &x)) != CRYPT_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (x > *outlen) {
|
||||||
|
*outlen = x;
|
||||||
|
return CRYPT_BUFFER_OVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compute length to store OID data */
|
||||||
|
z = 0;
|
||||||
|
wordbuf = words[0] * 40 + words[1];
|
||||||
|
for (y = 1; y < nwords; y++) {
|
||||||
|
t = der_object_identifier_bits(wordbuf);
|
||||||
|
z += t/7 + ((t%7) ? 1 : 0) + (wordbuf == 0 ? 1 : 0);
|
||||||
|
if (y < nwords - 1) {
|
||||||
|
wordbuf = words[y + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store header + length */
|
||||||
|
x = 0;
|
||||||
|
out[x++] = 0x06;
|
||||||
|
if (z < 128) {
|
||||||
|
out[x++] = (unsigned char)z;
|
||||||
|
} else if (z < 256) {
|
||||||
|
out[x++] = 0x81;
|
||||||
|
out[x++] = (unsigned char)z;
|
||||||
|
} else if (z < 65536UL) {
|
||||||
|
out[x++] = 0x82;
|
||||||
|
out[x++] = (unsigned char)((z>>8)&255);
|
||||||
|
out[x++] = (unsigned char)(z&255);
|
||||||
|
} else {
|
||||||
|
return CRYPT_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store first byte */
|
||||||
|
wordbuf = words[0] * 40 + words[1];
|
||||||
|
for (i = 1; i < nwords; i++) {
|
||||||
|
/* store 7 bit words in little endian */
|
||||||
|
t = wordbuf & 0xFFFFFFFF;
|
||||||
|
if (t) {
|
||||||
|
y = x;
|
||||||
|
mask = 0;
|
||||||
|
while (t) {
|
||||||
|
out[x++] = (unsigned char)((t & 0x7F) | mask);
|
||||||
|
t >>= 7;
|
||||||
|
mask |= 0x80; /* upper bit is set on all but the last byte */
|
||||||
|
}
|
||||||
|
/* now swap bytes y...x-1 */
|
||||||
|
z = x - 1;
|
||||||
|
while (y < z) {
|
||||||
|
t = out[y]; out[y] = out[z]; out[z] = (unsigned char)t;
|
||||||
|
++y;
|
||||||
|
--z;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* zero word */
|
||||||
|
out[x++] = 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < nwords - 1) {
|
||||||
|
wordbuf = words[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*outlen = x;
|
||||||
|
return CRYPT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_encode_object_identifier.c,v $ */
|
||||||
|
/* $Revision: 1.7 $ */
|
||||||
|
/* $Date: 2006/12/28 01:27:24 $ */
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
|
||||||
|
*
|
||||||
|
* LibTomCrypt is a library that provides various cryptographic
|
||||||
|
* algorithms in a highly modular and flexible manner.
|
||||||
|
*
|
||||||
|
* The library is free for all purposes without any express
|
||||||
|
* guarantee it works.
|
||||||
|
*
|
||||||
|
* Tom St Denis, tomstdenis@gmail.com, http://libtom.org
|
||||||
|
*/
|
||||||
|
#include "../../headers/tomcrypt.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
@file der_encode_octet_string.c
|
||||||
|
ASN.1 DER, encode a OCTET STRING, Tom St Denis
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef LTC_DER
|
||||||
|
|
||||||
|
/**
|
||||||
|
Store an OCTET STRING
|
||||||
|
@param in The array of OCTETS to store (one per char)
|
||||||
|
@param inlen The number of OCTETS to store
|
||||||
|
@param out [out] The destination for the DER encoded OCTET STRING
|
||||||
|
@param outlen [in/out] The max size and resulting size of the DER OCTET STRING
|
||||||
|
@return CRYPT_OK if successful
|
||||||
|
*/
|
||||||
|
int der_encode_octet_string(const unsigned char *in, unsigned long inlen,
|
||||||
|
unsigned char *out, unsigned long *outlen)
|
||||||
|
{
|
||||||
|
unsigned long x, y, len;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
LTC_ARGCHK(in != NULL);
|
||||||
|
LTC_ARGCHK(out != NULL);
|
||||||
|
LTC_ARGCHK(outlen != NULL);
|
||||||
|
|
||||||
|
/* get the size */
|
||||||
|
if ((err = der_length_octet_string(inlen, &len)) != CRYPT_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* too big? */
|
||||||
|
if (len > *outlen) {
|
||||||
|
*outlen = len;
|
||||||
|
return CRYPT_BUFFER_OVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* encode the header+len */
|
||||||
|
x = 0;
|
||||||
|
out[x++] = 0x04;
|
||||||
|
if (inlen < 128) {
|
||||||
|
out[x++] = (unsigned char)inlen;
|
||||||
|
} else if (inlen < 256) {
|
||||||
|
out[x++] = 0x81;
|
||||||
|
out[x++] = (unsigned char)inlen;
|
||||||
|
} else if (inlen < 65536UL) {
|
||||||
|
out[x++] = 0x82;
|
||||||
|
out[x++] = (unsigned char)((inlen>>8)&255);
|
||||||
|
out[x++] = (unsigned char)(inlen&255);
|
||||||
|
} else if (inlen < 16777216UL) {
|
||||||
|
out[x++] = 0x83;
|
||||||
|
out[x++] = (unsigned char)((inlen>>16)&255);
|
||||||
|
out[x++] = (unsigned char)((inlen>>8)&255);
|
||||||
|
out[x++] = (unsigned char)(inlen&255);
|
||||||
|
} else {
|
||||||
|
return CRYPT_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store octets */
|
||||||
|
for (y = 0; y < inlen; y++) {
|
||||||
|
out[x++] = in[y];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* retun length */
|
||||||
|
*outlen = x;
|
||||||
|
|
||||||
|
return CRYPT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_encode_octet_string.c,v $ */
|
||||||
|
/* $Revision: 1.5 $ */
|
||||||
|
/* $Date: 2006/12/28 01:27:24 $ */
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue