mirror of
https://github.com/mangosfour/server.git
synced 2025-12-12 01:37:00 +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"]
|
||||
path = src/realmd
|
||||
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
|
||||
# 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
|
||||
#
|
||||
|
||||
# CMake policies
|
||||
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)
|
||||
set(MANGOS_VERSION 0.20)
|
||||
|
||||
set(CMAKE_MODULE_PATH
|
||||
${CMAKE_MODULE_PATH}
|
||||
${CMAKE_SOURCE_DIR}/cmake
|
||||
"${CMAKE_MODULE_PATH}"
|
||||
"${CMAKE_SOURCE_DIR}/cmake"
|
||||
)
|
||||
|
||||
# define all options here
|
||||
option(ACE_USE_EXTERNAL "Use external ACE" OFF)
|
||||
if(PCHSupport_FOUND AND WIN32) # TODO: why only enable it on windows by default?
|
||||
option(PCH "Use precompiled headers" ON)
|
||||
if(WIN32)
|
||||
set(CONF_DIR "" CACHE STRING "Config path. Can be absolute or relative")
|
||||
else()
|
||||
option(PCH "Use precompiled headers" OFF)
|
||||
set(CONF_DIR "etc/" CACHE STRING "Config path. Can be absolute or relative")
|
||||
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:
|
||||
#option(CLI "With CLI" ON)
|
||||
#option(RA "With Remote Access" OFF)
|
||||
#option(SQL "Copy SQL files" OFF)
|
||||
#option(TOOLS "Build tools" OFF)
|
||||
|
||||
# Output description of this script
|
||||
message("")
|
||||
message(
|
||||
"This script builds the MaNGOS server.
|
||||
Options that can be used in order to configure the process:
|
||||
General:
|
||||
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
|
||||
INCLUDE_BINDINGS_DIR Include a script library in src/bindings/ with the
|
||||
defined name. the name must corespond to the name of
|
||||
the folder and the folder must contain a valid
|
||||
CMakeLists.txt
|
||||
DEBUG Debug mode
|
||||
USE_STD_MALLOC Use standard malloc instead of TBB
|
||||
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>'.
|
||||
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("")
|
||||
|
||||
|
|
@ -89,6 +109,11 @@ if(WIN32)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# Used by map-extractor for now
|
||||
if(UNIX)
|
||||
find_package(BZip2 REQUIRED)
|
||||
endif()
|
||||
|
||||
# find Git: used to get the revision number
|
||||
find_package(Git)
|
||||
|
||||
|
|
@ -109,8 +134,15 @@ if(CMAKE_CONFIGURATION_TYPES)
|
|||
FORCE)
|
||||
endif()
|
||||
|
||||
set(BIN_DIR ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
set(CONF_DIR ${CMAKE_INSTALL_PREFIX}/etc)
|
||||
#Force set the default install path if it is default
|
||||
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
|
||||
string(FIND "${CONF_DIR}" ":" CONF_DIR_ABSOLUTE)
|
||||
|
|
@ -118,26 +150,33 @@ if(${CONF_DIR_ABSOLUTE} EQUAL -1)
|
|||
#Path was not absolute
|
||||
set(CONF_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CONF_DIR}")
|
||||
if(MSVC)
|
||||
set(CONF_COPY_DIR "${CMAKE_BINARY_DIR}/bin/$(ConfigurationName)/${CONF_DIR}")
|
||||
else()
|
||||
#Path was absolute
|
||||
set(CONF_INSTALL_DIR "${CONF_DIR}")
|
||||
if(MSVC)
|
||||
set(CONF_COPY_DIR "${CONF_DIR}")
|
||||
endif()
|
||||
set(CONF_COPY_DIR "${CMAKE_BINARY_DIR}/bin/$(ConfigurationName)/${CONF_DIR}")
|
||||
endif()
|
||||
else()
|
||||
#Path was absolute
|
||||
set(CONF_INSTALL_DIR "${CONF_DIR}")
|
||||
if(MSVC)
|
||||
set(CONF_COPY_DIR "${CONF_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# If win32 put it in the bin dir not lib
|
||||
# If win32 put binaries in root folder, else bin.
|
||||
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()
|
||||
set(LIBS_DIR ${CMAKE_INSTALL_PREFIX}/lib)
|
||||
endif()
|
||||
|
||||
# For Unix systems set the rpath so that libraries are found
|
||||
set(CMAKE_INSTALL_RPATH ${LIBS_DIR})
|
||||
set(CMAKE_INSTALL_NAME_DIR ${LIBS_DIR})
|
||||
set(CMAKE_INSTALL_RPATH "${LIBS_DIR}")
|
||||
set(CMAKE_INSTALL_NAME_DIR "${LIBS_DIR}")
|
||||
# Run out of build tree
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH OFF)
|
||||
|
||||
|
|
@ -164,18 +203,28 @@ endif()
|
|||
|
||||
# Win32 delivered packages
|
||||
if(WIN32)
|
||||
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_DEBUG_LIBRARY ${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_debug/libmySQL.lib)
|
||||
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_DEBUG_LIBRARIES ${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_debug/libeay32.lib)
|
||||
set(MYSQL_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/dep/include/mysql")
|
||||
set(MYSQL_LIBRARY "${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/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_LIBRARIES "${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libeay32.lib")
|
||||
set(OPENSSL_DEBUG_LIBRARIES "${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libeay32.lib")
|
||||
# zlib is build
|
||||
endif()
|
||||
|
||||
# *nix-specific packages
|
||||
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(ZLIB REQUIRED)
|
||||
endif()
|
||||
|
|
@ -210,19 +259,11 @@ endif()
|
|||
|
||||
message(STATUS "MaNGOS-Core revision : ${GIT_REVISION}")
|
||||
message(STATUS "Install server to : ${CMAKE_INSTALL_PREFIX}")
|
||||
|
||||
if(DEFINED INCLUDE_BINDINGS_DIR AND INCLUDE_BINDINGS_DIR)
|
||||
# check if the directory exists
|
||||
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})")
|
||||
message(STATUS "Install configs to : ${CONF_INSTALL_DIR}")
|
||||
if("${CONF_DIR}" STREQUAL "")
|
||||
message(STATUS "Search configs from : binary directory (default)")
|
||||
else()
|
||||
message(STATUS "Build script library : No")
|
||||
message(STATUS "Search configs from : ${CONF_DIR}")
|
||||
endif()
|
||||
|
||||
# if(CLI)
|
||||
|
|
@ -239,42 +280,67 @@ endif()
|
|||
# message(STATUS "* Build with RA : No (default)")
|
||||
# endif(RA)
|
||||
|
||||
if(PCH AND NOT PCHSupport_FOUND)
|
||||
set(PCH 0 CACHE BOOL
|
||||
"Use precompiled headers"
|
||||
FORCE)
|
||||
message(WARNING "No PCH for your system possible but PCH was set to 1. Resetting it."
|
||||
)
|
||||
if(SOAP)
|
||||
message(STATUS "Support for SOAP : Yes")
|
||||
set(DEFINITIONS ${DEFINITIONS} -DENABLE_SOAP)
|
||||
else()
|
||||
message(STATUS "Support for SOAP : No (default)")
|
||||
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)
|
||||
message(STATUS "Use PCH : Yes")
|
||||
message(STATUS "Use PCH : Yes (default)")
|
||||
else()
|
||||
message(STATUS "Use PCH : No")
|
||||
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)
|
||||
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_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
|
||||
endif()
|
||||
|
||||
# Set warning levels for different builds
|
||||
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_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Wfatal-errors -Wextra")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_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 -Winvalid-pch")
|
||||
elseif(WIN32)
|
||||
# Disable warnings in Visual Studio 8 and above and add /MP
|
||||
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_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_CXX_FLAGS_DEBUG "${CMAKE_CXX_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 /MP")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
@ -284,12 +350,6 @@ endif()
|
|||
# message(STATUS "Install SQL-files : No (default)")
|
||||
# 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.
|
||||
if(MSVC)
|
||||
# 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_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)
|
||||
|
||||
# Generate revision-extractor
|
||||
|
|
@ -312,43 +372,23 @@ add_executable(genrev
|
|||
${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)
|
||||
install(
|
||||
FILES
|
||||
${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_release/libeay32.dll
|
||||
${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_release/libmySQL.dll
|
||||
"${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libeay32.dll"
|
||||
"${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/ssleay32.dll"
|
||||
"${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libmySQL.dll"
|
||||
DESTINATION ${LIBS_DIR}
|
||||
CONFIGURATIONS Release
|
||||
)
|
||||
install(
|
||||
FILES
|
||||
${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_debug/libeay32.dll
|
||||
${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}_debug/libmySQL.dll
|
||||
"${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libeay32.dll"
|
||||
"${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/ssleay32.dll"
|
||||
"${CMAKE_SOURCE_DIR}/dep/lib/${DEP_ARCH}/libmySQL.dll"
|
||||
DESTINATION ${LIBS_DIR}
|
||||
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()
|
||||
|
||||
if(XCODE)
|
||||
|
|
@ -359,30 +399,73 @@ if(XCODE)
|
|||
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
|
||||
# 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
|
||||
DO_MYSQL
|
||||
HAVE_CONFIG_H
|
||||
VERSION="${MANGOS_VERSION}"
|
||||
SYSCONFDIR="${CONF_DIR}/"
|
||||
MANGOS
|
||||
)
|
||||
|
||||
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_DEBUG _DEBUG MANGOS_DEBUG)
|
||||
set(DEFINITIONS_DEBUG) # _DEBUG MANGOS_DEBUG) was here..
|
||||
if(WIN32)
|
||||
set(DEFINITIONS ${DEFINITIONS} WIN32 _WIN32)
|
||||
set(DEFINITIONS_RELEASE ${DEFINITIONS_RELEASE} _CRT_SECURE_NO_WARNINGS)
|
||||
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}")
|
||||
#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_DEBUG "${DEFINITIONS_DEBUG}")
|
||||
|
||||
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
|
||||
# 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
|
||||
#
|
||||
#################
|
||||
## Eclipse
|
||||
#################
|
||||
|
||||
*.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
|
||||
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/SBaseDumpData.cpp
|
||||
src/SBaseFileTable.cpp
|
||||
src/SBaseSubTypes.cpp
|
||||
src/SCompression.cpp
|
||||
src/SFileAddFile.cpp
|
||||
src/SFileAttributes.cpp
|
||||
|
|
@ -22,12 +23,15 @@ set(SRC_FILES
|
|||
src/SFileCreateArchive.cpp
|
||||
src/SFileExtractFile.cpp
|
||||
src/SFileFindFile.cpp
|
||||
src/SFileGetFileInfo.cpp
|
||||
src/SFileListFile.cpp
|
||||
src/SFileOpenArchive.cpp
|
||||
src/SFileOpenFileEx.cpp
|
||||
src/SFilePatchArchives.cpp
|
||||
src/SFileReadFile.cpp
|
||||
src/SFileVerify.cpp
|
||||
src/libtomcrypt/src/pk/rsa/rsa_verify_simple.c
|
||||
src/libtomcrypt/src/misc/crypt_libc.c
|
||||
)
|
||||
|
||||
set(TOMCRYPT_FILES
|
||||
|
|
@ -43,7 +47,6 @@ set(TOMCRYPT_FILES
|
|||
src/libtomcrypt/src/misc/crypt_find_prng.c
|
||||
src/libtomcrypt/src/misc/crypt_hash_descriptor.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_prng_descriptor.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_utctime.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_boolean.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_oaep_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_encode.c
|
||||
src/libtomcrypt/src/pk/rsa/rsa_exptmod.c
|
||||
src/libtomcrypt/src/pk/rsa/rsa_free.c
|
||||
src/libtomcrypt/src/pk/rsa/rsa_import.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_simple.c
|
||||
)
|
||||
|
||||
set(TOMMATH_FILES
|
||||
|
|
@ -224,7 +243,7 @@ set(ZLIB_BZIP2_FILES
|
|||
src/bzip2/huffman.c
|
||||
src/bzip2/randtable.c
|
||||
src/zlib/adler32.c
|
||||
src/zlib/compress2.c
|
||||
src/zlib/compress.c
|
||||
src/zlib/crc32.c
|
||||
src/zlib/deflate.c
|
||||
src/zlib/inffast.c
|
||||
|
|
@ -234,10 +253,6 @@ set(ZLIB_BZIP2_FILES
|
|||
src/zlib/zutil.c
|
||||
)
|
||||
|
||||
# set(TEST_SRC_FILES
|
||||
# test/Test.cpp
|
||||
# )
|
||||
|
||||
add_definitions(-D_7ZIP_ST -DBZ_STRICT_ANSI)
|
||||
|
||||
if(WIN32)
|
||||
|
|
@ -268,37 +283,21 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL Linux)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
add_library(storm SHARED ${SRC_FILES} ${SRC_ADDITIONAL_FILES})
|
||||
target_link_libraries(storm ${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()
|
||||
add_library(StormLib STATIC ${SRC_FILES} ${SRC_ADDITIONAL_FILES})
|
||||
target_link_libraries(StormLib ${LINK_LIBS})
|
||||
|
||||
if(APPLE)
|
||||
set_target_properties(storm PROPERTIES FRAMEWORK true)
|
||||
set_target_properties(storm PROPERTIES PUBLIC_HEADER "src/StormLib.h src/StormPort.h")
|
||||
set_target_properties(storm PROPERTIES LINK_FLAGS "-framework Carbon")
|
||||
set_target_properties(StormLib PROPERTIES FRAMEWORK true)
|
||||
set_target_properties(StormLib PROPERTIES PUBLIC_HEADER "src/StormLib.h src/StormPort.h")
|
||||
set_target_properties(StormLib PROPERTIES LINK_FLAGS "-framework Carbon")
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
set_target_properties(storm PROPERTIES VERSION 8.20.0)
|
||||
set_target_properties(storm PROPERTIES SOVERSION 8.20)
|
||||
set_target_properties(StormLib PROPERTIES VERSION 9.0.0)
|
||||
set_target_properties(StormLib PROPERTIES SOVERSION 9)
|
||||
endif()
|
||||
|
||||
# On Win32, build StormLib.dll since we don't want to clash with Storm.dll
|
||||
if(WIN32)
|
||||
set_target_properties(storm PROPERTIES OUTPUT_NAME StormLib)
|
||||
set_target_properties(StormLib PROPERTIES OUTPUT_NAME StormLib)
|
||||
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
|
||||
================
|
||||
|
||||
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
|
||||
|
||||
- Support for UNICODE encoding for on-disk files
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
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)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position
|
||||
void * pvBuffer, // Pointer to data to be read
|
||||
DWORD dwBytesToRead // Number of bytes to read from the file
|
||||
);
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it reads from the current position
|
||||
void * pvBuffer, // Pointer to data to be read
|
||||
DWORD dwBytesToRead // Number of bytes to read from the file
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_WRITE)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to the current position
|
||||
const void * pvBuffer, // Pointer to data to be written
|
||||
DWORD dwBytesToWrite // Number of bytes to read from the file
|
||||
);
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pByteOffset, // Pointer to file byte offset. If NULL, it writes to the current position
|
||||
const void * pvBuffer, // Pointer to data to be written
|
||||
DWORD dwBytesToWrite // Number of bytes to read from the file
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_GETPOS)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pByteOffset // Pointer to store current file position
|
||||
);
|
||||
typedef bool (*STREAM_RESIZE)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG FileSize // New size for the file, in bytes
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_GETSIZE)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pFileSize // Receives the file size, in bytes
|
||||
);
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pFileSize // Receives the file size, in bytes
|
||||
);
|
||||
|
||||
typedef bool (*STREAM_SETSIZE)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG FileSize // New size for the file, in bytes
|
||||
);
|
||||
|
||||
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 bool (*STREAM_GETPOS)(
|
||||
struct TFileStream * pStream, // Pointer to an open stream
|
||||
ULONGLONG * pByteOffset // Pointer to store current file position
|
||||
);
|
||||
|
||||
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
|
||||
{
|
||||
|
|
@ -89,91 +108,100 @@ typedef struct _PART_FILE_MAP_ENTRY
|
|||
|
||||
} PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
typedef struct _FILE_BITMAP_FOOTER
|
||||
{
|
||||
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
|
||||
{
|
||||
ULONGLONG FileSize; // Size of the file
|
||||
ULONGLONG FilePos; // Current file position
|
||||
ULONGLONG FileTime; // Date/time of last modification of the file
|
||||
ULONGLONG FileTime; // Last write time
|
||||
HANDLE hFile; // File handle
|
||||
} File;
|
||||
|
||||
struct
|
||||
{
|
||||
ULONGLONG FileSize; // Mapped file size
|
||||
ULONGLONG FilePos; // Current stream position
|
||||
ULONGLONG FileTime; // Date/time of last modification of the file
|
||||
ULONGLONG FileSize; // Size of the file
|
||||
ULONGLONG FilePos; // Current file position
|
||||
ULONGLONG FileTime; // Last write time
|
||||
LPBYTE pbFile; // Pointer to mapped view
|
||||
} Map;
|
||||
|
||||
struct
|
||||
{
|
||||
ULONGLONG FileSize; // Size of the internet file
|
||||
ULONGLONG FilePos; // Current position in the file
|
||||
ULONGLONG FileTime; // Date/time of last modification of the file
|
||||
ULONGLONG FileSize; // Size of the file
|
||||
ULONGLONG FilePos; // Current file position
|
||||
ULONGLONG FileTime; // Last write time
|
||||
HANDLE hInternet; // Internet handle
|
||||
HANDLE hConnect; // Connection to the internet server
|
||||
} Http;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structure for linear stream
|
||||
|
||||
struct TFileStream
|
||||
{
|
||||
// Stream provider functions
|
||||
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_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_SETSIZE StreamSetSize; // Pointer to function changing file size
|
||||
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_GETPOS StreamGetPos; // Pointer to function that returns current file position
|
||||
STREAM_CLOSE StreamClose; // Pointer to function closing the stream
|
||||
|
||||
// Stream provider data members
|
||||
TCHAR szFileName[MAX_PATH]; // File name
|
||||
DWORD dwFlags; // Stream flags
|
||||
// Block-oriented functions
|
||||
BLOCK_READ BlockRead; // Pointer to function reading one or more blocks
|
||||
BLOCK_CHECK BlockCheck; // Pointer to function checking whether the block is present
|
||||
|
||||
// Base provider functions
|
||||
STREAM_READ BaseRead;
|
||||
STREAM_WRITE BaseWrite;
|
||||
STREAM_GETPOS BaseGetPos; // Pointer to function that returns current file position
|
||||
STREAM_CREATE BaseCreate; // Pointer to base create function
|
||||
STREAM_OPEN BaseOpen; // Pointer to base open function
|
||||
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_SETSIZE BaseSetSize; // Pointer to function changing file size
|
||||
STREAM_GETTIME BaseGetTime; // Pointer to function retrieving the file time
|
||||
STREAM_GETPOS BaseGetPos; // Pointer to function that returns current file position
|
||||
STREAM_CLOSE BaseClose; // Pointer to function closing the stream
|
||||
|
||||
// Base provider data members
|
||||
TBaseData Base; // Base provider data
|
||||
// Base provider data (file size, file position)
|
||||
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
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structure for linear stream
|
||||
// Structures for block-oriented stream
|
||||
|
||||
struct TLinearStream : public TFileStream
|
||||
struct TBlockStream : public TFileStream
|
||||
{
|
||||
TFileBitmap * pBitmap; // Pointer to the stream bitmap
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structure for partial stream
|
||||
|
||||
struct TPartialStream : public TFileStream
|
||||
{
|
||||
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
|
||||
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)
|
||||
DWORD BlockSize; // Size of one block, in bytes
|
||||
DWORD BlockCount; // Number of data blocks in the file
|
||||
DWORD IsComplete; // If nonzero, no blocks are missing
|
||||
DWORD IsModified; // nonzero if the bitmap has been modified
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -181,7 +209,7 @@ struct TPartialStream : public TFileStream
|
|||
|
||||
#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
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -93,7 +93,7 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable)
|
|||
|
||||
GetBits(pHetTable->pBetIndexes, i * pHetTable->dwIndexSizeTotal,
|
||||
pHetTable->dwIndexSize,
|
||||
&dwBetIndex,
|
||||
&dwBetIndex,
|
||||
4);
|
||||
|
||||
if(dwBetIndex < pHetTable->dwMaxFileCount)
|
||||
|
|
@ -102,41 +102,41 @@ void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable)
|
|||
|
||||
GetBits(pBetTable->pBetHashes, dwBetIndex * pBetTable->dwBetHashSizeTotal,
|
||||
pBetTable->dwBetHashSize,
|
||||
&BetHash,
|
||||
&BetHash,
|
||||
8);
|
||||
|
||||
GetBits(dwEntryIndex + pBetTable->dwBitIndex_FilePos,
|
||||
GetBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FilePos,
|
||||
pBetTable->dwBitCount_FilePos,
|
||||
&ByteOffset,
|
||||
&ByteOffset,
|
||||
8);
|
||||
|
||||
GetBits(dwEntryIndex + pBetTable->dwBitIndex_FileSize,
|
||||
GetBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FileSize,
|
||||
pBetTable->dwBitCount_FileSize,
|
||||
&dwFileSize,
|
||||
&dwFileSize,
|
||||
4);
|
||||
|
||||
GetBits(dwEntryIndex + pBetTable->dwBitIndex_CmpSize,
|
||||
GetBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_CmpSize,
|
||||
pBetTable->dwBitCount_CmpSize,
|
||||
&dwCmpSize,
|
||||
&dwCmpSize,
|
||||
4);
|
||||
|
||||
GetBits(dwEntryIndex + pBetTable->dwBitIndex_FlagIndex,
|
||||
GetBits(pBetTable->pFileTable, dwEntryIndex + pBetTable->dwBitIndex_FlagIndex,
|
||||
pBetTable->dwBitCount_FlagIndex,
|
||||
&dwFlagIndex,
|
||||
&dwFlagIndex,
|
||||
4);
|
||||
|
||||
dwFlags = pBetTable->pFileFlags[dwFlagIndex];
|
||||
}
|
||||
|
||||
printf(" %04X %02lX %04X %016llX %016llX %08X %08X %04X %08X\n", i,
|
||||
pHetTable->pHetHashes[i],
|
||||
dwBetIndex,
|
||||
BetHash,
|
||||
ByteOffset,
|
||||
dwFileSize,
|
||||
dwCmpSize,
|
||||
dwFlagIndex,
|
||||
dwFlags);
|
||||
pHetTable->pHetHashes[i],
|
||||
dwBetIndex,
|
||||
BetHash,
|
||||
ByteOffset,
|
||||
dwFileSize,
|
||||
dwCmpSize,
|
||||
dwFlagIndex,
|
||||
dwFlags);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
@ -22,32 +22,31 @@
|
|||
// Information about the input and output buffers for pklib
|
||||
typedef struct
|
||||
{
|
||||
char * pbInBuff; // Pointer to input data buffer
|
||||
char * pbInBuffEnd; // End of the input buffer
|
||||
char * pbOutBuff; // Pointer to output data buffer
|
||||
char * pbOutBuffEnd; // Pointer to output data buffer
|
||||
unsigned char * pbInBuff; // Pointer to input data buffer
|
||||
unsigned char * pbInBuffEnd; // End of the input buffer
|
||||
unsigned char * pbOutBuff; // Pointer to output data buffer
|
||||
unsigned char * pbOutBuffEnd; // Pointer to output data buffer
|
||||
} TDataInfo;
|
||||
|
||||
// Prototype of the compression function
|
||||
// Function doesn't return an error. A success means that the size of compressed buffer
|
||||
// is lower than size of uncompressed buffer.
|
||||
typedef void (*COMPRESS)(
|
||||
char * pbOutBuffer, // [out] Pointer to the buffer where the compressed data will be stored
|
||||
int * pcbOutBuffer, // [in] Pointer to length of the buffer pointed by pbOutBuffer
|
||||
// [out] Contains length of the compressed data
|
||||
char * pbInBuffer, // [in] Pointer to the buffer with data to compress
|
||||
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 nCmpLevel); // [in] Compression specific value. ADPCM uses this. Should be set to zero.
|
||||
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 pvOutBuffer
|
||||
void * pvInBuffer, // [in] Pointer to the buffer with data to compress
|
||||
int cbInBuffer, // [in] Length of the buffer pointer by pvInBuffer
|
||||
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.
|
||||
|
||||
// Prototype of the decompression function
|
||||
// Returns 1 if success, 0 if failure
|
||||
typedef int (*DECOMPRESS)(
|
||||
char * pbOutBuffer, // [out] Pointer to the buffer where to store decompressed data
|
||||
int * pcbOutBuffer, // [in] Pointer to total size of the buffer pointed by pbOutBuffer
|
||||
// [out] Contains length of the decompressed data
|
||||
char * pbInBuffer, // [in] Pointer to data to be decompressed
|
||||
int cbInBuffer); // [in] Length of the data to be decompressed
|
||||
void * pvOutBuffer, // [out] Pointer to the buffer where to store decompressed data
|
||||
int * pcbOutBuffer, // [in] Pointer to total size of the buffer pointed by pvOutBuffer
|
||||
// [out] Contains length of the decompressed data
|
||||
void * pvInBuffer, // [in] Pointer to data to be decompressed
|
||||
int cbInBuffer); // [in] Length of the data to be decompressed
|
||||
|
||||
// Table of compression functions
|
||||
typedef struct
|
||||
|
|
@ -70,59 +69,22 @@ typedef struct
|
|||
/* */
|
||||
/*****************************************************************************/
|
||||
|
||||
// 1500F4C0
|
||||
void Compress_huff(
|
||||
char * pbOutBuffer,
|
||||
int * pcbOutBuffer,
|
||||
char * pbInBuffer,
|
||||
int cbInBuffer,
|
||||
int * pCmpType,
|
||||
int /* nCmpLevel */)
|
||||
void Compress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||
{
|
||||
THuffmannTree ht; // Huffmann tree for compression
|
||||
TOutputStream os; // Output stream
|
||||
THuffmannTree ht(true);
|
||||
TOutputStream os(pvOutBuffer, *pcbOutBuffer);
|
||||
|
||||
// Initialize output stream
|
||||
os.pbOutBuffer = (unsigned char *)pbOutBuffer;
|
||||
os.cbOutSize = *pcbOutBuffer;
|
||||
os.pbOutPos = (unsigned char *)pbOutBuffer;
|
||||
os.dwBitBuff = 0;
|
||||
os.nBits = 0;
|
||||
|
||||
// Initialize the Huffmann tree for compression
|
||||
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();
|
||||
STORMLIB_UNUSED(nCmpLevel);
|
||||
*pcbOutBuffer = ht.Compress(&os, pvInBuffer, cbInBuffer, *pCmpType);
|
||||
}
|
||||
|
||||
// 1500F5F0
|
||||
int Decompress_huff(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer)
|
||||
int Decompress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||
{
|
||||
THuffmannTree ht;
|
||||
TInputStream is;
|
||||
THuffmannTree ht(false);
|
||||
TInputStream is(pvInBuffer, cbInBuffer);
|
||||
|
||||
// Initialize input stream
|
||||
is.pbInBufferEnd = (unsigned char *)pbInBuffer + cbInBuffer;
|
||||
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;
|
||||
*pcbOutBuffer = ht.Decompress(pvOutBuffer, *pcbOutBuffer, &is);
|
||||
return (*pcbOutBuffer == 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
@ -131,23 +93,21 @@ int Decompress_huff(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, i
|
|||
/* */
|
||||
/******************************************************************************/
|
||||
|
||||
void Compress_ZLIB(
|
||||
char * pbOutBuffer,
|
||||
int * pcbOutBuffer,
|
||||
char * pbInBuffer,
|
||||
int cbInBuffer,
|
||||
int * /* pCmpType */,
|
||||
int /* nCmpLevel */)
|
||||
void Compress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||
{
|
||||
z_stream z; // Stream information for zlib
|
||||
int windowBits;
|
||||
int nResult;
|
||||
|
||||
// Keep compilers happy
|
||||
STORMLIB_UNUSED(pCmpType);
|
||||
STORMLIB_UNUSED(nCmpLevel);
|
||||
|
||||
// Fill the stream structure for zlib
|
||||
z.next_in = (Bytef *)pbInBuffer;
|
||||
z.next_in = (Bytef *)pvInBuffer;
|
||||
z.avail_in = (uInt)cbInBuffer;
|
||||
z.total_in = cbInBuffer;
|
||||
z.next_out = (Bytef *)pbOutBuffer;
|
||||
z.next_out = (Bytef *)pvOutBuffer;
|
||||
z.avail_out = *pcbOutBuffer;
|
||||
z.total_out = 0;
|
||||
z.zalloc = NULL;
|
||||
|
|
@ -175,11 +135,11 @@ void Compress_ZLIB(
|
|||
// Storm.dll uses zlib version 1.1.3
|
||||
// Wow.exe uses zlib version 1.2.3
|
||||
nResult = deflateInit2(&z,
|
||||
6, // Compression level used by WoW MPQs
|
||||
Z_DEFLATED,
|
||||
windowBits,
|
||||
8,
|
||||
Z_DEFAULT_STRATEGY);
|
||||
6, // Compression level used by WoW MPQs
|
||||
Z_DEFLATED,
|
||||
windowBits,
|
||||
8,
|
||||
Z_DEFAULT_STRATEGY);
|
||||
if(nResult == Z_OK)
|
||||
{
|
||||
// Call zlib to compress the data
|
||||
|
|
@ -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
|
||||
int nResult;
|
||||
|
||||
// Fill the stream structure for zlib
|
||||
z.next_in = (Bytef *)pbInBuffer;
|
||||
z.next_in = (Bytef *)pvInBuffer;
|
||||
z.avail_in = (uInt)cbInBuffer;
|
||||
z.total_in = cbInBuffer;
|
||||
z.next_out = (Bytef *)pbOutBuffer;
|
||||
z.next_out = (Bytef *)pvOutBuffer;
|
||||
z.avail_out = *pcbOutBuffer;
|
||||
z.total_out = 0;
|
||||
z.zalloc = NULL;
|
||||
|
|
@ -272,68 +232,74 @@ static void WriteOutputData(char * buf, unsigned int * size, void * param)
|
|||
assert(pInfo->pbOutBuff <= pInfo->pbOutBuffEnd);
|
||||
}
|
||||
|
||||
static void Compress_PKLIB(
|
||||
char * pbOutBuffer,
|
||||
int * pcbOutBuffer,
|
||||
char * pbInBuffer,
|
||||
int cbInBuffer,
|
||||
int * /* pCmpType */,
|
||||
int /* nCmpLevel */)
|
||||
static void Compress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||
{
|
||||
TDataInfo Info; // Data information
|
||||
char * work_buf = STORM_ALLOC(char, CMP_BUFFER_SIZE);// Pklib's work buffer
|
||||
unsigned int dict_size; // Dictionary size
|
||||
unsigned int ctype = CMP_BINARY; // Compression type
|
||||
|
||||
// Fill data information structure
|
||||
memset(work_buf, 0, CMP_BUFFER_SIZE);
|
||||
Info.pbInBuff = pbInBuffer;
|
||||
Info.pbInBuffEnd = pbInBuffer + cbInBuffer;
|
||||
Info.pbOutBuff = pbOutBuffer;
|
||||
Info.pbOutBuffEnd = pbOutBuffer + *pcbOutBuffer;
|
||||
// Keep compilers happy
|
||||
STORMLIB_UNUSED(pCmpType);
|
||||
STORMLIB_UNUSED(nCmpLevel);
|
||||
|
||||
//
|
||||
// Set the dictionary size
|
||||
//
|
||||
// Diablo I ues fixed dictionary size of CMP_IMPLODE_DICT_SIZE3
|
||||
// Starcraft uses the variable dictionary size based on algorithm below
|
||||
//
|
||||
// Handle no-memory condition
|
||||
if(work_buf != NULL)
|
||||
{
|
||||
// Fill data information structure
|
||||
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;
|
||||
else if(0x600 <= cbInBuffer && cbInBuffer < 0xC00)
|
||||
dict_size = CMP_IMPLODE_DICT_SIZE2;
|
||||
else
|
||||
dict_size = CMP_IMPLODE_DICT_SIZE3;
|
||||
//
|
||||
// Set the dictionary size
|
||||
//
|
||||
// Diablo I uses fixed dictionary size of CMP_IMPLODE_DICT_SIZE3
|
||||
// Starcraft I uses the variable dictionary size based on algorithm below
|
||||
//
|
||||
|
||||
// Do the compression
|
||||
if(implode(ReadInputData, WriteOutputData, work_buf, &Info, &ctype, &dict_size) == CMP_NO_ERROR)
|
||||
*pcbOutBuffer = (int)(Info.pbOutBuff - pbOutBuffer);
|
||||
if (cbInBuffer < 0x600)
|
||||
dict_size = CMP_IMPLODE_DICT_SIZE1;
|
||||
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
|
||||
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
|
||||
memset(work_buf, 0, EXP_BUFFER_SIZE);
|
||||
Info.pbInBuff = pbInBuffer;
|
||||
Info.pbInBuffEnd = pbInBuffer + cbInBuffer;
|
||||
Info.pbOutBuff = pbOutBuffer;
|
||||
Info.pbOutBuffEnd = pbOutBuffer + *pcbOutBuffer;
|
||||
Info.pbInBuff = (unsigned char *)pvInBuffer;
|
||||
Info.pbInBuffEnd = (unsigned char *)pvInBuffer + cbInBuffer;
|
||||
Info.pbOutBuff = (unsigned char *)pvOutBuffer;
|
||||
Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer;
|
||||
|
||||
// Do the decompression
|
||||
explode(ReadInputData, WriteOutputData, work_buf, &Info);
|
||||
|
||||
// If PKLIB is unable to decompress the data, return 0;
|
||||
if(Info.pbOutBuff == pbOutBuffer)
|
||||
if(Info.pbOutBuff == pvOutBuffer)
|
||||
return 0;
|
||||
|
||||
// Give away the number of decompressed bytes
|
||||
*pcbOutBuffer = (int)(Info.pbOutBuff - pbOutBuffer);
|
||||
*pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer);
|
||||
STORM_FREE(work_buf);
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -344,19 +310,17 @@ static int Decompress_PKLIB(char * pbOutBuffer, int * pcbOutBuffer, char * pbInB
|
|||
/* */
|
||||
/******************************************************************************/
|
||||
|
||||
static void Compress_BZIP2(
|
||||
char * pbOutBuffer,
|
||||
int * pcbOutBuffer,
|
||||
char * pbInBuffer,
|
||||
int cbInBuffer,
|
||||
int * /* pCmpType */,
|
||||
int /* nCmpLevel */)
|
||||
static void Compress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||
{
|
||||
bz_stream strm;
|
||||
int blockSize100k = 9;
|
||||
int workFactor = 30;
|
||||
int bzError;
|
||||
|
||||
// Keep compilers happy
|
||||
STORMLIB_UNUSED(pCmpType);
|
||||
STORMLIB_UNUSED(nCmpLevel);
|
||||
|
||||
// Initialize the BZIP2 compression
|
||||
strm.bzalloc = NULL;
|
||||
strm.bzfree = NULL;
|
||||
|
|
@ -365,9 +329,9 @@ static void Compress_BZIP2(
|
|||
// Last checked on Starcraft II
|
||||
if(BZ2_bzCompressInit(&strm, blockSize100k, 0, workFactor) == BZ_OK)
|
||||
{
|
||||
strm.next_in = pbInBuffer;
|
||||
strm.next_in = (char *)pvInBuffer;
|
||||
strm.avail_in = cbInBuffer;
|
||||
strm.next_out = pbOutBuffer;
|
||||
strm.next_out = (char *)pvOutBuffer;
|
||||
strm.avail_out = *pcbOutBuffer;
|
||||
|
||||
// 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;
|
||||
int nResult = BZ_OK;
|
||||
|
|
@ -396,9 +360,9 @@ static int Decompress_BZIP2(char * pbOutBuffer, int * pcbOutBuffer, char * pbInB
|
|||
strm.bzfree = NULL;
|
||||
if(BZ2_bzDecompressInit(&strm, 0, 0) == BZ_OK)
|
||||
{
|
||||
strm.next_in = pbInBuffer;
|
||||
strm.next_in = (char *)pvInBuffer;
|
||||
strm.avail_in = cbInBuffer;
|
||||
strm.next_out = pbOutBuffer;
|
||||
strm.next_out = (char *)pvOutBuffer;
|
||||
strm.avail_out = *pcbOutBuffer;
|
||||
|
||||
// Perform the decompression
|
||||
|
|
@ -461,17 +425,12 @@ static void LZMA_Callback_Free(void *p, void *address)
|
|||
// the data compressed by StormLib.
|
||||
//
|
||||
|
||||
/*static */ void Compress_LZMA(
|
||||
char * pbOutBuffer,
|
||||
int * pcbOutBuffer,
|
||||
char * pbInBuffer,
|
||||
int cbInBuffer,
|
||||
int * /* pCmpType */,
|
||||
int /* nCmpLevel */)
|
||||
static void Compress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||
{
|
||||
ICompressProgress Progress;
|
||||
CLzmaEncProps props;
|
||||
ISzAlloc SzAlloc;
|
||||
Byte * pbOutBuffer = (Byte *)pvOutBuffer;
|
||||
Byte * destBuffer;
|
||||
SizeT destLen = *pcbOutBuffer;
|
||||
SizeT srcLen = cbInBuffer;
|
||||
|
|
@ -479,6 +438,10 @@ static void LZMA_Callback_Free(void *p, void *address)
|
|||
size_t encodedPropsSize = LZMA_PROPS_SIZE;
|
||||
SRes nResult;
|
||||
|
||||
// Keep compilers happy
|
||||
STORMLIB_UNUSED(pCmpType);
|
||||
STORMLIB_UNUSED(nCmpLevel);
|
||||
|
||||
// Fill the callbacks in structures
|
||||
Progress.Progress = LZMA_Callback_Progress;
|
||||
SzAlloc.Alloc = LZMA_Callback_Alloc;
|
||||
|
|
@ -488,19 +451,19 @@ static void LZMA_Callback_Free(void *p, void *address)
|
|||
LzmaEncProps_Init(&props);
|
||||
|
||||
// Perform compression
|
||||
destBuffer = (Byte *)pbOutBuffer + LZMA_HEADER_SIZE;
|
||||
destBuffer = (Byte *)pvOutBuffer + LZMA_HEADER_SIZE;
|
||||
destLen = *pcbOutBuffer - LZMA_HEADER_SIZE;
|
||||
nResult = LzmaEncode(destBuffer,
|
||||
&destLen,
|
||||
(Byte *)pbInBuffer,
|
||||
&destLen,
|
||||
(Byte *)pvInBuffer,
|
||||
srcLen,
|
||||
&props,
|
||||
&props,
|
||||
encodedProps,
|
||||
&encodedPropsSize,
|
||||
&encodedPropsSize,
|
||||
0,
|
||||
&Progress,
|
||||
&SzAlloc,
|
||||
&SzAlloc);
|
||||
&Progress,
|
||||
&SzAlloc,
|
||||
&SzAlloc);
|
||||
if(nResult != SZ_OK)
|
||||
return;
|
||||
|
||||
|
|
@ -512,7 +475,7 @@ static void LZMA_Callback_Free(void *p, void *address)
|
|||
*pbOutBuffer++ = 0;
|
||||
|
||||
// Copy the encoded properties to the output buffer
|
||||
memcpy(pbOutBuffer, encodedProps, encodedPropsSize);
|
||||
memcpy(pvOutBuffer, encodedProps, encodedPropsSize);
|
||||
pbOutBuffer += encodedPropsSize;
|
||||
|
||||
// Copy the size of the data
|
||||
|
|
@ -529,12 +492,12 @@ static void LZMA_Callback_Free(void *p, void *address)
|
|||
*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;
|
||||
ISzAlloc SzAlloc;
|
||||
Byte * destBuffer = (Byte *)pbOutBuffer;
|
||||
Byte * srcBuffer = (Byte *)pbInBuffer;
|
||||
Byte * destBuffer = (Byte *)pvOutBuffer;
|
||||
Byte * srcBuffer = (Byte *)pvInBuffer;
|
||||
SizeT destLen = *pcbOutBuffer;
|
||||
SizeT srcLen = cbInBuffer;
|
||||
SRes nResult;
|
||||
|
|
@ -554,14 +517,55 @@ static int Decompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBu
|
|||
// Perform compression
|
||||
srcLen = cbInBuffer - LZMA_HEADER_SIZE;
|
||||
nResult = LzmaDecode(destBuffer,
|
||||
&destLen,
|
||||
&destLen,
|
||||
srcBuffer + LZMA_HEADER_SIZE,
|
||||
&srcLen,
|
||||
&srcLen,
|
||||
srcBuffer + 1,
|
||||
LZMA_PROPS_SIZE,
|
||||
LZMA_FINISH_END,
|
||||
&LzmaStatus,
|
||||
&SzAlloc);
|
||||
&LzmaStatus,
|
||||
&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)
|
||||
return 0;
|
||||
|
||||
|
|
@ -575,20 +579,18 @@ static int Decompress_LZMA(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBu
|
|||
/* */
|
||||
/******************************************************************************/
|
||||
|
||||
void Compress_SPARSE(
|
||||
char * pbOutBuffer,
|
||||
int * pcbOutBuffer,
|
||||
char * pbInBuffer,
|
||||
int cbInBuffer,
|
||||
int * /* pCmpType */,
|
||||
int /* nCmpLevel */)
|
||||
void Compress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, 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(
|
||||
char * pbOutBuffer,
|
||||
int * pcbOutBuffer,
|
||||
char * pbInBuffer,
|
||||
int cbInBuffer,
|
||||
int * pCmpType,
|
||||
int nCmpLevel)
|
||||
static void Compress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||
{
|
||||
// Prepare the compression level for Huffmann compression,
|
||||
// which will be called as next step
|
||||
|
|
@ -622,12 +618,12 @@ static void Compress_ADPCM_mono(
|
|||
nCmpLevel = 5;
|
||||
*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;
|
||||
}
|
||||
|
||||
|
|
@ -637,13 +633,7 @@ static int Decompress_ADPCM_mono(char * pbOutBuffer, int * pcbOutBuffer, char *
|
|||
/* */
|
||||
/******************************************************************************/
|
||||
|
||||
static void Compress_ADPCM_stereo(
|
||||
char * pbOutBuffer,
|
||||
int * pcbOutBuffer,
|
||||
char * pbInBuffer,
|
||||
int cbInBuffer,
|
||||
int * pCmpType,
|
||||
int nCmpLevel)
|
||||
static void Compress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel)
|
||||
{
|
||||
// Prepare the compression level for Huffmann compression,
|
||||
// which will be called as next step
|
||||
|
|
@ -662,12 +652,12 @@ static void Compress_ADPCM_stereo(
|
|||
nCmpLevel = 5;
|
||||
*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;
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pbOutBuffer || !pbInBuffer)
|
||||
if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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(cbOutBuffer >= *pcbOutBuffer)
|
||||
{
|
||||
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
|
||||
memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
|
||||
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
|
||||
if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pbOutBuffer || !pbInBuffer)
|
||||
if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the input length is the same as output length, do nothing.
|
||||
cbOutBuffer = *pcbOutBuffer;
|
||||
if(cbInBuffer == cbOutBuffer)
|
||||
{
|
||||
// If the buffers are equal, don't copy anything.
|
||||
if(pbInBuffer == pbOutBuffer)
|
||||
if(pvInBuffer == pvOutBuffer)
|
||||
return 1;
|
||||
|
||||
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
|
||||
memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Perform decompression
|
||||
if(!Decompress_PKLIB(pbOutBuffer, &cbOutBuffer, pbInBuffer, cbInBuffer))
|
||||
if(!Decompress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer))
|
||||
{
|
||||
SetLastError(ERROR_FILE_CORRUPT);
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*pcbOutBuffer = cbOutBuffer;
|
||||
|
|
@ -758,29 +750,23 @@ int WINAPI SCompExplode(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffe
|
|||
|
||||
static TCompressTable cmp_table[] =
|
||||
{
|
||||
{MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse 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_HUFFMANN, Compress_huff}, // Huffmann compression
|
||||
{MPQ_COMPRESSION_ZLIB, Compress_ZLIB}, // Compression with the "zlib" library
|
||||
{MPQ_COMPRESSION_PKWARE, Compress_PKLIB}, // Compression with Pkware DCL
|
||||
{MPQ_COMPRESSION_BZIP2, Compress_BZIP2} // Compression Bzip2 library
|
||||
{MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse 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_HUFFMANN, Compress_huff}, // Huffmann compression
|
||||
{MPQ_COMPRESSION_ZLIB, Compress_ZLIB}, // Compression with the "zlib" library
|
||||
{MPQ_COMPRESSION_PKWARE, Compress_PKLIB}, // Compression with Pkware DCL
|
||||
{MPQ_COMPRESSION_BZIP2, Compress_BZIP2} // Compression Bzip2 library
|
||||
};
|
||||
|
||||
int WINAPI SCompCompress(
|
||||
char * pbOutBuffer,
|
||||
int * pcbOutBuffer,
|
||||
char * pbInBuffer,
|
||||
int cbInBuffer,
|
||||
unsigned uCompressionMask,
|
||||
int nCmpType,
|
||||
int nCmpLevel)
|
||||
int WINAPI SCompCompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel)
|
||||
{
|
||||
COMPRESS CompressFuncArray[0x10]; // Array of compression functions, applied sequentially
|
||||
unsigned char CompressByte[0x10]; // CompressByte for each method in the CompressFuncArray array
|
||||
char * pbWorkBuffer = NULL; // Temporary storage for decompressed data
|
||||
char * pbOutput = pbOutBuffer; // Current output buffer
|
||||
char * pbInput = pbInBuffer; // Current input buffer
|
||||
COMPRESS CompressFuncArray[0x10]; // Array of compression functions, applied sequentially
|
||||
unsigned char CompressByte[0x10]; // CompressByte for each method in the CompressFuncArray array
|
||||
unsigned char * pbWorkBuffer = NULL; // Temporary storage for decompressed data
|
||||
unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer;
|
||||
unsigned char * pbOutput = (unsigned char *)pvOutBuffer;// Current output buffer
|
||||
unsigned char * pbInput = (unsigned char *)pvInBuffer; // Current input buffer
|
||||
int nCompressCount = 0;
|
||||
int nCompressIndex = 0;
|
||||
int nAtLeastOneCompressionDone = 0;
|
||||
|
|
@ -789,7 +775,7 @@ int WINAPI SCompCompress(
|
|||
int nResult = 1;
|
||||
|
||||
// Check for valid parameters
|
||||
if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pbOutBuffer || !pbInBuffer)
|
||||
if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return 0;
|
||||
|
|
@ -838,7 +824,7 @@ int WINAPI SCompCompress(
|
|||
// If we need to do more than 1 compression, allocate intermediate buffer
|
||||
if(nCompressCount > 1)
|
||||
{
|
||||
pbWorkBuffer = STORM_ALLOC(char, *pcbOutBuffer);
|
||||
pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer);
|
||||
if(pbWorkBuffer == NULL)
|
||||
{
|
||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||
|
|
@ -884,12 +870,12 @@ int WINAPI SCompCompress(
|
|||
// If at least one compression succeeded, put the compression
|
||||
// mask to the begin of the output buffer
|
||||
if(nAtLeastOneCompressionDone)
|
||||
*pbOutBuffer = (char)uCompressionMask;
|
||||
*pbOutBuffer = (unsigned char)uCompressionMask;
|
||||
*pcbOutBuffer = cbOutBuffer + nAtLeastOneCompressionDone;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
|
||||
memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
|
||||
*pcbOutBuffer = cbInBuffer;
|
||||
}
|
||||
|
||||
|
|
@ -919,15 +905,13 @@ static TDecompressTable dcmp_table[] =
|
|||
{MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression
|
||||
};
|
||||
|
||||
int WINAPI SCompDecompress(
|
||||
char * pbOutBuffer,
|
||||
int * pcbOutBuffer,
|
||||
char * pbInBuffer,
|
||||
int cbInBuffer)
|
||||
int WINAPI SCompDecompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||
{
|
||||
char * pbWorkBuffer = NULL; // Temporary storage for decompressed data
|
||||
char * pbOutput = pbOutBuffer; // Where to store decompressed data
|
||||
char * pbInput; // Where to store decompressed data
|
||||
unsigned char * pbWorkBuffer = NULL;
|
||||
unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer;
|
||||
unsigned char * pbInBuffer = (unsigned char *)pvInBuffer;
|
||||
unsigned char * pbOutput = (unsigned char *)pvOutBuffer;
|
||||
unsigned char * pbInput;
|
||||
unsigned uCompressionMask; // Decompressions applied to the data
|
||||
unsigned uCompressionCopy; // Decompressions applied to the data
|
||||
int cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer
|
||||
|
|
@ -944,8 +928,8 @@ int WINAPI SCompDecompress(
|
|||
if(cbOutBuffer == cbInBuffer)
|
||||
{
|
||||
// If the buffers are equal, don't copy anything.
|
||||
if(pbInBuffer != pbOutBuffer)
|
||||
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
|
||||
if(pvInBuffer != pvOutBuffer)
|
||||
memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -981,7 +965,7 @@ int WINAPI SCompDecompress(
|
|||
// If there is more than one compression, we have to allocate extra buffer
|
||||
if(nCompressCount > 1)
|
||||
{
|
||||
pbWorkBuffer = STORM_ALLOC(char, cbOutBuffer);
|
||||
pbWorkBuffer = STORM_ALLOC(unsigned char, cbOutBuffer);
|
||||
if(pbWorkBuffer == NULL)
|
||||
{
|
||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||
|
|
@ -1027,15 +1011,12 @@ int WINAPI SCompDecompress(
|
|||
return nResult;
|
||||
}
|
||||
|
||||
int WINAPI SCompDecompress2(
|
||||
char * pbOutBuffer,
|
||||
int * pcbOutBuffer,
|
||||
char * pbInBuffer,
|
||||
int cbInBuffer)
|
||||
int WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer)
|
||||
{
|
||||
DECOMPRESS pfnDecompress1 = NULL;
|
||||
DECOMPRESS pfnDecompress2 = NULL;
|
||||
char * pbWorkBuffer = pbOutBuffer;
|
||||
unsigned char * pbWorkBuffer = (unsigned char *)pvOutBuffer;
|
||||
unsigned char * pbInBuffer = (unsigned char *)pvInBuffer;
|
||||
int cbWorkBuffer = *pcbOutBuffer;
|
||||
int nResult;
|
||||
char CompressionMethod;
|
||||
|
|
@ -1047,8 +1028,8 @@ int WINAPI SCompDecompress2(
|
|||
// If the outputbuffer is as big as input buffer, just copy the block
|
||||
if(*pcbOutBuffer == cbInBuffer)
|
||||
{
|
||||
if(pbOutBuffer != pbInBuffer)
|
||||
memcpy(pbOutBuffer, pbInBuffer, cbInBuffer);
|
||||
if(pvOutBuffer != pvInBuffer)
|
||||
memcpy(pvOutBuffer, pvInBuffer, cbInBuffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -1059,35 +1040,35 @@ int WINAPI SCompDecompress2(
|
|||
// We only recognize a fixed set of compression methods
|
||||
switch((unsigned char)CompressionMethod)
|
||||
{
|
||||
case MPQ_COMPRESSION_ZLIB:
|
||||
pfnDecompress1 = Decompress_ZLIB;
|
||||
break;
|
||||
case MPQ_COMPRESSION_ZLIB:
|
||||
pfnDecompress1 = Decompress_ZLIB;
|
||||
break;
|
||||
|
||||
case MPQ_COMPRESSION_PKWARE:
|
||||
pfnDecompress1 = Decompress_PKLIB;
|
||||
break;
|
||||
case MPQ_COMPRESSION_PKWARE:
|
||||
pfnDecompress1 = Decompress_PKLIB;
|
||||
break;
|
||||
|
||||
case MPQ_COMPRESSION_BZIP2:
|
||||
pfnDecompress1 = Decompress_BZIP2;
|
||||
break;
|
||||
case MPQ_COMPRESSION_BZIP2:
|
||||
pfnDecompress1 = Decompress_BZIP2;
|
||||
break;
|
||||
|
||||
case MPQ_COMPRESSION_LZMA:
|
||||
pfnDecompress1 = Decompress_LZMA;
|
||||
break;
|
||||
case MPQ_COMPRESSION_LZMA:
|
||||
pfnDecompress1 = Decompress_LZMA;
|
||||
break;
|
||||
|
||||
case MPQ_COMPRESSION_SPARSE:
|
||||
pfnDecompress1 = Decompress_SPARSE;
|
||||
break;
|
||||
case MPQ_COMPRESSION_SPARSE:
|
||||
pfnDecompress1 = Decompress_SPARSE;
|
||||
break;
|
||||
|
||||
case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB):
|
||||
pfnDecompress1 = Decompress_ZLIB;
|
||||
pfnDecompress2 = Decompress_SPARSE;
|
||||
break;
|
||||
case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB):
|
||||
pfnDecompress1 = Decompress_ZLIB;
|
||||
pfnDecompress2 = Decompress_SPARSE;
|
||||
break;
|
||||
|
||||
case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_BZIP2):
|
||||
pfnDecompress1 = Decompress_BZIP2;
|
||||
pfnDecompress2 = Decompress_SPARSE;
|
||||
break;
|
||||
case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_BZIP2):
|
||||
pfnDecompress1 = Decompress_BZIP2;
|
||||
pfnDecompress2 = Decompress_SPARSE;
|
||||
break;
|
||||
|
||||
//
|
||||
// Note: Any combination including MPQ_COMPRESSION_ADPCM_MONO,
|
||||
|
|
@ -1095,15 +1076,25 @@ int WINAPI SCompDecompress2(
|
|||
// is not supported by newer MPQs.
|
||||
//
|
||||
|
||||
default:
|
||||
SetLastError(ERROR_FILE_CORRUPT);
|
||||
return 0;
|
||||
case (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_HUFFMANN):
|
||||
pfnDecompress1 = Decompress_huff;
|
||||
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(pfnDecompress2 != NULL)
|
||||
{
|
||||
pbWorkBuffer = STORM_ALLOC(char, *pcbOutBuffer);
|
||||
pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer);
|
||||
if(pbWorkBuffer == NULL)
|
||||
{
|
||||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||
|
|
@ -1119,17 +1110,29 @@ int WINAPI SCompDecompress2(
|
|||
{
|
||||
cbInBuffer = cbWorkBuffer;
|
||||
cbWorkBuffer = *pcbOutBuffer;
|
||||
nResult = pfnDecompress2(pbOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer);
|
||||
nResult = pfnDecompress2(pvOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer);
|
||||
}
|
||||
|
||||
// Supply the output buffer size
|
||||
*pcbOutBuffer = cbWorkBuffer;
|
||||
|
||||
// Free temporary buffer
|
||||
if(pbWorkBuffer != pbOutBuffer)
|
||||
if(pbWorkBuffer != pvOutBuffer)
|
||||
STORM_FREE(pbWorkBuffer);
|
||||
|
||||
if(nResult == 0)
|
||||
SetLastError(ERROR_FILE_CORRUPT);
|
||||
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 */
|
||||
/* -------- ---- --- ------- */
|
||||
/* 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__
|
||||
|
|
@ -13,7 +14,17 @@
|
|||
#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_WAVE 0x45564157
|
||||
|
|
@ -39,36 +50,29 @@ typedef struct _WAVE_FILE_HEADER
|
|||
// Followed by "data" sub-chunk (we don't care)
|
||||
} WAVE_FILE_HEADER, *PWAVE_FILE_HEADER;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local variables
|
||||
|
||||
// Data compression for SFileAddFile
|
||||
// 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)
|
||||
static bool IsWaveFile_16BitsPerAdpcmSample(
|
||||
LPBYTE pbFileData,
|
||||
DWORD cbFileData,
|
||||
LPDWORD pdwChannels)
|
||||
{
|
||||
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))
|
||||
{
|
||||
// Check for the RIFF header
|
||||
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)
|
||||
{
|
||||
*pdwChannels = pWaveHdr->wChannels;
|
||||
return true;
|
||||
// Now the number of bits per sample must be at least 16.
|
||||
// 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;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MPQ write data functions
|
||||
|
||||
static int WriteDataToMpqFile(
|
||||
TMPQArchive * ha,
|
||||
TMPQFile * hf,
|
||||
LPBYTE pbFileData,
|
||||
DWORD dwDataSize,
|
||||
DWORD dwCompression)
|
||||
TMPQArchive * ha,
|
||||
TMPQFile * hf,
|
||||
LPBYTE pbFileData,
|
||||
DWORD dwDataSize,
|
||||
DWORD dwCompression)
|
||||
{
|
||||
TFileEntry * pFileEntry = hf->pFileEntry;
|
||||
ULONGLONG ByteOffset;
|
||||
LPBYTE pbCompressed = NULL; // Compressed (target) data
|
||||
LPBYTE pbToWrite = NULL; // Data to write to the file
|
||||
int nCompressionLevel = -1; // ADPCM compression level (only used for wave files)
|
||||
LPBYTE pbCompressed = NULL; // Compressed (target) data
|
||||
LPBYTE pbToWrite = hf->pbFileSector; // Data to write to the file
|
||||
int nCompressionLevel; // ADPCM compression level (only used for wave files)
|
||||
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
|
||||
assert(hf->dwFilePos + dwDataSize <= pFileEntry->dwFileSize);
|
||||
assert(hf->dwSectorCount != 0);
|
||||
assert(hf->pbFileSector != NULL);
|
||||
if((hf->dwFilePos + dwDataSize) > pFileEntry->dwFileSize)
|
||||
return ERROR_DISK_FULL;
|
||||
pbToWrite = hf->pbFileSector;
|
||||
|
||||
// Now write all data to the file sector buffer
|
||||
if(nError == ERROR_SUCCESS)
|
||||
|
|
@ -141,7 +141,7 @@ static int WriteDataToMpqFile(
|
|||
hf->dwCrc32 = crc32(hf->dwCrc32, hf->pbFileSector, dwBytesInSector);
|
||||
|
||||
// Compress the file sector, if needed
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED)
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK)
|
||||
{
|
||||
int nOutBuffer = (int)dwBytesInSector;
|
||||
int nInBuffer = (int)dwBytesInSector;
|
||||
|
|
@ -160,27 +160,33 @@ static int WriteDataToMpqFile(
|
|||
}
|
||||
|
||||
//
|
||||
// Note that both SCompImplode and SCompCompress give original buffer,
|
||||
// if they are unable to comperss the data.
|
||||
// Note that both SCompImplode and SCompCompress copy data as-is,
|
||||
// if they are unable to compress the data.
|
||||
//
|
||||
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
|
||||
{
|
||||
SCompImplode((char *)pbCompressed,
|
||||
&nOutBuffer,
|
||||
(char *)hf->pbFileSector,
|
||||
nInBuffer);
|
||||
SCompImplode(pbCompressed, &nOutBuffer, hf->pbFileSector, nInBuffer);
|
||||
}
|
||||
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
|
||||
{
|
||||
SCompCompress((char *)pbCompressed,
|
||||
&nOutBuffer,
|
||||
(char *)hf->pbFileSector,
|
||||
nInBuffer,
|
||||
(unsigned)dwCompression,
|
||||
0,
|
||||
nCompressionLevel);
|
||||
// If this is the first sector, we need to override the given compression
|
||||
// by the first sector compression. This is because the entire sector must
|
||||
// be compressed by the same compression.
|
||||
//
|
||||
// Test case:
|
||||
//
|
||||
// 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
|
||||
|
|
@ -209,8 +215,8 @@ static int WriteDataToMpqFile(
|
|||
}
|
||||
|
||||
// Call the compact callback, if any
|
||||
if(AddFileCB != NULL)
|
||||
AddFileCB(pvUserData, hf->dwFilePos, hf->dwDataSize, false);
|
||||
if(ha->pfnAddFileCB != NULL)
|
||||
ha->pfnAddFileCB(ha->pvAddFileUserData, hf->dwFilePos, hf->dwDataSize, false);
|
||||
|
||||
// Update the compressed file size
|
||||
pFileEntry->dwCmpSize += dwBytesInSector;
|
||||
|
|
@ -230,10 +236,10 @@ static int WriteDataToMpqFile(
|
|||
// Recrypts file data for file renaming
|
||||
|
||||
static int RecryptFileData(
|
||||
TMPQArchive * ha,
|
||||
TMPQFile * hf,
|
||||
const char * szFileName,
|
||||
const char * szNewFileName)
|
||||
TMPQArchive * ha,
|
||||
TMPQFile * hf,
|
||||
const char * szFileName,
|
||||
const char * szNewFileName)
|
||||
{
|
||||
ULONGLONG RawFilePos;
|
||||
TFileEntry * pFileEntry = hf->pFileEntry;
|
||||
|
|
@ -246,8 +252,8 @@ static int RecryptFileData(
|
|||
assert(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED);
|
||||
|
||||
// File decryption key is calculated from the plain name
|
||||
szNewFileName = GetPlainFileNameA(szNewFileName);
|
||||
szFileName = GetPlainFileNameA(szFileName);
|
||||
szNewFileName = GetPlainFileName(szNewFileName);
|
||||
szFileName = GetPlainFileName(szFileName);
|
||||
|
||||
// Calculate both file keys
|
||||
dwOldKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags);
|
||||
|
|
@ -277,7 +283,7 @@ static int RecryptFileData(
|
|||
if(hf->SectorOffsets != NULL)
|
||||
{
|
||||
// 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];
|
||||
|
||||
if(SectorOffsetsCopy == NULL)
|
||||
|
|
@ -351,13 +357,13 @@ static int RecryptFileData(
|
|||
// Support functions for adding files to the MPQ
|
||||
|
||||
int SFileAddFile_Init(
|
||||
TMPQArchive * ha,
|
||||
const char * szFileName,
|
||||
ULONGLONG FileTime,
|
||||
DWORD dwFileSize,
|
||||
LCID lcLocale,
|
||||
DWORD dwFlags,
|
||||
TMPQFile ** phf)
|
||||
TMPQArchive * ha,
|
||||
const char * szFileName,
|
||||
ULONGLONG FileTime,
|
||||
DWORD dwFileSize,
|
||||
LCID lcLocale,
|
||||
DWORD dwFlags,
|
||||
TMPQFile ** phf)
|
||||
{
|
||||
TFileEntry * pFileEntry = NULL;
|
||||
ULONGLONG TempPos; // For various file offset calculations
|
||||
|
|
@ -375,7 +381,7 @@ int SFileAddFile_Init(
|
|||
dwFlags &= ~MPQ_FILE_SECTOR_CRC;
|
||||
|
||||
// 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;
|
||||
|
||||
// Fix Key is not allowed if the file is not enrypted
|
||||
|
|
@ -388,21 +394,18 @@ int SFileAddFile_Init(
|
|||
lcLocale = 0;
|
||||
|
||||
// Allocate the TMPQFile entry for newly added file
|
||||
hf = CreateMpqFile(ha);
|
||||
hf = CreateFileHandle(ha, NULL);
|
||||
if(hf == NULL)
|
||||
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)
|
||||
{
|
||||
// Find the position where the file will be stored
|
||||
FindFreeMpqSpace(ha, &hf->MpqFilePos);
|
||||
hf->MpqFilePos = FindFreeMpqSpace(ha);
|
||||
hf->RawFilePos = ha->MpqPos + hf->MpqFilePos;
|
||||
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
|
||||
if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1)
|
||||
{
|
||||
|
|
@ -428,35 +431,32 @@ int SFileAddFile_Init(
|
|||
}
|
||||
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)
|
||||
nError = ERROR_ALREADY_EXISTS;
|
||||
|
||||
// If the file entry already contains a file
|
||||
// and it is a pseudo-name, replace it
|
||||
// When replacing an existing file,
|
||||
// we still need to invalidate the (attributes) file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
AllocateFileName(pFileEntry, szFileName);
|
||||
}
|
||||
InvalidateInternalFiles(ha);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Fill the file entry and TMPQFile structure
|
||||
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
|
||||
hf->pFileEntry = pFileEntry;
|
||||
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
|
||||
pFileEntry->ByteOffset = hf->MpqFilePos;
|
||||
pFileEntry->dwFileSize = dwFileSize;
|
||||
|
|
@ -473,14 +473,20 @@ int SFileAddFile_Init(
|
|||
// If the caller gave us a file time, use it.
|
||||
pFileEntry->FileTime = FileTime;
|
||||
|
||||
// Mark the archive as modified
|
||||
ha->dwFlags |= MPQ_FLAG_CHANGED;
|
||||
|
||||
// Call the callback, if needed
|
||||
if(AddFileCB != NULL)
|
||||
AddFileCB(pvUserData, 0, hf->dwDataSize, false);
|
||||
if(ha->pfnAddFileCB != NULL)
|
||||
ha->pfnAddFileCB(ha->pvAddFileUserData, 0, hf->dwDataSize, false);
|
||||
hf->nAddFileError = ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// If an error occured, remember it
|
||||
if(nError != ERROR_SUCCESS)
|
||||
hf->bErrorOccured = true;
|
||||
// Fre the file handle if failed
|
||||
if(nError != ERROR_SUCCESS && hf != NULL)
|
||||
FreeFileHandle(hf);
|
||||
|
||||
// Give the handle to the caller
|
||||
*phf = hf;
|
||||
return nError;
|
||||
}
|
||||
|
|
@ -505,12 +511,9 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d
|
|||
ULONGLONG RawFilePos = hf->RawFilePos;
|
||||
|
||||
// Allocate buffer for file sector
|
||||
nError = AllocateSectorBuffer(hf);
|
||||
hf->nAddFileError = nError = AllocateSectorBuffer(hf);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
hf->bErrorOccured = true;
|
||||
return nError;
|
||||
}
|
||||
|
||||
// Allocate patch info, if the data is patch
|
||||
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;
|
||||
|
||||
// Allocate the patch info
|
||||
nError = AllocatePatchInfo(hf, false);
|
||||
hf->nAddFileError = nError = AllocatePatchInfo(hf, false);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
hf->bErrorOccured = true;
|
||||
return nError;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate sector offsets
|
||||
if(hf->SectorOffsets == NULL)
|
||||
{
|
||||
nError = AllocateSectorOffsets(hf, false);
|
||||
hf->nAddFileError = nError = AllocateSectorOffsets(hf, false);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
hf->bErrorOccured = true;
|
||||
return nError;
|
||||
}
|
||||
}
|
||||
|
||||
// Create array of sector checksums
|
||||
if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC))
|
||||
{
|
||||
nError = AllocateSectorChecksums(hf, false);
|
||||
hf->nAddFileError = nError = AllocateSectorChecksums(hf, false);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
hf->bErrorOccured = true;
|
||||
return nError;
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-save the patch info, if any
|
||||
|
|
@ -574,7 +568,16 @@ int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD d
|
|||
|
||||
// Write the MPQ data to the file
|
||||
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);
|
||||
}
|
||||
|
||||
// If it succeeded and we wrote all the file data,
|
||||
// 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)
|
||||
{
|
||||
nError = WriteSectorChecksums(hf);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
hf->bErrorOccured = true;
|
||||
}
|
||||
|
||||
// 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->pFileEntry->dwFileSize = hf->dwPatchedFileSize;
|
||||
nError = WritePatchInfo(hf);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
hf->bErrorOccured = true;
|
||||
}
|
||||
|
||||
// Now write sector offsets to the file
|
||||
if(hf->SectorOffsets != NULL)
|
||||
{
|
||||
nError = WriteSectorOffsets(hf);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
hf->bErrorOccured = true;
|
||||
}
|
||||
|
||||
// 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,
|
||||
hf->pFileEntry->dwCmpSize,
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -639,51 +632,47 @@ int SFileAddFile_Finish(TMPQFile * hf)
|
|||
{
|
||||
TMPQArchive * ha = hf->ha;
|
||||
TFileEntry * pFileEntry = hf->pFileEntry;
|
||||
int nError = ERROR_SUCCESS;
|
||||
int nError = hf->nAddFileError;
|
||||
|
||||
// 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
|
||||
if(hf->pPatchInfo == NULL)
|
||||
{
|
||||
assert(pFileEntry != NULL);
|
||||
if(hf->dwFilePos != pFileEntry->dwFileSize)
|
||||
{
|
||||
nError = ERROR_CAN_NOT_COMPLETE;
|
||||
hf->bErrorOccured = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(hf->dwFilePos != hf->pPatchInfo->dwDataSize)
|
||||
{
|
||||
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
|
||||
if(AddFileCB != NULL)
|
||||
AddFileCB(pvUserData, hf->dwDataSize, hf->dwDataSize, true);
|
||||
|
||||
// Update the size of the block table
|
||||
ha->pHeader->dwBlockTableSize = ha->dwFileTableSize;
|
||||
if(ha->pfnAddFileCB != NULL)
|
||||
ha->pfnAddFileCB(ha->pvAddFileUserData, hf->dwDataSize, hf->dwDataSize, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Free the file entry in MPQ tables
|
||||
if(pFileEntry != NULL)
|
||||
FreeFileEntry(ha, pFileEntry);
|
||||
DeleteFileEntry(ha, pFileEntry);
|
||||
}
|
||||
|
||||
// Clear the add file callback
|
||||
FreeMPQFile(hf);
|
||||
pvUserData = NULL;
|
||||
AddFileCB = NULL;
|
||||
FreeFileHandle(hf);
|
||||
return nError;
|
||||
}
|
||||
|
||||
|
|
@ -691,19 +680,19 @@ int SFileAddFile_Finish(TMPQFile * hf)
|
|||
// Adds data as file to the archive
|
||||
|
||||
bool WINAPI SFileCreateFile(
|
||||
HANDLE hMpq,
|
||||
const char * szArchivedName,
|
||||
ULONGLONG FileTime,
|
||||
DWORD dwFileSize,
|
||||
LCID lcLocale,
|
||||
DWORD dwFlags,
|
||||
HANDLE * phFile)
|
||||
HANDLE hMpq,
|
||||
const char * szArchivedName,
|
||||
ULONGLONG FileTime,
|
||||
DWORD dwFileSize,
|
||||
LCID lcLocale,
|
||||
DWORD dwFlags,
|
||||
HANDLE * phFile)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Check valid parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(szArchivedName == NULL || *szArchivedName == 0)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
|
@ -711,16 +700,19 @@ bool WINAPI SFileCreateFile(
|
|||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Don't allow to add file if the MPQ is open for read only
|
||||
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
||||
nError = ERROR_ACCESS_DENIED;
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
||||
nError = ERROR_ACCESS_DENIED;
|
||||
|
||||
// Don't allow to add a file under pseudo-file name
|
||||
if(IsPseudoFileName(szArchivedName, NULL))
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
// Don't allow to add a file under pseudo-file name
|
||||
if(IsPseudoFileName(szArchivedName, NULL))
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Don't allow to add any of the internal files
|
||||
if(IsInternalMpqFileName(szArchivedName))
|
||||
nError = ERROR_INTERNAL_FILE;
|
||||
// Don't allow to add any of the internal files
|
||||
if(IsInternalMpqFileName(szArchivedName))
|
||||
nError = ERROR_INTERNAL_FILE;
|
||||
}
|
||||
|
||||
// Perform validity check of the MPQ flags
|
||||
if(nError == ERROR_SUCCESS)
|
||||
|
|
@ -733,17 +725,18 @@ bool WINAPI SFileCreateFile(
|
|||
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)
|
||||
{
|
||||
// Invalidate the entries for (listfile) and (attributes)
|
||||
// After we are done with MPQ changes, we need to re-create them anyway
|
||||
InvalidateInternalFiles(ha);
|
||||
|
||||
// Initiate the add file operation
|
||||
nError = SFileAddFile_Init(ha, szArchivedName, FileTime, dwFileSize, lcLocale, dwFlags, (TMPQFile **)phFile);
|
||||
if(ha->dwFileTableSize > ha->dwMaxFileCount)
|
||||
nError = ERROR_DISK_FULL;
|
||||
}
|
||||
|
||||
// Initiate the add file operation
|
||||
if(nError == ERROR_SUCCESS)
|
||||
nError = SFileAddFile_Init(ha, szArchivedName, FileTime, dwFileSize, lcLocale, dwFlags, (TMPQFile **)phFile);
|
||||
|
||||
// Deal with the errors
|
||||
if(nError != ERROR_SUCCESS)
|
||||
SetLastError(nError);
|
||||
|
|
@ -751,16 +744,16 @@ bool WINAPI SFileCreateFile(
|
|||
}
|
||||
|
||||
bool WINAPI SFileWriteFile(
|
||||
HANDLE hFile,
|
||||
const void * pvData,
|
||||
DWORD dwSize,
|
||||
DWORD dwCompression)
|
||||
HANDLE hFile,
|
||||
const void * pvData,
|
||||
DWORD dwSize,
|
||||
DWORD dwCompression)
|
||||
{
|
||||
TMPQFile * hf = (TMPQFile *)hFile;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Check the proper parameters
|
||||
if(!IsValidFileHandle(hf))
|
||||
if(!IsValidFileHandle(hFile))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(hf->bIsWriteHandle == false)
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
|
|
@ -774,14 +767,14 @@ bool WINAPI SFileWriteFile(
|
|||
// the calling application must ensure that such flag combination doesn't get here
|
||||
//
|
||||
|
||||
// if(dwFlags & MPQ_FILE_IMPLODE)
|
||||
// nError = ERROR_INVALID_PARAMETER;
|
||||
//
|
||||
// if(dwFlags & MPQ_FILE_ENCRYPTED)
|
||||
// nError = ERROR_INVALID_PARAMETER;
|
||||
// if(dwFlags & MPQ_FILE_IMPLODE)
|
||||
// nError = ERROR_INVALID_PARAMETER;
|
||||
//
|
||||
// if(dwFlags & MPQ_FILE_ENCRYPTED)
|
||||
// nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Lossy compression is not allowed on single unit files
|
||||
if(dwCompression & LOSSY_COMPRESSION_MASK)
|
||||
if(dwCompression & MPQ_LOSSY_COMPRESSION_MASK)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
|
|
@ -802,7 +795,7 @@ bool WINAPI SFileFinishFile(HANDLE hFile)
|
|||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Check the proper parameters
|
||||
if(!IsValidFileHandle(hf))
|
||||
if(!IsValidFileHandle(hFile))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(hf->bIsWriteHandle == false)
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
|
|
@ -821,12 +814,12 @@ bool WINAPI SFileFinishFile(HANDLE hFile)
|
|||
// Adds a file to the archive
|
||||
|
||||
bool WINAPI SFileAddFileEx(
|
||||
HANDLE hMpq,
|
||||
const TCHAR * szFileName,
|
||||
const char * szArchivedName,
|
||||
DWORD dwFlags,
|
||||
DWORD dwCompression, // Compression of the first sector
|
||||
DWORD dwCompressionNext) // Compression of next sectors
|
||||
HANDLE hMpq,
|
||||
const TCHAR * szFileName,
|
||||
const char * szArchivedName,
|
||||
DWORD dwFlags,
|
||||
DWORD dwCompression, // Compression of the first sector
|
||||
DWORD dwCompressionNext) // Compression of next sectors
|
||||
{
|
||||
ULONGLONG FileSize = 0;
|
||||
ULONGLONG FileTime = 0;
|
||||
|
|
@ -842,13 +835,13 @@ bool WINAPI SFileAddFileEx(
|
|||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Check parameters
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
if(hMpq == NULL || szFileName == NULL || *szFileName == 0)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Open added file
|
||||
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)
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
|
@ -885,10 +878,13 @@ bool WINAPI SFileAddFileEx(
|
|||
// that the first sector is not compressed with lossy compression
|
||||
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))
|
||||
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);
|
||||
bIsAdpcmCompression = true;
|
||||
}
|
||||
|
|
@ -916,15 +912,19 @@ bool WINAPI SFileAddFileEx(
|
|||
// If the file being added is a WAVE file, we check number of channels
|
||||
if(bIsFirstSector && bIsAdpcmCompression)
|
||||
{
|
||||
// The file must really be a wave file, otherwise it's data corruption
|
||||
if(!IsWaveFile(pbFileData, dwBytesToRead, &dwChannels))
|
||||
// The file must really be a WAVE file with at least 16 bits per sample,
|
||||
// otherwise the ADPCM compression will corrupt it
|
||||
if(IsWaveFile_16BitsPerAdpcmSample(pbFileData, dwBytesToRead, &dwChannels))
|
||||
{
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
break;
|
||||
// Setup the compression of next sectors according to number of channels
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -988,20 +988,20 @@ bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szA
|
|||
// Convert quality to data compression
|
||||
switch(dwQuality)
|
||||
{
|
||||
case MPQ_WAVE_QUALITY_HIGH:
|
||||
// WaveCompressionLevel = -1;
|
||||
dwCompression = MPQ_COMPRESSION_PKWARE;
|
||||
break;
|
||||
case MPQ_WAVE_QUALITY_HIGH:
|
||||
// WaveCompressionLevel = -1;
|
||||
dwCompression = MPQ_COMPRESSION_PKWARE;
|
||||
break;
|
||||
|
||||
case MPQ_WAVE_QUALITY_MEDIUM:
|
||||
// WaveCompressionLevel = 4;
|
||||
dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN;
|
||||
break;
|
||||
case MPQ_WAVE_QUALITY_MEDIUM:
|
||||
// WaveCompressionLevel = 4;
|
||||
dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN;
|
||||
break;
|
||||
|
||||
case MPQ_WAVE_QUALITY_LOW:
|
||||
// WaveCompressionLevel = 2;
|
||||
dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN;
|
||||
break;
|
||||
case MPQ_WAVE_QUALITY_LOW:
|
||||
// WaveCompressionLevel = 2;
|
||||
dwCompression = MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN;
|
||||
break;
|
||||
}
|
||||
|
||||
return SFileAddFileEx(hMpq,
|
||||
|
|
@ -1032,7 +1032,7 @@ bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearch
|
|||
// Check the parameters
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
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
|
||||
InvalidateInternalFiles(ha);
|
||||
|
||||
// Mark the file entry as free
|
||||
nError = FreeFileEntry(ha, pFileEntry);
|
||||
// Delete the file entry in the file table and defragment the file table
|
||||
DeleteFileEntry(ha, pFileEntry);
|
||||
|
||||
// We also need to rebuild the HET table, if present
|
||||
if(ha->pHetTable != NULL)
|
||||
nError = RebuildHetTable(ha);
|
||||
}
|
||||
|
||||
// Resolve error and exit
|
||||
|
|
@ -1097,7 +1101,7 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s
|
|||
// Test the valid parameters
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(szFileName == NULL || *szFileName == 0 || szNewFileName == NULL || *szNewFileName == 0)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
|
@ -1144,6 +1148,10 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s
|
|||
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
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
|
|
@ -1151,12 +1159,10 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s
|
|||
// with the new decryption key
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
|
||||
{
|
||||
hf = CreateMpqFile(ha);
|
||||
hf = CreateFileHandle(ha, pFileEntry);
|
||||
if(hf != NULL)
|
||||
{
|
||||
// Recrypt the file data in the MPQ
|
||||
hf->pFileEntry = pFileEntry;
|
||||
hf->dwDataSize = pFileEntry->dwFileSize;
|
||||
nError = RecryptFileData(ha, hf, szFileName, szNewFileName);
|
||||
|
||||
// Update the MD5
|
||||
|
|
@ -1169,7 +1175,7 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s
|
|||
ha->pHeader->dwRawChunkSize);
|
||||
}
|
||||
|
||||
FreeMPQFile(hf);
|
||||
FreeFileHandle(hf);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1178,9 +1184,8 @@ bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * s
|
|||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Note: MPQ_FLAG_CHANGED is set by RenameFileEntry
|
||||
//
|
||||
// assert((ha->dwFlags & MPQ_FLAG_CHANGED) != 0);
|
||||
|
||||
// Resolve error and return
|
||||
if(nError != ERROR_SUCCESS)
|
||||
|
|
@ -1215,7 +1220,7 @@ bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale)
|
|||
TMPQFile * hf = (TMPQFile *)hFile;
|
||||
|
||||
// Invalid handle => do nothing
|
||||
if(!IsValidFileHandle(hf))
|
||||
if(!IsValidFileHandle(hFile))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
|
|
@ -1278,9 +1283,17 @@ bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale)
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
AddFileCB = aAddFileCB;
|
||||
TMPQArchive * ha = (TMPQArchive *) hMpq;
|
||||
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
ha->pvAddFileUserData = pvUserData;
|
||||
ha->pfnAddFileCB = AddFileCB;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,346 +24,450 @@ typedef struct _MPQ_ATTRIBUTES_HEADER
|
|||
// Followed by an array of file times
|
||||
// Followed by an array of MD5
|
||||
// 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;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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)
|
||||
|
||||
int SAttrLoadAttributes(TMPQArchive * ha)
|
||||
{
|
||||
MPQ_ATTRIBUTES_HEADER AttrHeader;
|
||||
HANDLE hFile = NULL;
|
||||
DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize;
|
||||
DWORD dwArraySize;
|
||||
LPBYTE pbAttrFile;
|
||||
DWORD dwBytesRead;
|
||||
DWORD i;
|
||||
int nError = ERROR_SUCCESS;
|
||||
DWORD cbAttrFile = 0;
|
||||
int nError = ERROR_FILE_CORRUPT;
|
||||
|
||||
// File table must be initialized
|
||||
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.
|
||||
// If it's not there, then the archive doesn't support attributes
|
||||
if(SFileOpenFileEx((HANDLE)ha, ATTRIBUTES_NAME, SFILE_OPEN_ANY_LOCALE, &hFile))
|
||||
{
|
||||
// Load the content of the attributes file
|
||||
SFileReadFile(hFile, &AttrHeader, sizeof(MPQ_ATTRIBUTES_HEADER), &dwBytesRead, NULL);
|
||||
if(dwBytesRead != sizeof(MPQ_ATTRIBUTES_HEADER))
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
// Retrieve and check size of the (attributes) file
|
||||
cbAttrFile = SFileGetFileSize(hFile, NULL);
|
||||
|
||||
// Verify the header of the (attributes) file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
// Size of the (attributes) might be 1 byte less than expected
|
||||
// See GetSizeOfAttributesFile for more info
|
||||
pbAttrFile = STORM_ALLOC(BYTE, cbAttrFile + 1);
|
||||
if(pbAttrFile != NULL)
|
||||
{
|
||||
AttrHeader.dwVersion = BSWAP_INT32_UNSIGNED(AttrHeader.dwVersion);
|
||||
AttrHeader.dwFlags = BSWAP_INT32_UNSIGNED(AttrHeader.dwFlags);
|
||||
ha->dwAttrFlags = AttrHeader.dwFlags;
|
||||
if(dwBytesRead != sizeof(MPQ_ATTRIBUTES_HEADER))
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
// Set the last byte to 0 in case the size should be 1 byte greater
|
||||
pbAttrFile[cbAttrFile] = 0;
|
||||
|
||||
// Load the entire file to memory
|
||||
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
|
||||
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
|
||||
// Close the attributes file
|
||||
SFileCloseFile(hFile);
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
// Saves the (attributes) to the MPQ
|
||||
int SAttrFileSaveToMpq(TMPQArchive * ha)
|
||||
{
|
||||
MPQ_ATTRIBUTES_HEADER AttrHeader;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQFile * hf = NULL;
|
||||
DWORD dwFinalBlockTableSize = ha->dwFileTableSize;
|
||||
DWORD dwFileSize = 0;
|
||||
DWORD dwToWrite;
|
||||
DWORD i;
|
||||
LPBYTE pbAttrFile;
|
||||
DWORD cbAttrFile = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Now we have to check if we need patch bits in the (attributes)
|
||||
if(nError == ERROR_SUCCESS)
|
||||
// Only save the attributes if we should do so
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
// Write the content of the attributes file to the MPQ
|
||||
nError = SFileAddFile_Write(hf, pbAttrFile, cbAttrFile, MPQ_COMPRESSION_ZLIB);
|
||||
SFileAddFile_Finish(hf);
|
||||
}
|
||||
|
||||
nError = SFileAddFile_Write(hf, pbBitArray, dwByteSize, MPQ_COMPRESSION_ZLIB);
|
||||
STORM_FREE(pbBitArray);
|
||||
// Free the attributes buffer
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -375,7 +479,7 @@ DWORD WINAPI SFileGetAttributes(HANDLE hMpq)
|
|||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
|
||||
// Verify the parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return SFILE_INVALID_ATTRIBUTES;
|
||||
|
|
@ -389,7 +493,7 @@ bool WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags)
|
|||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
|
||||
// Verify the parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return false;
|
||||
|
|
@ -434,12 +538,12 @@ bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName)
|
|||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// Get the file size
|
||||
hf = (TMPQFile *)hFile;
|
||||
SFileGetFileInfo(hFile, SFILE_INFO_FILE_SIZE, &dwTotalBytes, sizeof(DWORD), NULL);
|
||||
dwTotalBytes = hf->pFileEntry->dwFileSize;
|
||||
|
||||
// Initialize the CRC32 and MD5 contexts
|
||||
md5_init(&md5_state);
|
||||
|
|
|
|||
|
|
@ -7,26 +7,47 @@
|
|||
/* -------- ---- --- ------- */
|
||||
/* 14.04.03 1.00 Lad Splitted from SFileCreateArchiveEx.cpp */
|
||||
/* 19.11.03 1.01 Dan Big endian handling */
|
||||
/* 21.04.13 1.02 Dea Compact callback now part of TMPQArchive */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define __STORMLIB_SELF__
|
||||
#include "StormLib.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 */
|
||||
/*****************************************************************************/
|
||||
|
||||
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 * pFileEntry;
|
||||
|
|
@ -34,11 +55,11 @@ static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, LPDWO
|
|||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Add the listfile to the MPQ
|
||||
if(nError == ERROR_SUCCESS && szListFile != NULL)
|
||||
if(szListFile != NULL)
|
||||
{
|
||||
// Notify the user
|
||||
if(CompactCB != NULL)
|
||||
CompactCB(pvUserData, CCB_CHECKING_FILES, CompactBytesProcessed, CompactTotalBytes);
|
||||
if(ha->pfnCompactCB != NULL)
|
||||
ha->pfnCompactCB(ha->pvCompactUserData, CCB_CHECKING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||
|
||||
nError = SFileAddListFile((HANDLE)ha, szListFile);
|
||||
}
|
||||
|
|
@ -49,30 +70,49 @@ static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, LPDWO
|
|||
pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++, dwBlockIndex++)
|
||||
{
|
||||
// If the file exists and it's encrypted
|
||||
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))
|
||||
{
|
||||
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
|
||||
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
|
||||
// Create file handle where we load the sector offset table
|
||||
hf = CreateFileHandle(ha, pFileEntry);
|
||||
if(hf != NULL)
|
||||
{
|
||||
dwFileKey = DecryptFileKey(pFileEntry->szFileName,
|
||||
pFileEntry->ByteOffset,
|
||||
pFileEntry->dwFileSize,
|
||||
pFileEntry->dwFlags);
|
||||
// Call one dummy load of the first 4 bytes.
|
||||
// This enforces loading all buffers and also detecting of the decryption key
|
||||
SFileReadFile((HANDLE)hf, FileData, sizeof(FileData), &dwBytesRead, NULL);
|
||||
pFileKeys[dwBlockIndex] = hf->dwFileKey;
|
||||
FreeFileHandle(hf);
|
||||
}
|
||||
|
||||
// Give the key to the caller
|
||||
if(pFileKeys != NULL)
|
||||
pFileKeys[dwBlockIndex] = dwFileKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
nError = ERROR_CAN_NOT_COMPLETE;
|
||||
break;
|
||||
// If we succeeded in reading 16 bytes from the file,
|
||||
// we also know the encryption key
|
||||
if(dwBytesRead == sizeof(FileData))
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
// 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(
|
||||
TFileStream * pSrcStream,
|
||||
TFileStream * pTrgStream,
|
||||
ULONGLONG & ByteOffset,
|
||||
ULONGLONG & ByteCount)
|
||||
TMPQArchive * ha,
|
||||
TFileStream * pSrcStream,
|
||||
TFileStream * pTrgStream,
|
||||
ULONGLONG & ByteOffset,
|
||||
ULONGLONG & ByteCount)
|
||||
{
|
||||
ULONGLONG DataSize = ByteCount;
|
||||
DWORD dwToRead;
|
||||
|
|
@ -114,10 +155,10 @@ static int CopyNonMpqData(
|
|||
}
|
||||
|
||||
// Update the progress
|
||||
if(CompactCB != NULL)
|
||||
if(ha->pfnCompactCB != NULL)
|
||||
{
|
||||
CompactBytesProcessed += dwToRead;
|
||||
CompactCB(pvUserData, CCB_COPYING_NON_MPQ_DATA, CompactBytesProcessed, CompactTotalBytes);
|
||||
ha->CompactBytesProcessed += dwToRead;
|
||||
ha->pfnCompactCB(ha->pvCompactUserData, CCB_COPYING_NON_MPQ_DATA, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||
}
|
||||
|
||||
// Decrement the number of data to be copied
|
||||
|
|
@ -125,18 +166,18 @@ static int CopyNonMpqData(
|
|||
DataSize -= dwToRead;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
return nError;
|
||||
}
|
||||
|
||||
// Copies all file sectors into another archive.
|
||||
static int CopyMpqFileSectors(
|
||||
TMPQArchive * ha,
|
||||
TMPQFile * hf,
|
||||
TFileStream * pNewStream)
|
||||
TMPQArchive * ha,
|
||||
TMPQFile * hf,
|
||||
TFileStream * pNewStream,
|
||||
ULONGLONG MpqFilePos) // MPQ file position in the new archive
|
||||
{
|
||||
TFileEntry * pFileEntry = hf->pFileEntry;
|
||||
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 dwPatchSize = 0; // Size of patch header
|
||||
DWORD dwFileKey1 = 0; // File key used for decryption
|
||||
|
|
@ -144,10 +185,6 @@ static int CopyMpqFileSectors(
|
|||
DWORD dwCmpSize = 0; // Compressed file size, including patch header
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Remember the position in the destination file
|
||||
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
|
||||
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(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];
|
||||
|
||||
assert((pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) == 0);
|
||||
assert(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED);
|
||||
assert(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK);
|
||||
|
||||
if(SectorOffsetsCopy == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
|
@ -199,10 +236,10 @@ static int CopyMpqFileSectors(
|
|||
}
|
||||
|
||||
// Update compact progress
|
||||
if(CompactCB != NULL)
|
||||
if(ha->pfnCompactCB != NULL)
|
||||
{
|
||||
CompactBytesProcessed += dwSectorOffsLen;
|
||||
CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes);
|
||||
ha->CompactBytesProcessed += dwSectorOffsLen;
|
||||
ha->pfnCompactCB(ha->pvCompactUserData, CCB_COMPACTING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||
}
|
||||
|
||||
STORM_FREE(SectorOffsetsCopy);
|
||||
|
|
@ -257,10 +294,10 @@ static int CopyMpqFileSectors(
|
|||
}
|
||||
|
||||
// Update compact progress
|
||||
if(CompactCB != NULL)
|
||||
if(ha->pfnCompactCB != NULL)
|
||||
{
|
||||
CompactBytesProcessed += dwRawDataInSector;
|
||||
CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes);
|
||||
ha->CompactBytesProcessed += dwRawDataInSector;
|
||||
ha->pfnCompactCB(ha->pvCompactUserData, CCB_COMPACTING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||
}
|
||||
|
||||
// Adjust byte counts
|
||||
|
|
@ -285,10 +322,10 @@ static int CopyMpqFileSectors(
|
|||
nError = GetLastError();
|
||||
|
||||
// Update compact progress
|
||||
if(CompactCB != NULL)
|
||||
if(ha->pfnCompactCB != NULL)
|
||||
{
|
||||
CompactBytesProcessed += dwCrcLength;
|
||||
CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes);
|
||||
ha->CompactBytesProcessed += dwCrcLength;
|
||||
ha->pfnCompactCB(ha->pvCompactUserData, CCB_COMPACTING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||
}
|
||||
|
||||
// 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
|
||||
dwCmpSize += dwBytesToCopy;
|
||||
dwBytesToCopy = 0;
|
||||
STORM_FREE(pbExtraData);
|
||||
}
|
||||
else
|
||||
|
|
@ -334,30 +370,24 @@ static int CopyMpqFileSectors(
|
|||
ha->pHeader->dwRawChunkSize);
|
||||
}
|
||||
|
||||
// Update file position in the block table
|
||||
// Verify the number of bytes written
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// At this point, number of bytes written should be exactly
|
||||
// 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"
|
||||
// seems to have improper compressed size. Instead of real compressed size,
|
||||
// the "dwCmpSize" member of the block table entry contains
|
||||
// uncompressed size of file data + size of the sector table.
|
||||
// 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
|
||||
// into compressed size
|
||||
//
|
||||
|
||||
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
|
||||
if(!(dwCmpSize <= pFileEntry->dwCmpSize && pFileEntry->dwCmpSize <= dwCmpSize + dwPatchSize))
|
||||
{
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
assert(false);
|
||||
|
|
@ -372,6 +402,7 @@ static int CopyMpqFiles(TMPQArchive * ha, LPDWORD pFileKeys, TFileStream * pNewS
|
|||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQFile * hf = NULL;
|
||||
ULONGLONG MpqFilePos;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// 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
|
||||
// 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
|
||||
hf = CreateMpqFile(ha);
|
||||
if(hf == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
// Query the position where the destination file will be
|
||||
FileStream_GetPos(pNewStream, &MpqFilePos);
|
||||
MpqFilePos = MpqFilePos - ha->MpqPos;
|
||||
|
||||
// Store file entry
|
||||
hf->pFileEntry = pFileEntry;
|
||||
|
||||
// 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)
|
||||
// Perform file copy ONLY if the file has nonzero size
|
||||
if(pFileEntry->dwFileSize != 0)
|
||||
{
|
||||
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)
|
||||
break;
|
||||
}
|
||||
|
||||
// Allocate buffers for file sector and sector offset table
|
||||
nError = AllocateSectorBuffer(hf);
|
||||
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);
|
||||
// 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)
|
||||
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
|
||||
nError = CopyMpqFileSectors(ha, hf, pNewStream);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Free buffers. This also sets "hf" to NULL.
|
||||
FreeMPQFile(hf);
|
||||
// Note: DO NOT update the compressed size in the file entry, no matter how bad it is.
|
||||
pFileEntry->ByteOffset = MpqFilePos;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup and exit
|
||||
if(hf != NULL)
|
||||
FreeMPQFile(hf);
|
||||
FreeFileHandle(hf);
|
||||
return nError;
|
||||
}
|
||||
|
||||
|
|
@ -444,10 +478,18 @@ static int CopyMpqFiles(TMPQArchive * ha, LPDWORD pFileKeys, TFileStream * pNewS
|
|||
/* 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;
|
||||
pvUserData = pvData;
|
||||
TMPQArchive * ha = (TMPQArchive *) hMpq;
|
||||
|
||||
if (!IsValidMpqHandle(hMpq))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
ha->pfnCompactCB = pfnCompactCB;
|
||||
ha->pvCompactUserData = pvUserData;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -466,7 +508,7 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
|
|||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Test the valid parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
||||
nError = ERROR_ACCESS_DENIED;
|
||||
|
|
@ -491,9 +533,9 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
|
|||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Initialize the progress variables for compact callback
|
||||
FileStream_GetSize(ha->pStream, &CompactTotalBytes);
|
||||
CompactBytesProcessed = 0;
|
||||
nError = CheckIfAllFilesKnown(ha, szListFile, pFileKeys);
|
||||
FileStream_GetSize(ha->pStream, &(ha->CompactTotalBytes));
|
||||
ha->CompactBytesProcessed = 0;
|
||||
nError = CheckIfAllKeysKnown(ha, szListFile, pFileKeys);
|
||||
}
|
||||
|
||||
// Get the temporary file name and create it
|
||||
|
|
@ -505,7 +547,7 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
|
|||
else
|
||||
_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)
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
|
@ -514,12 +556,12 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
|
|||
if(nError == ERROR_SUCCESS && ha->UserDataPos != 0)
|
||||
{
|
||||
// Inform the application about the progress
|
||||
if(CompactCB != NULL)
|
||||
CompactCB(pvUserData, CCB_COPYING_NON_MPQ_DATA, CompactBytesProcessed, CompactTotalBytes);
|
||||
if(ha->pfnCompactCB != NULL)
|
||||
ha->pfnCompactCB(ha->pvCompactUserData, CCB_COPYING_NON_MPQ_DATA, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||
|
||||
ByteOffset = 0;
|
||||
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)
|
||||
|
|
@ -533,35 +575,50 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
|
|||
|
||||
assert(ha->pUserData != NULL);
|
||||
assert(ha->pUserData->dwHeaderOffs == ByteCount);
|
||||
nError = CopyNonMpqData(ha->pStream, pTempStream, ByteOffset, ByteCount);
|
||||
nError = CopyNonMpqData(ha, ha->pStream, pTempStream, ByteOffset, ByteCount);
|
||||
}
|
||||
|
||||
// Write the MPQ header
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Remember the header size before swapping
|
||||
DWORD dwBytesToWrite = ha->pHeader->dwHeaderSize;
|
||||
TMPQHeader SaveMpqHeader;
|
||||
|
||||
BSWAP_TMPQHEADER(ha->pHeader);
|
||||
if(!FileStream_Write(pTempStream, NULL, ha->pHeader, dwBytesToWrite))
|
||||
// Write the MPQ header to the file
|
||||
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();
|
||||
BSWAP_TMPQHEADER(ha->pHeader);
|
||||
|
||||
// Update the progress
|
||||
CompactBytesProcessed += ha->pHeader->dwHeaderSize;
|
||||
ha->CompactBytesProcessed += ha->pHeader->dwHeaderSize;
|
||||
}
|
||||
|
||||
// Now copy all files
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
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(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(FileStream_Switch(ha->pStream, pTempStream))
|
||||
if(FileStream_Replace(ha->pStream, pTempStream))
|
||||
pTempStream = NULL;
|
||||
else
|
||||
nError = ERROR_CAN_NOT_COMPLETE;
|
||||
|
|
@ -576,18 +633,14 @@ bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bR
|
|||
//
|
||||
|
||||
nError = SaveMPQTables(ha);
|
||||
if(nError == ERROR_SUCCESS && CompactCB != NULL)
|
||||
if(nError == ERROR_SUCCESS && ha->pfnCompactCB != NULL)
|
||||
{
|
||||
CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
|
||||
CompactBytesProcessed += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock));
|
||||
CompactCB(pvUserData, CCB_CLOSING_ARCHIVE, CompactBytesProcessed, CompactTotalBytes);
|
||||
ha->CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
|
||||
ha->CompactBytesProcessed += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock));
|
||||
ha->pfnCompactCB(ha->pvCompactUserData, CCB_CLOSING_ARCHIVE, ha->CompactBytesProcessed, ha->CompactTotalBytes);
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate the compact callback
|
||||
pvUserData = NULL;
|
||||
CompactCB = NULL;
|
||||
|
||||
// Cleanup and return
|
||||
if(pTempStream != NULL)
|
||||
FileStream_Close(pTempStream);
|
||||
|
|
@ -610,156 +663,48 @@ DWORD WINAPI SFileGetMaxFileCount(HANDLE hMpq)
|
|||
|
||||
bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount)
|
||||
{
|
||||
TMPQHetTable * pOldHetTable = NULL;
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
TFileEntry * pOldFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||
TFileEntry * pOldFileTable = NULL;
|
||||
TFileEntry * pOldFileEntry;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQHash * pOldHashTable = NULL;
|
||||
DWORD dwOldHashTableSize = 0;
|
||||
DWORD dwOldFileTableSize = 0;
|
||||
DWORD dwNewHashTableSize = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Test the valid parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
|
||||
nError = ERROR_ACCESS_DENIED;
|
||||
|
||||
// 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)
|
||||
if(dwMaxFileCount < ha->dwFileTableSize)
|
||||
nError = ERROR_DISK_FULL;
|
||||
|
||||
// ALL file names must be known in order to be able
|
||||
// to rebuild hash table size
|
||||
// to rebuild hash table
|
||||
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(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)
|
||||
{
|
||||
// Save the current file table
|
||||
dwOldFileTableSize = ha->dwFileTableSize;
|
||||
pOldFileTable = ha->pFileTable;
|
||||
// Calculate the hash table size for the new file limit
|
||||
dwNewHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount);
|
||||
|
||||
// Create new one
|
||||
ha->pFileTable = STORM_ALLOC(TFileEntry, dwMaxFileCount);
|
||||
if(ha->pFileTable != NULL)
|
||||
memset(ha->pFileTable, 0, dwMaxFileCount * sizeof(TFileEntry));
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
// Rebuild both file tables
|
||||
nError = RebuildFileTable(ha, dwNewHashTableSize, dwMaxFileCount);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
DWORD dwFileIndex = 0;
|
||||
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;
|
||||
// Invalidate (listfile) and (attributes)
|
||||
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);
|
||||
}
|
||||
|
||||
// Return the result
|
||||
return (nError == ERROR_SUCCESS);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,10 @@ static int WriteNakedMPQHeader(TMPQArchive * ha)
|
|||
Header.wSectorSize = pHeader->wSectorSize;
|
||||
|
||||
// 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))
|
||||
nError = GetLastError();
|
||||
|
||||
|
|
@ -66,19 +69,32 @@ static int WriteNakedMPQHeader(TMPQArchive * ha)
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
// Fill the create structure
|
||||
memset(&CreateInfo, 0, sizeof(SFILE_CREATE_MPQ));
|
||||
CreateInfo.cbSize = sizeof(SFILE_CREATE_MPQ);
|
||||
CreateInfo.dwMpqVersion = (dwFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT;
|
||||
CreateInfo.dwStreamFlags = STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE;
|
||||
CreateInfo.dwAttrFlags = (dwFlags & MPQ_CREATE_ATTRIBUTES) ? MPQ_ATTRIBUTE_ALL : 0;
|
||||
CreateInfo.dwMpqVersion = (dwCreateFlags & MPQ_CREATE_ARCHIVE_VMASK) >> FLAGS_TO_FORMAT_SHIFT;
|
||||
CreateInfo.dwStreamFlags = STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE;
|
||||
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.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0;
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -86,11 +102,13 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea
|
|||
{
|
||||
TFileStream * pStream = NULL; // File stream
|
||||
TMPQArchive * ha = NULL; // MPQ archive handle
|
||||
TMPQHeader * pHeader;
|
||||
ULONGLONG MpqPos = 0; // Position of MPQ header in the file
|
||||
HANDLE hMpq = NULL;
|
||||
DWORD dwBlockTableSize = 0; // Initial block table size
|
||||
DWORD dwHashTableSize = 0;
|
||||
DWORD dwMaxFileCount;
|
||||
DWORD dwReservedFiles = 0; // Number of reserved file entries
|
||||
DWORD dwMpqFlags = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// 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
|
||||
if((pCreateInfo->cbSize == 0 || pCreateInfo->cbSize > sizeof(SFILE_CREATE_MPQ)) ||
|
||||
(pCreateInfo->dwMpqVersion > MPQ_FORMAT_VERSION_4) ||
|
||||
(pCreateInfo->pvUserData != NULL || pCreateInfo->cbUserData != 0) ||
|
||||
(pCreateInfo->dwAttrFlags & ~MPQ_ATTRIBUTE_ALL) ||
|
||||
(pCreateInfo->dwSectorSize & (pCreateInfo->dwSectorSize - 1)) ||
|
||||
(pCreateInfo->dwRawChunkSize & (pCreateInfo->dwRawChunkSize - 1)) ||
|
||||
(pCreateInfo->dwMaxFileCount < 4))
|
||||
if((pCreateInfo->cbSize == 0 || pCreateInfo->cbSize > sizeof(SFILE_CREATE_MPQ)) ||
|
||||
(pCreateInfo->dwMpqVersion > MPQ_FORMAT_VERSION_4) ||
|
||||
(pCreateInfo->pvUserData != NULL || pCreateInfo->cbUserData != 0) ||
|
||||
(pCreateInfo->dwAttrFlags & ~MPQ_ATTRIBUTE_ALL) ||
|
||||
(pCreateInfo->dwSectorSize & (pCreateInfo->dwSectorSize - 1)) ||
|
||||
(pCreateInfo->dwRawChunkSize & (pCreateInfo->dwRawChunkSize - 1)))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
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.
|
||||
// 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);
|
||||
SetLastError(ERROR_ALREADY_EXISTS);
|
||||
|
|
@ -139,15 +156,29 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea
|
|||
return false;
|
||||
}
|
||||
|
||||
// Increment the maximum amount of files to have space
|
||||
// for listfile and attributes file
|
||||
dwMaxFileCount = pCreateInfo->dwMaxFileCount;
|
||||
if(pCreateInfo->dwAttrFlags != 0)
|
||||
dwMaxFileCount++;
|
||||
dwMaxFileCount++;
|
||||
// Increment the maximum amount of files to have space for (listfile)
|
||||
if(pCreateInfo->dwMaxFileCount && pCreateInfo->dwFileFlags1)
|
||||
{
|
||||
dwMpqFlags |= MPQ_FLAG_LISTFILE_INVALID;
|
||||
dwReservedFiles++;
|
||||
}
|
||||
|
||||
// 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
|
||||
dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount);
|
||||
dwHashTableSize = GetHashTableSizeForFileCount(pCreateInfo->dwMaxFileCount + dwReservedFiles);
|
||||
|
||||
// Retrieve the file size and round it up to 0x200 bytes
|
||||
FileStream_GetSize(pStream, &MpqPos);
|
||||
|
|
@ -157,7 +188,7 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea
|
|||
|
||||
#ifdef _DEBUG
|
||||
// Debug code, used for testing StormLib
|
||||
// dwBlockTableSize = dwHashTableSize * 2;
|
||||
// dwBlockTableSize = dwHashTableSize * 2;
|
||||
#endif
|
||||
|
||||
// Create the archive handle
|
||||
|
|
@ -171,26 +202,21 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea
|
|||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
memset(ha, 0, sizeof(TMPQArchive));
|
||||
ha->pfnHashString = HashString;
|
||||
ha->pStream = pStream;
|
||||
ha->dwSectorSize = pCreateInfo->dwSectorSize;
|
||||
ha->UserDataPos = MpqPos;
|
||||
ha->MpqPos = MpqPos;
|
||||
ha->pHeader = (TMPQHeader *)ha->HeaderData;
|
||||
ha->dwMaxFileCount = dwMaxFileCount;
|
||||
ha->pHeader = pHeader = (TMPQHeader *)ha->HeaderData;
|
||||
ha->dwMaxFileCount = dwHashTableSize;
|
||||
ha->dwFileTableSize = 0;
|
||||
ha->dwReservedFiles = dwReservedFiles;
|
||||
ha->dwFileFlags1 = pCreateInfo->dwFileFlags1;
|
||||
ha->dwFileFlags2 = pCreateInfo->dwFileFlags2;
|
||||
ha->dwFlags = 0;
|
||||
|
||||
// Setup the attributes
|
||||
ha->dwFileFlags3 = pCreateInfo->dwFileFlags3 ? MPQ_FILE_EXISTS : 0;
|
||||
ha->dwAttrFlags = pCreateInfo->dwAttrFlags;
|
||||
ha->dwFlags = dwMpqFlags | MPQ_FLAG_CHANGED;
|
||||
pStream = NULL;
|
||||
}
|
||||
|
||||
// Fill the MPQ header
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
TMPQHeader * pHeader = ha->pHeader;
|
||||
|
||||
// Fill the MPQ header
|
||||
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
|
||||
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
|
||||
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)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Create initial hash table
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(nError == ERROR_SUCCESS && dwHashTableSize != 0)
|
||||
{
|
||||
nError = CreateHashTable(ha, dwHashTableSize);
|
||||
}
|
||||
|
||||
// Create initial file table
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(nError == ERROR_SUCCESS && ha->dwMaxFileCount != 0)
|
||||
{
|
||||
ha->pFileTable = STORM_ALLOC(TFileEntry, ha->dwMaxFileCount);
|
||||
if(ha->pFileTable != NULL)
|
||||
|
|
@ -244,7 +267,7 @@ bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCrea
|
|||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
FileStream_Close(pStream);
|
||||
FreeMPQArchive(ha);
|
||||
FreeArchiveHandle(ha);
|
||||
SetLastError(nError);
|
||||
ha = NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR
|
|||
// Create the local file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
pLocalFile = FileStream_CreateFile(szExtracted, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
|
||||
pLocalFile = FileStream_CreateFile(szExtracted, 0);
|
||||
if(pLocalFile == NULL)
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,12 +37,14 @@ struct TMPQSearch
|
|||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static bool IsValidSearchHandle(TMPQSearch * hs)
|
||||
static TMPQSearch * IsValidSearchHandle(HANDLE hFind)
|
||||
{
|
||||
if(hs == NULL)
|
||||
return false;
|
||||
TMPQSearch * hs = (TMPQSearch *)hFind;
|
||||
|
||||
return IsValidMpqHandle(hs->ha);
|
||||
if(hs != NULL && IsValidMpqHandle(hs->ha))
|
||||
return hs;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CheckWildCard(const char * szString, const char * szWildCard)
|
||||
|
|
@ -95,7 +97,7 @@ bool CheckWildCard(const char * szString, const char * szWildCard)
|
|||
// Calculate match count
|
||||
while(nMatchCount < nSubStringLength)
|
||||
{
|
||||
if(toupper(szString[nMatchCount]) != toupper(szWildCard[nMatchCount]))
|
||||
if(AsciiToUpperTable[(BYTE)szString[nMatchCount]] != AsciiToUpperTable[(BYTE)szWildCard[nMatchCount]])
|
||||
break;
|
||||
if(szString[nMatchCount] == 0)
|
||||
break;
|
||||
|
|
@ -118,7 +120,7 @@ bool CheckWildCard(const char * szString, const char * szWildCard)
|
|||
else
|
||||
{
|
||||
// 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;
|
||||
|
||||
// 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)
|
||||
{
|
||||
// Append the number of files
|
||||
dwMergeItems += (ha->pHetTable != NULL) ? ha->pHetTable->dwMaxFileCount
|
||||
dwMergeItems += (ha->pHetTable != NULL) ? ha->pHetTable->dwEntryCount
|
||||
: ha->pHeader->dwBlockTableSize;
|
||||
// Move to the patched archive
|
||||
ha = ha->haPatch;
|
||||
|
|
@ -151,9 +153,9 @@ static DWORD GetSearchTableItems(TMPQArchive * ha)
|
|||
}
|
||||
|
||||
static bool FileWasFoundBefore(
|
||||
TMPQArchive * ha,
|
||||
TMPQSearch * hs,
|
||||
TFileEntry * pFileEntry)
|
||||
TMPQArchive * ha,
|
||||
TMPQSearch * hs,
|
||||
TFileEntry * pFileEntry)
|
||||
{
|
||||
TFileEntry * pEntry;
|
||||
char * szRealFileName = pFileEntry->szFileName;
|
||||
|
|
@ -165,14 +167,14 @@ static bool FileWasFoundBefore(
|
|||
{
|
||||
// If we are in patch MPQ, we check if patch prefix matches
|
||||
// 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
|
||||
// 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;
|
||||
|
||||
szRealFileName += ha->cchPatchPrefix;
|
||||
szRealFileName += ha->pPatchPrefix->nLength;
|
||||
}
|
||||
|
||||
// Calculate the hash to the table
|
||||
|
|
@ -211,6 +213,14 @@ static bool FileWasFoundBefore(
|
|||
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)
|
||||
{
|
||||
TFileEntry * pPatchEntry = NULL;
|
||||
|
|
@ -223,9 +233,11 @@ static TFileEntry * FindPatchEntry(TMPQArchive * ha, TFileEntry * pFileEntry)
|
|||
{
|
||||
// Move to the patch archive
|
||||
ha = ha->haPatch;
|
||||
szFileName[0] = 0;
|
||||
|
||||
// Prepare the prefix for the file name
|
||||
strcpy(szFileName, ha->szPatchPrefix);
|
||||
if(ha->pPatchPrefix != NULL)
|
||||
strcpy(szFileName, ha->pPatchPrefix->szPatchPrefix);
|
||||
strcat(szFileName, pFileEntry->szFileName);
|
||||
|
||||
// Try to find the file there
|
||||
|
|
@ -259,7 +271,7 @@ static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
|
|||
pFileEntry = ha->pFileTable + hs->dwNextIndex;
|
||||
|
||||
// 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
|
||||
while(pFileEntry < pFileTableEnd)
|
||||
|
|
@ -267,61 +279,77 @@ static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
|
|||
// Increment the next index for subsequent search
|
||||
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)
|
||||
{
|
||||
// Now we have to check if this file was not enumerated before
|
||||
if(!FileWasFoundBefore(ha, hs, pFileEntry))
|
||||
// Spazzler3 protector: Some files are clearly wrong
|
||||
if(!FileEntryIsInvalid(ha, pFileEntry))
|
||||
{
|
||||
// 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)
|
||||
// Now we have to check if this file was not enumerated before
|
||||
if(!FileWasFoundBefore(ha, hs, pFileEntry))
|
||||
{
|
||||
// Open the file by its pseudo-name.
|
||||
// This also generates the file name with a proper extension
|
||||
sprintf(szPseudoName, "File%08u.xxx", dwBlockIndex);
|
||||
if(SFileOpenFileEx((HANDLE)hs->ha, szPseudoName, SFILE_OPEN_FROM_MPQ, &hFile))
|
||||
// if(pFileEntry != NULL && !_stricmp(pFileEntry->szFileName, "TriggerLibs\\NativeLib.galaxy"))
|
||||
// DebugBreak();
|
||||
|
||||
// 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;
|
||||
SFileCloseFile(hFile);
|
||||
// Open the file by its pseudo-name.
|
||||
// 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++;
|
||||
}
|
||||
|
||||
// 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
|
||||
hs->ha = ha = ha->haPatch;
|
||||
hs->dwNextIndex = 0;
|
||||
|
|
@ -353,7 +381,7 @@ HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DA
|
|||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Check for the valid parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(szMask == NULL || lpFindFileData == NULL)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
|
@ -413,11 +441,11 @@ HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DA
|
|||
|
||||
bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
|
||||
{
|
||||
TMPQSearch * hs = (TMPQSearch *)hFind;
|
||||
TMPQSearch * hs = IsValidSearchHandle(hFind);
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Check the parameters
|
||||
if(!IsValidSearchHandle(hs))
|
||||
if(hs == NULL)
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(lpFindFileData == NULL)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
|
@ -432,10 +460,10 @@ bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
|
|||
|
||||
bool WINAPI SFileFindClose(HANDLE hFind)
|
||||
{
|
||||
TMPQSearch * hs = (TMPQSearch *)hFind;
|
||||
TMPQSearch * hs = IsValidSearchHandle(hFind);
|
||||
|
||||
// Check the parameters
|
||||
if(!IsValidSearchHandle(hs))
|
||||
if(hs == NULL)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
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
|
||||
{
|
||||
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 dwFilePos; // Position of the cache in the file
|
||||
BYTE * pBegin; // The begin of the listfile cache
|
||||
BYTE * pPos;
|
||||
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)
|
||||
|
||||
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)
|
||||
{
|
||||
// Valid parameter check
|
||||
if(pCache == NULL)
|
||||
return false;
|
||||
|
||||
// Free all allocated buffers
|
||||
if(pCache->hFile != NULL)
|
||||
SFileCloseFile(pCache->hFile);
|
||||
if(pCache->szMask != NULL)
|
||||
STORM_FREE(pCache->szMask);
|
||||
STORM_FREE(pCache);
|
||||
if(pCache != NULL)
|
||||
STORM_FREE(pCache);
|
||||
return true;
|
||||
}
|
||||
|
||||
static TListFileCache * CreateListFileCache(HANDLE hListFile, const char * szMask)
|
||||
{
|
||||
TListFileCache * pCache = NULL;
|
||||
size_t nMaskLength = 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
|
||||
pCache = (TListFileCache *)STORM_ALLOC(TListFileCache, 1);
|
||||
if(pCache == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Clear the entire structure
|
||||
if(nError == ERROR_SUCCESS)
|
||||
pCache = (TListFileCache *)STORM_ALLOC(BYTE, sizeof(TListFileCache) + nMaskLength);
|
||||
if(pCache != NULL)
|
||||
{
|
||||
memset(pCache, 0, sizeof(TListFileCache));
|
||||
pCache->hFile = hListFile;
|
||||
// Clear the entire structure
|
||||
memset(pCache, 0, sizeof(TListFileCache) + nMaskLength);
|
||||
|
||||
// Shall we allocate a mask?
|
||||
// Shall we copy the mask?
|
||||
if(szMask != NULL)
|
||||
{
|
||||
pCache->szMask = STORM_ALLOC(char, strlen(szMask) + 1);
|
||||
if(pCache->szMask != NULL)
|
||||
strcpy(pCache->szMask, szMask);
|
||||
else
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
pCache->szMask = (char *)(pCache + 1);
|
||||
memcpy(pCache->szMask, szMask, nMaskLength);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the file cache
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
pCache->dwFileSize = SFileGetFileSize(pCache->hFile, NULL);
|
||||
|
||||
// Fill the cache
|
||||
SFileReadFile(pCache->hFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL);
|
||||
if(dwBytesRead == 0)
|
||||
nError = GetLastError();
|
||||
}
|
||||
|
||||
// Allocate pointers
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
pCache->pBegin =
|
||||
pCache->pPos = &pCache->Buffer[0];
|
||||
pCache->pEnd = pCache->pBegin + dwBytesRead;
|
||||
}
|
||||
else
|
||||
{
|
||||
FreeListFileCache(pCache);
|
||||
SetLastError(nError);
|
||||
pCache = NULL;
|
||||
// Load the file cache from the file
|
||||
SFileReadFile(hListFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL);
|
||||
if(dwBytesRead != 0)
|
||||
{
|
||||
// Allocate pointers
|
||||
pCache->pBegin = pCache->pPos = &pCache->Buffer[0];
|
||||
pCache->pEnd = pCache->pBegin + dwBytesRead;
|
||||
pCache->dwFileSize = dwFileSize;
|
||||
pCache->hFile = hListFile;
|
||||
}
|
||||
else
|
||||
{
|
||||
FreeListFileCache(pCache);
|
||||
pCache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the cache
|
||||
|
|
@ -116,8 +116,6 @@ static DWORD ReloadListFileCache(TListFileCache * pCache)
|
|||
// Only do something if the cache is empty
|
||||
if(pCache->pPos >= pCache->pEnd)
|
||||
{
|
||||
// __TryReadBlock:
|
||||
|
||||
// Move the file position forward
|
||||
pCache->dwFilePos += CACHE_BUFFER_SIZE;
|
||||
if(pCache->dwFilePos >= pCache->dwFileSize)
|
||||
|
|
@ -130,7 +128,7 @@ static DWORD ReloadListFileCache(TListFileCache * pCache)
|
|||
|
||||
// Load the next data chunk to the cache
|
||||
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
|
||||
// of the file is not available (in case of partial MPQs).
|
||||
|
|
@ -141,7 +139,7 @@ static DWORD ReloadListFileCache(TListFileCache * pCache)
|
|||
|
||||
// Set the buffer pointers
|
||||
pCache->pBegin =
|
||||
pCache->pPos = &pCache->Buffer[0];
|
||||
pCache->pPos = &pCache->Buffer[0];
|
||||
pCache->pEnd = pCache->pBegin + dwBytesRead;
|
||||
}
|
||||
|
||||
|
|
@ -191,6 +189,10 @@ static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, int nMaxC
|
|||
if(*pCache->pPos == '~')
|
||||
szExtraString = szLine;
|
||||
|
||||
// Remember that last occurence of a slash or backslash
|
||||
// if(*pCache->pPos == '\\' || *pCache->pPos == '/')
|
||||
// szPlainName = szLine + 1;
|
||||
|
||||
// Copy the character
|
||||
*szLine++ = *pCache->pPos++;
|
||||
}
|
||||
|
|
@ -220,19 +222,91 @@ static int CompareFileNodes(const void * p1, const void * p2)
|
|||
return _stricmp(szFileName1, szFileName2);
|
||||
}
|
||||
|
||||
static int WriteListFileLine(
|
||||
TMPQFile * hf,
|
||||
const char * szLine)
|
||||
static LPBYTE CreateListFile(TMPQArchive * ha, DWORD * pcbListFile)
|
||||
{
|
||||
char szNewLine[2] = {0x0D, 0x0A};
|
||||
size_t nLength = strlen(szLine);
|
||||
int nError;
|
||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||
TFileEntry * pFileEntry;
|
||||
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);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
return nError;
|
||||
// Allocate the table for sorting listfile
|
||||
SortTable = STORM_ALLOC(char*, ha->dwFileTableSize);
|
||||
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;
|
||||
TMPQHash * pFirstHash;
|
||||
TMPQHash * pHash;
|
||||
bool bNameEntryCreated = false;
|
||||
|
||||
// If we have HET table, use that one
|
||||
if(ha->pHetTable != NULL)
|
||||
|
|
@ -256,15 +329,14 @@ static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFil
|
|||
if(pFileEntry != NULL)
|
||||
{
|
||||
// Allocate file name for the file entry
|
||||
AllocateFileName(pFileEntry, szFileName);
|
||||
bNameEntryCreated = true;
|
||||
AllocateFileName(ha, pFileEntry, szFileName);
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
// 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
|
||||
pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
|
||||
|
|
@ -276,8 +348,7 @@ static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFil
|
|||
if(pHash->dwBlockIndex < pHeader->dwBlockTableSize)
|
||||
{
|
||||
// Allocate file name for the file entry
|
||||
AllocateFileName(ha->pFileTable + pHash->dwBlockIndex, szFileName);
|
||||
bNameEntryCreated = true;
|
||||
AllocateFileName(ha, ha->pFileTable + pHash->dwBlockIndex, szFileName);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Saves the whole listfile into the MPQ.
|
||||
// Saves the whole listfile to the MPQ
|
||||
int SListFileSaveToMpq(TMPQArchive * ha)
|
||||
{
|
||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQFile * hf = NULL;
|
||||
char * szPrevItem;
|
||||
char ** SortTable = NULL;
|
||||
DWORD dwFileSize = 0;
|
||||
size_t nFileNodes = 0;
|
||||
size_t i;
|
||||
LPBYTE pbListFile;
|
||||
DWORD cbListFile = 0;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Allocate the table for sorting listfile
|
||||
SortTable = STORM_ALLOC(char*, ha->dwFileTableSize);
|
||||
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 save the listfile if we should do so
|
||||
if(ha->dwFileFlags1 != 0)
|
||||
{
|
||||
// Only take existing items
|
||||
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL)
|
||||
// At this point, we expect to have at least one reserved entry in the file table
|
||||
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
|
||||
if(!IsPseudoFileName(pFileEntry->szFileName, NULL) && !IsInternalMpqFileName(pFileEntry->szFileName))
|
||||
// We expect it to be nonzero size
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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++)
|
||||
else
|
||||
{
|
||||
// If the item is the same like the last one, skip it
|
||||
if(_stricmp(SortTable[i], szPrevItem))
|
||||
{
|
||||
dwFileSize += (DWORD)strlen(SortTable[i]) + 2;
|
||||
szPrevItem = SortTable[i];
|
||||
}
|
||||
// If the list file is empty, we assume ERROR_SUCCESS
|
||||
nError = (cbListFile == 0) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Determine the flags for (listfile)
|
||||
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 the save process succeeded, we clear the MPQ_FLAG_LISTFILE_INVALID flag
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// Each name is followed by newline ("\x0D\x0A")
|
||||
szPrevItem = SortTable[0];
|
||||
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);
|
||||
ha->dwFlags &= ~MPQ_FLAG_LISTFILE_INVALID;
|
||||
ha->dwReservedFiles--;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
@ -415,25 +431,20 @@ static int SFileAddArbitraryListFile(
|
|||
TListFileCache * pCache = NULL;
|
||||
size_t nLength;
|
||||
char szFileName[MAX_PATH];
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Create the listfile cache for that file
|
||||
pCache = CreateListFileCache(hListFile, NULL);
|
||||
if(pCache == NULL)
|
||||
nError = GetLastError();
|
||||
|
||||
// Load the node list. Add the node for every locale in the archive
|
||||
if(nError == ERROR_SUCCESS)
|
||||
if(pCache != NULL)
|
||||
{
|
||||
// Load the node list. Add the node for every locale in the archive
|
||||
while((nLength = ReadListFileLine(pCache, szFileName, sizeof(szFileName))) > 0)
|
||||
SListFileCreateNodeForAllLocales(ha, szFileName);
|
||||
pCache->hFile = NULL;
|
||||
|
||||
// Delete the cache
|
||||
FreeListFileCache(pCache);
|
||||
}
|
||||
|
||||
// Delete the cache
|
||||
if(pCache != NULL)
|
||||
FreeListFileCache(pCache);
|
||||
return nError;
|
||||
return (pCache != NULL) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
static int SFileAddExternalListFile(
|
||||
|
|
@ -445,12 +456,12 @@ static int SFileAddExternalListFile(
|
|||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Open the external list file
|
||||
if(SFileOpenFileEx(hMpq, szListFile, SFILE_OPEN_LOCAL_FILE, &hListFile))
|
||||
{
|
||||
// Add the data from the listfile to MPQ
|
||||
nError = SFileAddArbitraryListFile(ha, hListFile);
|
||||
SFileCloseFile(hListFile);
|
||||
}
|
||||
if(!SFileOpenFileEx(hMpq, szListFile, SFILE_OPEN_LOCAL_FILE, &hListFile))
|
||||
return GetLastError();
|
||||
|
||||
// Add the data from the listfile to MPQ
|
||||
nError = SFileAddArbitraryListFile(ha, hListFile);
|
||||
SFileCloseFile(hListFile);
|
||||
return nError;
|
||||
}
|
||||
|
||||
|
|
@ -517,9 +528,9 @@ int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile)
|
|||
while(ha != NULL)
|
||||
{
|
||||
if(szListFile != NULL)
|
||||
SFileAddExternalListFile(ha, hMpq, szListFile);
|
||||
nError = SFileAddExternalListFile(ha, hMpq, szListFile);
|
||||
else
|
||||
SFileAddInternalListFile(ha, hMpq);
|
||||
nError = SFileAddInternalListFile(ha, hMpq);
|
||||
|
||||
// Also, add three special files to the listfile:
|
||||
// (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)
|
||||
{
|
||||
TListFileCache * pCache = NULL;
|
||||
HANDLE hListFile;
|
||||
HANDLE hListFile = NULL;
|
||||
size_t nLength = 0;
|
||||
DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
|
@ -566,12 +577,16 @@ HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const
|
|||
{
|
||||
pCache = CreateListFileCache(hListFile, szMask);
|
||||
if(pCache == NULL)
|
||||
nError = GetLastError();
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Perform file search
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// The listfile handle is in the cache now
|
||||
hListFile = NULL;
|
||||
|
||||
// Iterate through the listfile
|
||||
for(;;)
|
||||
{
|
||||
// Read the (next) line
|
||||
|
|
@ -591,12 +606,17 @@ HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const
|
|||
// Cleanup & exit
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
|
||||
FreeListFileCache(pCache);
|
||||
|
||||
SetLastError(nError);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -604,34 +624,44 @@ bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData
|
|||
{
|
||||
TListFileCache * pCache = (TListFileCache *)hFind;
|
||||
size_t nLength;
|
||||
bool bResult = false;
|
||||
int nError = ERROR_SUCCESS;
|
||||
int nError = ERROR_INVALID_PARAMETER;
|
||||
|
||||
for(;;)
|
||||
// Check for parameters
|
||||
if(pCache != NULL)
|
||||
{
|
||||
// Read the (next) line
|
||||
nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
|
||||
if(nLength == 0)
|
||||
for(;;)
|
||||
{
|
||||
nError = ERROR_NO_MORE_FILES;
|
||||
break;
|
||||
}
|
||||
// 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 some mask entered, check it
|
||||
if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
|
||||
{
|
||||
nError = ERROR_SUCCESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(nError != ERROR_SUCCESS)
|
||||
SetLastError(nError);
|
||||
return bResult;
|
||||
return (nError == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
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 */
|
||||
/*****************************************************************************/
|
||||
|
||||
static bool IsAviFile(void * pvFileBegin)
|
||||
static bool IsAviFile(DWORD * HeaderData)
|
||||
{
|
||||
LPDWORD AviHeader = (DWORD *)pvFileBegin;
|
||||
DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(AviHeader[0]);
|
||||
DWORD DwordValue2 = BSWAP_INT32_UNSIGNED(AviHeader[2]);
|
||||
DWORD DwordValue3 = BSWAP_INT32_UNSIGNED(AviHeader[3]);
|
||||
DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(HeaderData[0]);
|
||||
DWORD DwordValue2 = BSWAP_INT32_UNSIGNED(HeaderData[2]);
|
||||
DWORD DwordValue3 = BSWAP_INT32_UNSIGNED(HeaderData[3]);
|
||||
|
||||
// Test for 'RIFF', 'AVI ' or 'LIST'
|
||||
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;
|
||||
size_t nLength;
|
||||
TMPQUserData * pUserData;
|
||||
|
||||
// Calculate the length of the bitmap in blocks and in bytes
|
||||
nLength = (size_t)(((ha->pHeader->ArchiveSize64 - 1) / pMpqBitmap->dwBlockSize) + 1);
|
||||
nLength = (size_t)(((nLength - 1) / 8) + 1);
|
||||
// BSWAP the source data and copy them to our buffer
|
||||
BSWAP_ARRAY32_UNSIGNED(&pvUserData, sizeof(TMPQUserData));
|
||||
pUserData = (TMPQUserData *)pvUserData;
|
||||
|
||||
// Allocate the file bitmap
|
||||
pBitmap = (TFileBitmap *)STORM_ALLOC(BYTE, sizeof(TFileBitmap) + nLength);
|
||||
if(pBitmap != NULL)
|
||||
// Check the sizes
|
||||
if(pUserData->cbUserDataHeader <= pUserData->cbUserDataSize && pUserData->cbUserDataSize <= pUserData->dwHeaderOffs)
|
||||
{
|
||||
// Fill the structure
|
||||
pBitmap->StartOffset = ha->MpqPos;
|
||||
pBitmap->EndOffset = ha->MpqPos + ha->pHeader->ArchiveSize64;
|
||||
pBitmap->IsComplete = bFileIsComplete ? 1 : 0;
|
||||
pBitmap->BitmapSize = (DWORD)nLength;
|
||||
pBitmap->BlockSize = pMpqBitmap->dwBlockSize;
|
||||
pBitmap->Reserved = 0;
|
||||
// Move to the position given by the userdata
|
||||
ByteOffset += pUserData->dwHeaderOffs;
|
||||
|
||||
// Copy the file bitmap
|
||||
memcpy((pBitmap + 1), (pMpqBitmap + 1), nLength);
|
||||
// The MPQ header should be within range of the file size
|
||||
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.
|
||||
|
|
@ -86,6 +84,9 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize)
|
|||
if(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)
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
|
@ -94,6 +95,9 @@ static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize)
|
|||
if(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)
|
||||
return ERROR_BAD_FORMAT;
|
||||
}
|
||||
|
|
@ -139,11 +143,12 @@ LCID WINAPI SFileSetLocale(LCID lcNewLocale)
|
|||
// phMpq - Pointer to store open archive handle
|
||||
|
||||
bool WINAPI SFileOpenArchive(
|
||||
const TCHAR * szMpqName,
|
||||
DWORD dwPriority,
|
||||
DWORD dwFlags,
|
||||
HANDLE * phMpq)
|
||||
const TCHAR * szMpqName,
|
||||
DWORD dwPriority,
|
||||
DWORD dwFlags,
|
||||
HANDLE * phMpq)
|
||||
{
|
||||
TMPQUserData * pUserData;
|
||||
TFileStream * pStream = NULL; // Open file stream
|
||||
TMPQArchive * ha = NULL; // Archive handle
|
||||
TFileEntry * pFileEntry;
|
||||
|
|
@ -161,16 +166,28 @@ bool WINAPI SFileOpenArchive(
|
|||
// Open the MPQ archive file
|
||||
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
|
||||
pStream = FileStream_OpenFile(szMpqName, (dwFlags & STREAM_OPTIONS_MASK));
|
||||
pStream = FileStream_OpenFile(szMpqName, dwStreamFlags);
|
||||
if(pStream == NULL)
|
||||
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
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
FileStream_GetSize(pStream, &FileSize);
|
||||
if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
|
@ -178,98 +195,129 @@ bool WINAPI SFileOpenArchive(
|
|||
// Initialize handle structure and allocate structure for MPQ header
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
ULONGLONG SearchOffset = 0;
|
||||
ULONGLONG EndOfSearch = FileSize;
|
||||
DWORD dwStreamFlags = 0;
|
||||
DWORD dwHeaderSize;
|
||||
DWORD dwHeaderID;
|
||||
|
||||
memset(ha, 0, sizeof(TMPQArchive));
|
||||
ha->pfnHashString = HashString;
|
||||
ha->pStream = pStream;
|
||||
pStream = NULL;
|
||||
|
||||
// Remember if the archive is open for write
|
||||
if(FileStream_IsReadOnly(ha->pStream))
|
||||
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
|
||||
// Set the archive read only if the stream is read-only
|
||||
FileStream_GetFlags(ha->pStream, &dwStreamFlags);
|
||||
ha->dwFlags |= (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? MPQ_FLAG_READ_ONLY : 0;
|
||||
|
||||
// Also remember if we shall check sector CRCs when reading file
|
||||
if(dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC)
|
||||
ha->dwFlags |= MPQ_FLAG_CHECK_SECTOR_CRC;
|
||||
}
|
||||
ha->dwFlags |= (dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC) ? MPQ_FLAG_CHECK_SECTOR_CRC : 0;
|
||||
|
||||
// Find the offset of MPQ header within the file
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
ULONGLONG SearchPos = 0;
|
||||
DWORD dwHeaderID;
|
||||
// Also remember if this MPQ is a patch
|
||||
ha->dwFlags |= (dwFlags & MPQ_OPEN_PATCH) ? MPQ_FLAG_PATCH : 0;
|
||||
|
||||
while(SearchPos < FileSize)
|
||||
// Limit the header searching to about 130 MB of data
|
||||
if(EndOfSearch > 0x08000000)
|
||||
EndOfSearch = 0x08000000;
|
||||
|
||||
// Find the offset of MPQ header within the file
|
||||
while(SearchOffset < EndOfSearch)
|
||||
{
|
||||
DWORD dwBytesAvailable = MPQ_HEADER_SIZE_V4;
|
||||
|
||||
// Cut the bytes available, if needed
|
||||
if((FileSize - SearchPos) < MPQ_HEADER_SIZE_V4)
|
||||
dwBytesAvailable = (DWORD)(FileSize - SearchPos);
|
||||
if((FileSize - SearchOffset) < MPQ_HEADER_SIZE_V4)
|
||||
dwBytesAvailable = (DWORD)(FileSize - SearchOffset);
|
||||
|
||||
// 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();
|
||||
break;
|
||||
}
|
||||
|
||||
// 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;
|
||||
break;
|
||||
}
|
||||
|
||||
// If there is the MPQ user data signature, process it
|
||||
dwHeaderID = BSWAP_INT32_UNSIGNED(*(LPDWORD)ha->HeaderData);
|
||||
if(dwHeaderID == ID_MPQ_USERDATA && ha->pUserData == NULL)
|
||||
dwHeaderID = BSWAP_INT32_UNSIGNED(ha->HeaderData[0]);
|
||||
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
|
||||
if((dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0)
|
||||
// Verify if this looks like a valid user data
|
||||
pUserData = IsValidMpqUserData(SearchOffset, FileSize, ha->HeaderData);
|
||||
if(pUserData != NULL)
|
||||
{
|
||||
// Fill the user data header
|
||||
ha->UserDataPos = SearchOffset;
|
||||
ha->pUserData = &ha->UserData;
|
||||
memcpy(ha->pUserData, ha->HeaderData, sizeof(TMPQUserData));
|
||||
BSWAP_TMPQUSERDATA(ha->pUserData);
|
||||
memcpy(ha->pUserData, pUserData, sizeof(TMPQUserData));
|
||||
|
||||
// Remember the position of the user data and continue search
|
||||
ha->UserDataPos = SearchPos;
|
||||
SearchPos += ha->pUserData->dwHeaderOffs;
|
||||
// Continue searching from that position
|
||||
SearchOffset += ha->pUserData->dwHeaderOffs;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// There must be MPQ header signature
|
||||
if(dwHeaderID == ID_MPQ)
|
||||
// There must be MPQ header signature. Note that STORM.dll from Warcraft III actually
|
||||
// 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
|
||||
BSWAP_TMPQHEADER(ha->pHeader);
|
||||
nError = ConvertMpqHeaderToFormat4(ha, FileSize, dwFlags);
|
||||
nError = ConvertMpqHeaderToFormat4(ha, SearchOffset, 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;
|
||||
}
|
||||
|
||||
// Move to the next possible offset
|
||||
SearchPos += 0x200;
|
||||
SearchOffset += 0x200;
|
||||
}
|
||||
|
||||
// If we haven't found MPQ header in the file, it's an error
|
||||
if(ha->pHeader == NULL)
|
||||
nError = ERROR_BAD_FORMAT;
|
||||
// Did we identify one of the supported headers?
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// 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
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
// 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,
|
||||
// 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
|
||||
// We can overcome such protectors by forcing opening the archive as MPQ v 1.0
|
||||
if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
|
||||
|
|
@ -291,24 +339,6 @@ bool WINAPI SFileOpenArchive(
|
|||
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 HET table. Ignore the result, as HET table is no longer required
|
||||
if(nError == ERROR_SUCCESS)
|
||||
|
|
@ -320,14 +350,13 @@ bool WINAPI SFileOpenArchive(
|
|||
// the block table, BET table, hi-block table, (attributes) and (listfile).
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
nError = BuildFileTable(ha, FileSize);
|
||||
nError = BuildFileTable(ha);
|
||||
}
|
||||
|
||||
// Verify the file table, if no kind of protection was detected
|
||||
if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_PROTECTED) == 0)
|
||||
// Verify the file table, if no kind of malformation was detected
|
||||
if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_MALFORMED) == 0)
|
||||
{
|
||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->pHeader->dwBlockTableSize;
|
||||
// ULONGLONG ArchiveSize = 0;
|
||||
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
||||
ULONGLONG RawFilePos;
|
||||
|
||||
// Parse all file entries
|
||||
|
|
@ -354,10 +383,6 @@ bool WINAPI SFileOpenArchive(
|
|||
nError = ERROR_FILE_CORRUPT;
|
||||
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
|
||||
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);
|
||||
if(pFileEntry != NULL)
|
||||
{
|
||||
// Ignore result of the operation. (listfile) is optional.
|
||||
SFileAddListFile((HANDLE)ha, NULL);
|
||||
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
|
||||
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);
|
||||
if(pFileEntry != NULL)
|
||||
{
|
||||
// Ignore result of the operation. (attributes) is optional.
|
||||
SAttrLoadAttributes(ha);
|
||||
ha->dwFileFlags2 = pFileEntry->dwFlags;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore result of the operation. (attributes) is optional.
|
||||
SAttrLoadAttributes(ha);
|
||||
// Remember whether the archive has weak signature. Only for MPQs format 1.0.
|
||||
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
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
FileStream_Close(pStream);
|
||||
FreeMPQArchive(ha);
|
||||
FreeArchiveHandle(ha);
|
||||
SetLastError(nError);
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
// Do nothing if 'hMpq' is bad parameter
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
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(ha->dwFlags & MPQ_FLAG_INV_LISTFILE)
|
||||
if(ha->dwFlags & MPQ_FLAG_LISTFILE_INVALID)
|
||||
{
|
||||
nError = SListFileSaveToMpq(ha);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
|
|
@ -440,7 +501,7 @@ bool WINAPI SFileFlushArchive(HANDLE hMpq)
|
|||
}
|
||||
|
||||
// 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);
|
||||
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
|
||||
if(ha->dwFlags & MPQ_FLAG_CHANGED)
|
||||
{
|
||||
// Save all MPQ tables first
|
||||
nError = SaveMPQTables(ha);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
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
|
||||
if(nResultError != ERROR_SUCCESS)
|
||||
SetLastError(nResultError);
|
||||
|
|
@ -470,11 +543,16 @@ bool WINAPI SFileCloseArchive(HANDLE hMpq)
|
|||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
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
|
||||
bResult = SFileFlushArchive(hMpq);
|
||||
|
||||
// Free all memory used by MPQ archive
|
||||
FreeMPQArchive(ha);
|
||||
FreeArchiveHandle(ha);
|
||||
return bResult;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,29 +16,46 @@
|
|||
/* 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)
|
||||
{
|
||||
TFileStream * pStream;
|
||||
TMPQFile * hf = NULL;
|
||||
|
||||
// We have to convert the local file name to UNICODE, if needed
|
||||
#ifdef _UNICODE
|
||||
TCHAR szFileNameT[MAX_PATH];
|
||||
int i;
|
||||
|
||||
for(i = 0; szFileName[i] != 0; i++)
|
||||
szFileNameT[i] = szFileName[i];
|
||||
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
|
||||
// Convert the file name to UNICODE (if needed)
|
||||
CopyFileName(szFileNameT, szFileName, strlen(szFileName));
|
||||
|
||||
// Open the file and create the TMPQFile structure
|
||||
pStream = FileStream_OpenFile(szFileNameT, STREAM_FLAG_READ_ONLY);
|
||||
if(pStream != NULL)
|
||||
{
|
||||
// Allocate and initialize file handle
|
||||
hf = CreateMpqFile(NULL);
|
||||
hf = CreateFileHandle(NULL, NULL);
|
||||
if(hf != NULL)
|
||||
{
|
||||
hf->pStream = pStream;
|
||||
|
|
@ -55,87 +72,67 @@ static bool OpenLocalFile(const char * szFileName, HANDLE * phFile)
|
|||
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;
|
||||
TFileEntry * pFileEntry;
|
||||
TMPQFile * hfPatch; // Pointer to patch 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;
|
||||
HANDLE hPatchFile;
|
||||
char szPatchFileName[MAX_PATH];
|
||||
char szNameBuffer[MAX_PATH];
|
||||
|
||||
// Keep this flag here for future updates
|
||||
dwReserved = dwReserved;
|
||||
|
||||
// First of all, try to open the original version of the file in any of the patch chain
|
||||
// First of all, find the latest archive where the file is in base version
|
||||
// (i.e. where the original, unpatched version of the file exists)
|
||||
while(ha != NULL)
|
||||
{
|
||||
// Construct the name of the patch file
|
||||
strcpy(szPatchFileName, ha->szPatchPrefix);
|
||||
strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName);
|
||||
if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, (HANDLE *)&hfBase))
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
// If the file is there, then we remember the archive
|
||||
pFileEntry = GetFileEntryExact(ha, GetPatchFileName(ha, szFileName, szNameBuffer), 0);
|
||||
if(pFileEntry != NULL && (pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
|
||||
haBase = ha;
|
||||
|
||||
SFileCloseFile((HANDLE)hfBase);
|
||||
}
|
||||
|
||||
// Move to the next file in the patch chain
|
||||
// Move to the patch archive
|
||||
ha = ha->haPatch;
|
||||
}
|
||||
|
||||
// If we couldn't find the file in any of the patches, it doesn't exist
|
||||
if(hf == NULL)
|
||||
// If we couldn't find the base file in any of the patches, it doesn't exist
|
||||
if((ha = haBase) == NULL)
|
||||
{
|
||||
SetLastError(ERROR_FILE_NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now keep going in the patch chain and open every patch file that is there
|
||||
for(ha = ha->haPatch; ha != NULL; ha = ha->haPatch)
|
||||
// Now open the base file
|
||||
if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szNameBuffer), SFILE_OPEN_BASE_FILE, (HANDLE *)&hfBase))
|
||||
{
|
||||
// Construct patch file name
|
||||
strcpy(szPatchFileName, ha->szPatchPrefix);
|
||||
strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName);
|
||||
if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, &hPatchFile))
|
||||
// The file must be a base file, i.e. without MPQ_FILE_PATCH_FILE
|
||||
assert((hfBase->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0);
|
||||
hf = hfBase;
|
||||
|
||||
// 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
|
||||
hfPatch = (TMPQFile *)hPatchFile;
|
||||
// Prepare the file name with a correct prefix
|
||||
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 have to remember the highest full file
|
||||
if((hfPatch->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
|
||||
hfLast = hfPatch;
|
||||
// We should not find patch file
|
||||
assert((hfPatch->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) != 0);
|
||||
|
||||
// Set current patch to base file and move on
|
||||
hf->hfPatchFile = hfPatch;
|
||||
hf = hfPatch;
|
||||
// Attach the patch to the base file
|
||||
hf->hfPatch = 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
|
||||
if(phFile != NULL)
|
||||
*phFile = (HANDLE)hfBase;
|
||||
return true;
|
||||
return (hfBase != NULL);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
@ -149,11 +146,11 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HAN
|
|||
// otherwise the function returns ERROR_INSUFFICIENT_BUFFER.
|
||||
|
||||
int WINAPI SFileEnumLocales(
|
||||
HANDLE hMpq,
|
||||
const char * szFileName,
|
||||
LCID * plcLocales,
|
||||
LPDWORD pdwMaxLocales,
|
||||
DWORD dwSearchScope)
|
||||
HANDLE hMpq,
|
||||
const char * szFileName,
|
||||
LCID * plcLocales,
|
||||
LPDWORD pdwMaxLocales,
|
||||
DWORD dwSearchScope)
|
||||
{
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
TFileEntry * pFileEntry;
|
||||
|
|
@ -163,7 +160,7 @@ int WINAPI SFileEnumLocales(
|
|||
DWORD dwLocales = 0;
|
||||
|
||||
// Test the parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
return ERROR_INVALID_HANDLE;
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
|
@ -232,11 +229,11 @@ bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName)
|
|||
TFileEntry * pFileEntry;
|
||||
DWORD dwFlagsToCheck = MPQ_FILE_EXISTS;
|
||||
DWORD dwFileIndex = 0;
|
||||
char szPatchFileName[MAX_PATH];
|
||||
char szPrefixBuffer[MAX_PATH];
|
||||
bool bIsPseudoName;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
|
|
@ -251,7 +248,7 @@ bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName)
|
|||
while(ha != NULL)
|
||||
{
|
||||
// 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);
|
||||
// Verify the file flags
|
||||
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
|
||||
dwFlagsToCheck = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE;
|
||||
ha = ha->haPatch;
|
||||
|
||||
// Prepare the patched file name
|
||||
if(ha != NULL)
|
||||
{
|
||||
strcpy(szPatchFileName, ha->szPatchPrefix);
|
||||
strcat(szPatchFileName, szFileName);
|
||||
szFileName = szPatchFileName;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found, sorry
|
||||
|
|
@ -306,73 +295,78 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch
|
|||
{
|
||||
switch(dwSearchScope)
|
||||
{
|
||||
case SFILE_OPEN_PATCHED_FILE:
|
||||
case SFILE_OPEN_FROM_MPQ:
|
||||
case SFILE_OPEN_BASE_FILE:
|
||||
|
||||
// We want to open the updated version of the file
|
||||
return OpenPatchedFile(hMpq, szFileName, 0, phFile);
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
{
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
case SFILE_OPEN_FROM_MPQ:
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
{
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!IsValidMpqHandle(ha))
|
||||
{
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
// Check the pseudo-file name
|
||||
if(IsPseudoFileName(szFileName, &dwFileIndex))
|
||||
{
|
||||
pFileEntry = GetFileEntryByIndex(ha, dwFileIndex);
|
||||
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;
|
||||
}
|
||||
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
{
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
case SFILE_OPEN_ANY_LOCALE:
|
||||
|
||||
// First of all, check the name as-is
|
||||
if(!IsPseudoFileName(szFileName, &dwFileIndex))
|
||||
{
|
||||
pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale);
|
||||
// This open option is reserved for opening MPQ internal listfile.
|
||||
// No argument validation. Tries to open file with neutral locale first,
|
||||
// then any other available.
|
||||
pFileEntry = GetFileEntryAny(ha, szFileName);
|
||||
if(pFileEntry == NULL)
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
else
|
||||
{
|
||||
bOpenByIndex = true;
|
||||
pFileEntry = GetFileEntryByIndex(ha, dwFileIndex);
|
||||
if(pFileEntry == NULL)
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case SFILE_OPEN_ANY_LOCALE:
|
||||
case SFILE_OPEN_LOCAL_FILE:
|
||||
|
||||
// This open option is reserved for opening MPQ internal listfile.
|
||||
// No argument validation. Tries to open file with neutral locale first,
|
||||
// then any other available.
|
||||
dwSearchScope = SFILE_OPEN_FROM_MPQ;
|
||||
pFileEntry = GetFileEntryAny(ha, szFileName);
|
||||
if(pFileEntry == NULL)
|
||||
nError = ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
{
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
||||
return OpenLocalFile(szFileName, phFile);
|
||||
|
||||
default:
|
||||
|
||||
// Don't accept any other value
|
||||
nError = ERROR_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
// Quick return if something failed
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
SetLastError(nError);
|
||||
*phFile = NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -389,22 +383,14 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch
|
|||
// Allocate file handle
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if((hf = STORM_ALLOC(TMPQFile, 1)) == NULL)
|
||||
hf = CreateFileHandle(ha, pFileEntry);
|
||||
if(hf == NULL)
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
// Initialize file handle
|
||||
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(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC)
|
||||
hf->bCheckSectorCRCs = true;
|
||||
|
|
@ -413,7 +399,7 @@ bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearch
|
|||
if(bOpenByIndex == false)
|
||||
{
|
||||
// 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(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
|
||||
if(nError == ERROR_SUCCESS && pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
|
||||
{
|
||||
assert(hf->pPatchInfo == NULL);
|
||||
nError = AllocatePatchInfo(hf, true);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
// Cleanup and exit
|
||||
if(nError != ERROR_SUCCESS)
|
||||
{
|
||||
SetLastError(nError);
|
||||
FreeMPQFile(hf);
|
||||
FreeFileHandle(hf);
|
||||
return false;
|
||||
}
|
||||
|
||||
*phFile = hf;
|
||||
return (nError == ERROR_SUCCESS);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -457,13 +437,13 @@ bool WINAPI SFileCloseFile(HANDLE hFile)
|
|||
{
|
||||
TMPQFile * hf = (TMPQFile *)hFile;
|
||||
|
||||
if(!IsValidFileHandle(hf))
|
||||
if(!IsValidFileHandle(hFile))
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Free the structure
|
||||
FreeMPQFile(hf);
|
||||
FreeFileHandle(hf);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Local structures
|
||||
|
||||
#define PATCH_SIGNATURE_HEADER 0x48435450
|
||||
#define PATCH_SIGNATURE_MD5 0x5f35444d
|
||||
#define PATCH_SIGNATURE_XFRM 0x4d524658
|
||||
|
||||
typedef struct _BLIZZARD_BSDIFF40_FILE
|
||||
{
|
||||
ULONGLONG Signature;
|
||||
|
|
@ -24,37 +28,29 @@ typedef struct _BLIZZARD_BSDIFF40_FILE
|
|||
} BLIZZARD_BSDIFF40_FILE, *PBLIZZARD_BSDIFF40_FILE;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
// Local variables
|
||||
|
||||
static bool GetDefaultPatchPrefix(
|
||||
const TCHAR * szBaseMpqName,
|
||||
char * szBuffer)
|
||||
static const char * LanguageList[] =
|
||||
{
|
||||
const TCHAR * szExtension;
|
||||
const TCHAR * szDash;
|
||||
"deDE",
|
||||
"enCN",
|
||||
"enGB",
|
||||
"enTW",
|
||||
"enUS",
|
||||
"esES",
|
||||
"esMX",
|
||||
"frFR",
|
||||
"koKR",
|
||||
"ptBR",
|
||||
"ptPT",
|
||||
"ruRU",
|
||||
"zhCN",
|
||||
"zhTW",
|
||||
NULL
|
||||
};
|
||||
|
||||
// Ensure that both names are plain names
|
||||
szBaseMpqName = GetPlainFileNameT(szBaseMpqName);
|
||||
|
||||
// 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;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
|
||||
static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE pbCompressed, DWORD cbCompressed)
|
||||
{
|
||||
|
|
@ -65,7 +61,6 @@ static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE p
|
|||
|
||||
// Cut the initial DWORD from the compressed chunk
|
||||
pbCompressed += sizeof(DWORD);
|
||||
cbCompressed -= sizeof(DWORD);
|
||||
|
||||
// Pre-fill decompressed buffer with zeros
|
||||
memset(pbDecompressed, 0, cbDecompressed);
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -120,7 +115,7 @@ static int LoadMpqPatch_COPY(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
|||
return nError;
|
||||
}
|
||||
|
||||
static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
||||
static int LoadFilePatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
||||
{
|
||||
LPBYTE pbDecompressed = NULL;
|
||||
LPBYTE pbCompressed = NULL;
|
||||
|
|
@ -133,7 +128,7 @@ static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
|||
cbCompressed = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER;
|
||||
pbCompressed = STORM_ALLOC(BYTE, cbCompressed);
|
||||
if(pbCompressed == NULL)
|
||||
nError = ERROR_SUCCESS;
|
||||
nError = ERROR_NOT_ENOUGH_MEMORY;
|
||||
|
||||
// Read the compressed patch data
|
||||
if(nError == ERROR_SUCCESS)
|
||||
|
|
@ -178,46 +173,38 @@ static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader)
|
|||
return nError;
|
||||
}
|
||||
|
||||
static int ApplyMpqPatch_COPY(
|
||||
TMPQFile * hf,
|
||||
TPatchHeader * pPatchHeader)
|
||||
static int ApplyFilePatch_COPY(
|
||||
TMPQFile * hfFrom,
|
||||
TMPQFile * hf,
|
||||
TPatchHeader * pPatchHeader)
|
||||
{
|
||||
LPBYTE pbNewFileData;
|
||||
DWORD cbNewFileData;
|
||||
|
||||
// Allocate space for new file data
|
||||
cbNewFileData = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER;
|
||||
pbNewFileData = STORM_ALLOC(BYTE, cbNewFileData);
|
||||
if(pbNewFileData == NULL)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
// Sanity checks
|
||||
assert(hf->cbFileData == (pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER));
|
||||
assert(hf->pbFileData != NULL);
|
||||
hfFrom = hfFrom;
|
||||
|
||||
// Copy the patch data as-is
|
||||
memcpy(pbNewFileData, (LPBYTE)pPatchHeader + sizeof(TPatchHeader), cbNewFileData);
|
||||
|
||||
// Free the old file data
|
||||
STORM_FREE(hf->pbFileData);
|
||||
|
||||
// Put the new file data there
|
||||
hf->pbFileData = pbNewFileData;
|
||||
hf->cbFileData = cbNewFileData;
|
||||
memcpy(hf->pbFileData, (LPBYTE)pPatchHeader + sizeof(TPatchHeader), hf->cbFileData);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static int ApplyMpqPatch_BSD0(
|
||||
TMPQFile * hf,
|
||||
TPatchHeader * pPatchHeader)
|
||||
static int ApplyFilePatch_BSD0(
|
||||
TMPQFile * hfFrom,
|
||||
TMPQFile * hf,
|
||||
TPatchHeader * pPatchHeader)
|
||||
{
|
||||
PBLIZZARD_BSDIFF40_FILE pBsdiff;
|
||||
LPDWORD pCtrlBlock;
|
||||
LPBYTE pbPatchData = (LPBYTE)pPatchHeader + sizeof(TPatchHeader);
|
||||
LPBYTE pDataBlock;
|
||||
LPBYTE pExtraBlock;
|
||||
LPBYTE pbNewData = NULL;
|
||||
LPBYTE pbOldData = (LPBYTE)hf->pbFileData;
|
||||
LPBYTE pbOldData = hfFrom->pbFileData;
|
||||
LPBYTE pbNewData = hf->pbFileData;
|
||||
DWORD dwCombineSize;
|
||||
DWORD dwNewOffset = 0; // Current position to patch
|
||||
DWORD dwOldOffset = 0; // Current source position
|
||||
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
|
||||
// Format of BSDIFF header corresponds to original BSDIFF, which is:
|
||||
|
|
@ -245,11 +232,6 @@ static int ApplyMpqPatch_BSD0(
|
|||
pExtraBlock = (LPBYTE)pbPatchData;
|
||||
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
|
||||
while(dwNewOffset < dwNewSize)
|
||||
{
|
||||
|
|
@ -260,31 +242,26 @@ static int ApplyMpqPatch_BSD0(
|
|||
|
||||
// Sanity check
|
||||
if((dwNewOffset + dwAddDataLength) > dwNewSize)
|
||||
{
|
||||
STORM_FREE(pbNewData);
|
||||
return ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Read the diff string to the target buffer
|
||||
memcpy(pbNewData + dwNewOffset, pDataBlock, dwAddDataLength);
|
||||
pDataBlock += dwAddDataLength;
|
||||
|
||||
// Now combine the patch data with the original file
|
||||
for(i = 0; i < dwAddDataLength; i++)
|
||||
{
|
||||
if(dwOldOffset < dwOldSize)
|
||||
pbNewData[dwNewOffset] = pbNewData[dwNewOffset] + pbOldData[dwOldOffset];
|
||||
// Get the longest block that we can combine
|
||||
dwCombineSize = ((dwOldOffset + dwAddDataLength) >= dwOldSize) ? (dwOldSize - dwOldOffset) : dwAddDataLength;
|
||||
|
||||
dwNewOffset++;
|
||||
dwOldOffset++;
|
||||
}
|
||||
// Now combine the patch data with the original file
|
||||
for(i = 0; i < dwCombineSize; i++)
|
||||
pbNewData[dwNewOffset + i] = pbNewData[dwNewOffset + i] + pbOldData[dwOldOffset + i];
|
||||
|
||||
// Move the offsets
|
||||
dwNewOffset += dwAddDataLength;
|
||||
dwOldOffset += dwAddDataLength;
|
||||
|
||||
// Sanity check
|
||||
if((dwNewOffset + dwMovDataLength) > dwNewSize)
|
||||
{
|
||||
STORM_FREE(pbNewData);
|
||||
return ERROR_FILE_CORRUPT;
|
||||
}
|
||||
|
||||
// Copy the data from the extra block in BSDIFF patch
|
||||
memcpy(pbNewData + dwNewOffset, pExtraBlock, dwMovDataLength);
|
||||
|
|
@ -298,17 +275,12 @@ static int ApplyMpqPatch_BSD0(
|
|||
pCtrlBlock += 3;
|
||||
}
|
||||
|
||||
// Free the old file data
|
||||
STORM_FREE(hf->pbFileData);
|
||||
|
||||
// Put the new data to the fil structure
|
||||
hf->pbFileData = pbNewData;
|
||||
hf->cbFileData = dwNewSize;
|
||||
// Success
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int LoadMpqPatch(TMPQFile * hf)
|
||||
static int LoadFilePatch(TMPQFile * hf)
|
||||
{
|
||||
TPatchHeader PatchHeader;
|
||||
DWORD dwBytesRead;
|
||||
|
|
@ -328,7 +300,7 @@ static int LoadMpqPatch(TMPQFile * hf)
|
|||
PatchHeader.dwXfrmBlockSize = BSWAP_INT32_UNSIGNED(PatchHeader.dwXfrmBlockSize);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -337,52 +309,65 @@ static int LoadMpqPatch(TMPQFile * hf)
|
|||
{
|
||||
switch(PatchHeader.dwPatchType)
|
||||
{
|
||||
case 0x59504f43: // 'COPY'
|
||||
nError = LoadMpqPatch_COPY(hf, &PatchHeader);
|
||||
break;
|
||||
case 0x59504f43: // 'COPY'
|
||||
nError = LoadFilePatch_COPY(hf, &PatchHeader);
|
||||
break;
|
||||
|
||||
case 0x30445342: // 'BSD0'
|
||||
nError = LoadMpqPatch_BSD0(hf, &PatchHeader);
|
||||
break;
|
||||
case 0x30445342: // 'BSD0'
|
||||
nError = LoadFilePatch_BSD0(hf, &PatchHeader);
|
||||
break;
|
||||
|
||||
default:
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
default:
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nError;
|
||||
}
|
||||
|
||||
static int ApplyMpqPatch(
|
||||
TMPQFile * hf,
|
||||
TPatchHeader * pPatchHeader)
|
||||
static int ApplyFilePatch(
|
||||
TMPQFile * hfBase, // The file in the base MPQ
|
||||
TMPQFile * hfPrev, // The file in the previous MPQ
|
||||
TMPQFile * hf)
|
||||
{
|
||||
TPatchHeader * pPatchHeader = hf->pPatchHeader;
|
||||
TMPQFile * hfFrom = NULL;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Verify the original file before patching
|
||||
if(pPatchHeader->dwSizeBeforePatch != 0)
|
||||
{
|
||||
if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_before_patch))
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
}
|
||||
// Sanity checks
|
||||
assert(hf->pbFileData == NULL);
|
||||
|
||||
// Either take the base version or the previous version
|
||||
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
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
switch(pPatchHeader->dwPatchType)
|
||||
{
|
||||
case 0x59504f43: // 'COPY'
|
||||
nError = ApplyMpqPatch_COPY(hf, pPatchHeader);
|
||||
break;
|
||||
case 0x59504f43: // 'COPY'
|
||||
nError = ApplyFilePatch_COPY(hfFrom, hf, pPatchHeader);
|
||||
break;
|
||||
|
||||
case 0x30445342: // 'BSD0'
|
||||
nError = ApplyMpqPatch_BSD0(hf, pPatchHeader);
|
||||
break;
|
||||
case 0x30445342: // 'BSD0'
|
||||
nError = ApplyFilePatch_BSD0(hfFrom, hf, pPatchHeader);
|
||||
break;
|
||||
|
||||
default:
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
default:
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -392,11 +377,267 @@ static int ApplyMpqPatch(
|
|||
// Verify the patched file
|
||||
if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_after_patch))
|
||||
nError = ERROR_FILE_CORRUPT;
|
||||
|
||||
// Copy the MD5 of the new block
|
||||
memcpy(hf->FileDataMD5, pPatchHeader->md5_after_patch, MD5_DIGEST_SIZE);
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
|
|
@ -425,34 +666,75 @@ bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatche
|
|||
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)
|
||||
{
|
||||
TMPQFile * hfBase = hf;
|
||||
TMPQFile * hfPrev = hf;
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Move to the first patch
|
||||
hf = hf->hfPatchFile;
|
||||
// We need to calculate the MD5 of the entire file
|
||||
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
|
||||
while(hf != NULL)
|
||||
// Apply all patches
|
||||
for(hf = hf->hfPatch; hf != NULL; hf = hf->hfPatch)
|
||||
{
|
||||
// This must be true
|
||||
assert(hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE);
|
||||
|
||||
// Make sure that the patch data is loaded
|
||||
nError = LoadMpqPatch(hf);
|
||||
nError = LoadFilePatch(hf);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Apply the patch
|
||||
nError = ApplyMpqPatch(hfBase, hf->pPatchHeader);
|
||||
nError = ApplyFilePatch(hfBase, hfPrev, hf);
|
||||
if(nError != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
// Move to the next patch
|
||||
hf = hf->hfPatchFile;
|
||||
// Only keep base file version and previous version
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -478,34 +760,25 @@ int PatchFileData(TMPQFile * hf)
|
|||
//
|
||||
|
||||
bool WINAPI SFileOpenPatchArchive(
|
||||
HANDLE hMpq,
|
||||
const TCHAR * szPatchMpqName,
|
||||
const char * szPatchPathPrefix,
|
||||
DWORD dwFlags)
|
||||
HANDLE hMpq,
|
||||
const TCHAR * szPatchMpqName,
|
||||
const char * szPatchPathPrefix,
|
||||
DWORD dwFlags)
|
||||
{
|
||||
TMPQArchive * haPatch;
|
||||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
HANDLE hPatchMpq = NULL;
|
||||
char szPatchPrefixBuff[MPQ_PATCH_PREFIX_LEN];
|
||||
int nError = ERROR_SUCCESS;
|
||||
|
||||
// Keep compiler happy
|
||||
dwFlags = dwFlags;
|
||||
|
||||
// Verify input parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
nError = ERROR_INVALID_HANDLE;
|
||||
if(szPatchMpqName == NULL || *szPatchMpqName == 0)
|
||||
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
|
||||
//
|
||||
|
|
@ -520,38 +793,19 @@ bool WINAPI SFileOpenPatchArchive(
|
|||
|
||||
if(nError == ERROR_SUCCESS)
|
||||
{
|
||||
if(!FileStream_IsReadOnly(ha->pStream))
|
||||
if(!(ha->dwFlags & MPQ_FLAG_READ_ONLY))
|
||||
nError = ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
// Open the archive like it is normal archive
|
||||
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;
|
||||
haPatch = (TMPQArchive *)hPatchMpq;
|
||||
|
||||
// Older WoW patches (build 13914) used to have
|
||||
// several language versions in one patch file
|
||||
// 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);
|
||||
}
|
||||
// We need to remember the proper patch prefix to match names of patched files
|
||||
FindPatchPrefix(ha, (TMPQArchive *)hPatchMpq, szPatchPathPrefix);
|
||||
|
||||
// Now add the patch archive to the list of patches to the original MPQ
|
||||
while(ha != NULL)
|
||||
|
|
@ -580,7 +834,7 @@ bool WINAPI SFileIsPatchedArchive(HANDLE hMpq)
|
|||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
|
||||
// Verify input parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
return false;
|
||||
|
||||
return (ha->haPatch != NULL);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -20,89 +20,83 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Local defines
|
||||
|
||||
#define SIGNATURE_TYPE_NONE 0
|
||||
#define SIGNATURE_TYPE_WEAK 1
|
||||
#define SIGNATURE_TYPE_STRONG 2
|
||||
|
||||
#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
|
||||
// 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 =
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJJidwS/uILMBSO5DLGsBFknIXWWjQJe"
|
||||
"2kfdfEk3G/j66w4KkhZ1V61Rt4zLaMVCYpDun7FLwRjkMDSepO1q2DcCAwEAAQ=="
|
||||
"-----END PUBLIC KEY-----";
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJJidwS/uILMBSO5DLGsBFknIXWWjQJe"
|
||||
"2kfdfEk3G/j66w4KkhZ1V61Rt4zLaMVCYpDun7FLwRjkMDSepO1q2DcCAwEAAQ=="
|
||||
"-----END PUBLIC KEY-----";
|
||||
|
||||
static const char * szBlizzardStrongPublicKey =
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsQZ+ziT2h8h+J/iMQpgd"
|
||||
"tH1HaJzOBE3agjU4yMPcrixaPOZoA4t8bwfey7qczfWywocYo3pleytFF+IuD4HD"
|
||||
"Fl9OXN1SFyupSgMx1EGZlgbFAomnbq9MQJyMqQtMhRAjFgg4TndS7YNb+JMSAEKp"
|
||||
"kXNqY28n/EVBHD5TsMuVCL579gIenbr61dI92DDEdy790IzIG0VKWLh/KOTcTJfm"
|
||||
"Ds/7HQTkGouVW+WUsfekuqNQo7ND9DBnhLjLjptxeFE2AZqYcA1ao3S9LN3GL1tW"
|
||||
"lVXFIX9c7fWqaVTQlZ2oNsI/ARVApOK3grNgqvwH6YoVYVXjNJEo5sQJsPsdV/hk"
|
||||
"dwIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsQZ+ziT2h8h+J/iMQpgd"
|
||||
"tH1HaJzOBE3agjU4yMPcrixaPOZoA4t8bwfey7qczfWywocYo3pleytFF+IuD4HD"
|
||||
"Fl9OXN1SFyupSgMx1EGZlgbFAomnbq9MQJyMqQtMhRAjFgg4TndS7YNb+JMSAEKp"
|
||||
"kXNqY28n/EVBHD5TsMuVCL579gIenbr61dI92DDEdy790IzIG0VKWLh/KOTcTJfm"
|
||||
"Ds/7HQTkGouVW+WUsfekuqNQo7ND9DBnhLjLjptxeFE2AZqYcA1ao3S9LN3GL1tW"
|
||||
"lVXFIX9c7fWqaVTQlZ2oNsI/ARVApOK3grNgqvwH6YoVYVXjNJEo5sQJsPsdV/hk"
|
||||
"dwIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
|
||||
static const char * szWarcraft3MapPublicKey =
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1BwklUUQ3UvjizOBRoF5"
|
||||
"yyOVc7KD+oGOQH5i6eUk1yfs0luCC70kNucNrfqhmviywVtahRse1JtXCPrx2bd3"
|
||||
"iN8Dx91fbkxjYIOGTsjYoHKTp0BbaFkJih776fcHgnFSb+7mJcDuJVvJOXxEH6w0"
|
||||
"1vo6VtujCqj1arqbyoal+xtAaczF3us5cOEp45sR1zAWTn1+7omN7VWV4QqJPaDS"
|
||||
"gBSESc0l1grO0i1VUSumayk7yBKIkb+LBvcG6WnYZHCi7VdLmaxER5m8oZfER66b"
|
||||
"heHoiSQIZf9PAY6Guw2DT5BTc54j/AaLQAKf2qcRSgQLVo5kQaddF3rCpsXoB/74"
|
||||
"6QIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1BwklUUQ3UvjizOBRoF5"
|
||||
"yyOVc7KD+oGOQH5i6eUk1yfs0luCC70kNucNrfqhmviywVtahRse1JtXCPrx2bd3"
|
||||
"iN8Dx91fbkxjYIOGTsjYoHKTp0BbaFkJih776fcHgnFSb+7mJcDuJVvJOXxEH6w0"
|
||||
"1vo6VtujCqj1arqbyoal+xtAaczF3us5cOEp45sR1zAWTn1+7omN7VWV4QqJPaDS"
|
||||
"gBSESc0l1grO0i1VUSumayk7yBKIkb+LBvcG6WnYZHCi7VdLmaxER5m8oZfER66b"
|
||||
"heHoiSQIZf9PAY6Guw2DT5BTc54j/AaLQAKf2qcRSgQLVo5kQaddF3rCpsXoB/74"
|
||||
"6QIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
|
||||
static const char * szWowPatchPublicKey =
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwOsMV0LagAWPEtEQM6b9"
|
||||
"6FHFkUyGbbyda2/Dfc9dyl21E9QvX+Yw7qKRMAKPzA2TlQQLZKvXpnKXF/YIK5xa"
|
||||
"5uwg9CEHCEAYolLG4xn0FUOE0E/0PuuytI0p0ICe6rk00PifZzTr8na2wI/l/GnQ"
|
||||
"bvnIVF1ck6cslATpQJ5JJVMXzoFlUABS19WESw4MXuJAS3AbMhxNWdEhVv7eO51c"
|
||||
"yGjRLy9QjogZODZTY0fSEksgBqQxNCoYVJYI/sF5K2flDsGqrIp0OdJ6teJlzg1Y"
|
||||
"UjYnb6bKjlidXoHEXI2TgA/mD6O3XFIt08I9s3crOCTgICq7cgX35qrZiIVWZdRv"
|
||||
"TwIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwOsMV0LagAWPEtEQM6b9"
|
||||
"6FHFkUyGbbyda2/Dfc9dyl21E9QvX+Yw7qKRMAKPzA2TlQQLZKvXpnKXF/YIK5xa"
|
||||
"5uwg9CEHCEAYolLG4xn0FUOE0E/0PuuytI0p0ICe6rk00PifZzTr8na2wI/l/GnQ"
|
||||
"bvnIVF1ck6cslATpQJ5JJVMXzoFlUABS19WESw4MXuJAS3AbMhxNWdEhVv7eO51c"
|
||||
"yGjRLy9QjogZODZTY0fSEksgBqQxNCoYVJYI/sF5K2flDsGqrIp0OdJ6teJlzg1Y"
|
||||
"UjYnb6bKjlidXoHEXI2TgA/mD6O3XFIt08I9s3crOCTgICq7cgX35qrZiIVWZdRv"
|
||||
"TwIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
|
||||
static const char * szWowSurveyPublicKey =
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnIt1DR6nRyyKsy2qahHe"
|
||||
"MKLtacatn/KxieHcwH87wLBxKy+jZ0gycTmJ7SaTdBAEMDs/V5IPIXEtoqYnid2c"
|
||||
"63TmfGDU92oc3Ph1PWUZ2PWxBhT06HYxRdbrgHw9/I29pNPi/607x+lzPORITOgU"
|
||||
"BR6MR8au8HsQP4bn4vkJNgnSgojh48/XQOB/cAln7As1neP61NmVimoLR4Bwi3zt"
|
||||
"zfgrZaUpyeNCUrOYJmH09YIjbBySTtXOUidoPHjFrMsCWpr6xs8xbETbs7MJFL6a"
|
||||
"vcUfTT67qfIZ9RsuKfnXJTIrV0kwDSjjuNXiPTmWAehSsiHIsrUXX5RNcwsSjClr"
|
||||
"nQIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnIt1DR6nRyyKsy2qahHe"
|
||||
"MKLtacatn/KxieHcwH87wLBxKy+jZ0gycTmJ7SaTdBAEMDs/V5IPIXEtoqYnid2c"
|
||||
"63TmfGDU92oc3Ph1PWUZ2PWxBhT06HYxRdbrgHw9/I29pNPi/607x+lzPORITOgU"
|
||||
"BR6MR8au8HsQP4bn4vkJNgnSgojh48/XQOB/cAln7As1neP61NmVimoLR4Bwi3zt"
|
||||
"zfgrZaUpyeNCUrOYJmH09YIjbBySTtXOUidoPHjFrMsCWpr6xs8xbETbs7MJFL6a"
|
||||
"vcUfTT67qfIZ9RsuKfnXJTIrV0kwDSjjuNXiPTmWAehSsiHIsrUXX5RNcwsSjClr"
|
||||
"nQIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
|
||||
static const char * szStarcraft2MapPublicKey =
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmk4GT8zb+ICC25a17KZB"
|
||||
"q/ygKGJ2VSO6IT5PGHJlm1KfnHBA4B6SH3xMlJ4c6eG2k7QevZv+FOhjsAHubyWq"
|
||||
"2VKqWbrIFKv2ILc2RfMn8J9EDVRxvcxh6slRrVL69D0w1tfVGjMiKq2Fym5yGoRT"
|
||||
"E7CRgDqbAbXP9LBsCNWHiJLwfxMGzHbk8pIl9oia5pvM7ofZamSHchxlpy6xa4GJ"
|
||||
"7xKN01YCNvklTL1D7uol3wkwcHc7vrF8QwuJizuA5bSg4poEGtH62BZOYi+UL/z0"
|
||||
"31YK+k9CbQyM0X0pJoJoYz1TK+Y5J7vBnXCZtfcTYQ/ZzN6UcxTa57dJaiOlCh9z"
|
||||
"nQIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
"-----BEGIN PUBLIC KEY-----"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmk4GT8zb+ICC25a17KZB"
|
||||
"q/ygKGJ2VSO6IT5PGHJlm1KfnHBA4B6SH3xMlJ4c6eG2k7QevZv+FOhjsAHubyWq"
|
||||
"2VKqWbrIFKv2ILc2RfMn8J9EDVRxvcxh6slRrVL69D0w1tfVGjMiKq2Fym5yGoRT"
|
||||
"E7CRgDqbAbXP9LBsCNWHiJLwfxMGzHbk8pIl9oia5pvM7ofZamSHchxlpy6xa4GJ"
|
||||
"7xKN01YCNvklTL1D7uol3wkwcHc7vrF8QwuJizuA5bSg4poEGtH62BZOYi+UL/z0"
|
||||
"31YK+k9CbQyM0X0pJoJoYz1TK+Y5J7vBnXCZtfcTYQ/ZzN6UcxTa57dJaiOlCh9z"
|
||||
"nQIDAQAB"
|
||||
"-----END PUBLIC KEY-----";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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)
|
||||
{
|
||||
unsigned char decoded_key[0x200];
|
||||
|
|
@ -153,10 +140,10 @@ static bool decode_base64_key(const char * szKeyBase64, rsa_key * key)
|
|||
}
|
||||
|
||||
static void GetPlainAnsiFileName(
|
||||
const TCHAR * szFileName,
|
||||
char * szPlainName)
|
||||
const TCHAR * szFileName,
|
||||
char * szPlainName)
|
||||
{
|
||||
const TCHAR * szPlainNameT = GetPlainFileNameT(szFileName);
|
||||
const TCHAR * szPlainNameT = GetPlainFileName(szFileName);
|
||||
|
||||
// Convert the plain name to ANSI
|
||||
while(*szPlainNameT != 0)
|
||||
|
|
@ -166,8 +153,8 @@ static void GetPlainAnsiFileName(
|
|||
|
||||
// Calculate begin and end of the MPQ archive
|
||||
static void CalculateArchiveRange(
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI)
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI)
|
||||
{
|
||||
ULONGLONG TempPos = 0;
|
||||
char szMapHeader[0x200];
|
||||
|
|
@ -186,71 +173,17 @@ static void CalculateArchiveRange(
|
|||
}
|
||||
}
|
||||
|
||||
// Get the MPQ data end. This is stored in our MPQ header,
|
||||
// and it's been already prepared by SFileOpenArchive,
|
||||
// Get the MPQ data end. This is stored in the MPQ header
|
||||
pSI->EndMpqData = ha->MpqPos + ha->pHeader->ArchiveSize64;
|
||||
|
||||
// Get the size of the entire file
|
||||
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(
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI,
|
||||
LPBYTE pMd5Digest)
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI,
|
||||
LPBYTE pMd5Digest)
|
||||
{
|
||||
hash_state md5_state;
|
||||
ULONGLONG BeginBuffer;
|
||||
|
|
@ -324,17 +257,18 @@ static bool CalculateMpqHashMd5(
|
|||
}
|
||||
|
||||
static void AddTailToSha1(
|
||||
hash_state * psha1_state,
|
||||
const char * szTail)
|
||||
hash_state * psha1_state,
|
||||
const char * szTail)
|
||||
{
|
||||
unsigned char * pbTail = (unsigned char *)szTail;
|
||||
unsigned char szUpperCase[0x200];
|
||||
unsigned long nLength = 0;
|
||||
|
||||
// Convert the tail to uppercase
|
||||
// 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
|
||||
|
|
@ -342,11 +276,11 @@ static void AddTailToSha1(
|
|||
}
|
||||
|
||||
static bool CalculateMpqHashSha1(
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI,
|
||||
unsigned char * sha1_tail0,
|
||||
unsigned char * sha1_tail1,
|
||||
unsigned char * sha1_tail2)
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI,
|
||||
unsigned char * sha1_tail0,
|
||||
unsigned char * sha1_tail1,
|
||||
unsigned char * sha1_tail2)
|
||||
{
|
||||
ULONGLONG BeginBuffer;
|
||||
hash_state sha1_state_temp;
|
||||
|
|
@ -411,9 +345,9 @@ static bool CalculateMpqHashSha1(
|
|||
}
|
||||
|
||||
static int VerifyRawMpqData(
|
||||
TMPQArchive * ha,
|
||||
ULONGLONG ByteOffset,
|
||||
DWORD dwDataSize)
|
||||
TMPQArchive * ha,
|
||||
ULONGLONG ByteOffset,
|
||||
DWORD dwDataSize)
|
||||
{
|
||||
ULONGLONG DataOffset = ha->MpqPos + ByteOffset;
|
||||
LPBYTE pbDataChunk;
|
||||
|
|
@ -499,8 +433,8 @@ static int VerifyRawMpqData(
|
|||
}
|
||||
|
||||
static DWORD VerifyWeakSignature(
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI)
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI)
|
||||
{
|
||||
BYTE RevSignature[MPQ_WEAK_SIGNATURE_SIZE];
|
||||
BYTE Md5Digest[MD5_DIGEST_SIZE];
|
||||
|
|
@ -527,9 +461,9 @@ static DWORD VerifyWeakSignature(
|
|||
}
|
||||
|
||||
static DWORD VerifyStrongSignatureWithKey(
|
||||
unsigned char * reversed_signature,
|
||||
unsigned char * padded_digest,
|
||||
const char * szPublicKey)
|
||||
unsigned char * reversed_signature,
|
||||
unsigned char * padded_digest,
|
||||
const char * szPublicKey)
|
||||
{
|
||||
rsa_key key;
|
||||
int result = 0;
|
||||
|
|
@ -551,8 +485,8 @@ static DWORD VerifyStrongSignatureWithKey(
|
|||
}
|
||||
|
||||
static DWORD VerifyStrongSignature(
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI)
|
||||
TMPQArchive * ha,
|
||||
PMPQ_SIGNATURE_INFO pSI)
|
||||
{
|
||||
unsigned char reversed_signature[MPQ_STRONG_SIGNATURE_SIZE];
|
||||
unsigned char Sha1Digest_tail0[SHA1_DIGEST_SIZE];
|
||||
|
|
@ -614,11 +548,11 @@ static DWORD VerifyStrongSignature(
|
|||
}
|
||||
|
||||
static DWORD VerifyFile(
|
||||
HANDLE hMpq,
|
||||
const char * szFileName,
|
||||
LPDWORD pdwCrc32,
|
||||
char * pMD5,
|
||||
DWORD dwFlags)
|
||||
HANDLE hMpq,
|
||||
const char * szFileName,
|
||||
LPDWORD pdwCrc32,
|
||||
char * pMD5,
|
||||
DWORD dwFlags)
|
||||
{
|
||||
hash_state md5_state;
|
||||
unsigned char * pFileMd5;
|
||||
|
|
@ -628,14 +562,17 @@ static DWORD VerifyFile(
|
|||
BYTE Buffer[0x1000];
|
||||
HANDLE hFile = NULL;
|
||||
DWORD dwVerifyResult = 0;
|
||||
DWORD dwSearchScope = SFILE_OPEN_FROM_MPQ;
|
||||
DWORD dwTotalBytes = 0;
|
||||
DWORD dwBytesRead;
|
||||
DWORD dwCrc32 = 0;
|
||||
|
||||
// Fix the open type for patched archives
|
||||
if(SFileIsPatchedArchive(hMpq))
|
||||
dwSearchScope = SFILE_OPEN_PATCHED_FILE;
|
||||
//
|
||||
// Note: When the MPQ is patched, it will
|
||||
// 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(dwFlags & SFILE_VERIFY_RAW_MD5)
|
||||
|
|
@ -667,7 +604,7 @@ static DWORD VerifyFile(
|
|||
}
|
||||
|
||||
// Attempt to open the file
|
||||
if(SFileOpenFileEx(hMpq, szFileName, dwSearchScope, &hFile))
|
||||
if(SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_FROM_MPQ, &hFile))
|
||||
{
|
||||
// Get the file size
|
||||
hf = (TMPQFile *)hFile;
|
||||
|
|
@ -719,7 +656,7 @@ static DWORD VerifyFile(
|
|||
if(dwTotalBytes == 0)
|
||||
{
|
||||
// Check CRC32 and MD5 only if there is no patches
|
||||
if(hf->hfPatchFile == NULL)
|
||||
if(hf->hfPatch == NULL)
|
||||
{
|
||||
// Check if the CRC32 matches.
|
||||
if(dwFlags & SFILE_VERIFY_FILE_CRC)
|
||||
|
|
@ -741,7 +678,7 @@ static DWORD VerifyFile(
|
|||
md5_done(&md5_state, md5);
|
||||
|
||||
// Only check the MD5 if it is valid
|
||||
if(is_valid_md5(pFileMd5))
|
||||
if(IsValidMD5(pFileMd5))
|
||||
{
|
||||
dwVerifyResult |= VERIFY_FILE_HAS_MD5;
|
||||
if(memcmp(md5, pFileMd5, MD5_DIGEST_SIZE))
|
||||
|
|
@ -777,6 +714,154 @@ static DWORD VerifyFile(
|
|||
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
|
||||
|
||||
|
|
@ -824,7 +909,7 @@ int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * sz
|
|||
TMPQHeader * pHeader;
|
||||
|
||||
// Verify input parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
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
|
||||
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
|
||||
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:
|
||||
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
|
||||
if(pHeader->HetTablePos64 && pHeader->HetTableSize64)
|
||||
return VerifyRawMpqData(ha, pHeader->HetTablePos64, (DWORD)pHeader->HetTableSize64);
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case SFILE_VERIFY_BET_TABLE:
|
||||
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
|
||||
if(pHeader->BetTablePos64 && pHeader->BetTableSize64)
|
||||
return VerifyRawMpqData(ha, pHeader->BetTablePos64, (DWORD)pHeader->BetTableSize64);
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case SFILE_VERIFY_HASH_TABLE:
|
||||
case SFILE_VERIFY_HASH_TABLE:
|
||||
|
||||
// Hash table is not protected by MD5
|
||||
return ERROR_SUCCESS;
|
||||
// Hash table is not protected by MD5
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case SFILE_VERIFY_BLOCK_TABLE:
|
||||
case SFILE_VERIFY_BLOCK_TABLE:
|
||||
|
||||
// Block table is not protected by MD5
|
||||
return ERROR_SUCCESS;
|
||||
// Block table is not protected by MD5
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case SFILE_VERIFY_HIBLOCK_TABLE:
|
||||
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.
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
case SFILE_VERIFY_FILE:
|
||||
case SFILE_VERIFY_FILE:
|
||||
|
||||
// Verify parameters
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
// Verify parameters
|
||||
if(szFileName == NULL || *szFileName == 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
// Get the offset of a file
|
||||
pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale);
|
||||
if(pFileEntry == NULL)
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
// Get the offset of a file
|
||||
pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale);
|
||||
if(pFileEntry == NULL)
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
|
||||
return VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize);
|
||||
return VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize);
|
||||
}
|
||||
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
|
@ -896,26 +981,73 @@ DWORD WINAPI SFileVerifyArchive(HANDLE hMpq)
|
|||
TMPQArchive * ha = (TMPQArchive *)hMpq;
|
||||
|
||||
// Verify input parameters
|
||||
if(!IsValidMpqHandle(ha))
|
||||
if(!IsValidMpqHandle(hMpq))
|
||||
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
|
||||
memset(&si, 0, sizeof(MPQ_SIGNATURE_INFO));
|
||||
if(!QueryMpqSignatureInfo(ha, &si))
|
||||
return ERROR_VERIFY_FAILED;
|
||||
|
||||
// Verify the signature
|
||||
switch(si.nSignatureType)
|
||||
{
|
||||
case SIGNATURE_TYPE_NONE:
|
||||
// If there is no signature
|
||||
if(si.SignatureTypes == 0)
|
||||
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);
|
||||
|
||||
case SIGNATURE_TYPE_STRONG:
|
||||
return VerifyStrongSignature(ha, &si);
|
||||
return ERROR_NO_SIGNATURE;
|
||||
}
|
||||
|
||||
// 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
|
||||
#ifndef __SYS_ZLIB
|
||||
#include "zlib/zlib.h"
|
||||
#include "zlib/zlib.h"
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
// Include functions from bzlib
|
||||
#ifndef __SYS_BZLIB
|
||||
#include "bzip2/bzlib.h"
|
||||
#include "bzip2/bzlib.h"
|
||||
#else
|
||||
#include <bzlib.h>
|
||||
#include <bzlib.h>
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -61,56 +61,73 @@
|
|||
|
||||
#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,
|
||||
// as they are not defined on all platforms
|
||||
#define STORMLIB_MIN(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
|
||||
#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
|
||||
//
|
||||
// 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
|
||||
// (i.e not to throw exception)
|
||||
// - It is not necessary to fill the allocated buffer with zeros
|
||||
// - Memory freeing function doesn't have to test the pointer to NULL.
|
||||
// - 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
|
||||
//
|
||||
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
__inline void * DebugMalloc(char * /* szFile */, int /* nLine */, size_t nSize)
|
||||
{
|
||||
// return new BYTE[nSize];
|
||||
return HeapAlloc(GetProcessHeap(), 0, nSize);
|
||||
}
|
||||
//#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
//
|
||||
//#define STORM_ALLOC(type, nitems) (type *)HeapAlloc(GetProcessHeap(), 0, ((nitems) * sizeof(type)))
|
||||
//#define STORM_FREE(ptr) HeapFree(GetProcessHeap(), 0, ptr)
|
||||
//
|
||||
//#else
|
||||
|
||||
__inline void DebugFree(void * ptr)
|
||||
{
|
||||
// delete [] ptr;
|
||||
HeapFree(GetProcessHeap(), 0, ptr);
|
||||
}
|
||||
#define STORM_ALLOC(type, nitems) (type *)malloc((nitems) * sizeof(type))
|
||||
#define STORM_FREE(ptr) free(ptr)
|
||||
|
||||
#define STORM_ALLOC(type, nitems) (type *)DebugMalloc(__FILE__, __LINE__, (nitems) * sizeof(type))
|
||||
#define STORM_FREE(ptr) DebugFree(ptr)
|
||||
#else
|
||||
|
||||
#define STORM_ALLOC(type, nitems) (type *)malloc((nitems) * sizeof(type))
|
||||
#define STORM_FREE(ptr) free(ptr)
|
||||
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// StormLib internal global variables
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -118,8 +135,11 @@ extern LCID lcFileLocale; // Preferred file locale
|
|||
#define MPQ_HASH_NAME_A 0x100
|
||||
#define MPQ_HASH_NAME_B 0x200
|
||||
#define MPQ_HASH_FILE_KEY 0x300
|
||||
#define MPQ_HASH_KEY2_MIX 0x400
|
||||
|
||||
DWORD HashString(const char * szFileName, DWORD dwHashType);
|
||||
DWORD HashStringSlash(const char * szFileName, DWORD dwHashType);
|
||||
DWORD HashStringLower(const char * szFileName, DWORD dwHashType);
|
||||
|
||||
void InitializeMpqCryptography();
|
||||
|
||||
|
|
@ -128,15 +148,13 @@ DWORD GetHashTableSizeForFileCount(DWORD dwFileCount);
|
|||
bool IsPseudoFileName(const char * szFileName, LPDWORD pdwFileIndex);
|
||||
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);
|
||||
void DecryptMpqBlock(void * pvFileBlock, DWORD dwLength, DWORD dwKey);
|
||||
|
||||
DWORD DetectFileKeyBySectorSize(LPDWORD SectorOffsets, DWORD decrypted);
|
||||
DWORD DetectFileKeyByContent(void * pvFileContent, DWORD dwFileSize);
|
||||
DWORD DetectFileKeyBySectorSize(LPDWORD EncryptedData, DWORD dwSectorSize, DWORD dwSectorOffsLen);
|
||||
DWORD DetectFileKeyByContent(void * pvEncryptedData, DWORD dwSectorSize, DWORD dwFileSize);
|
||||
DWORD DecryptFileKey(const char * szFileName, ULONGLONG MpqPos, DWORD dwFileSize, DWORD dwFlags);
|
||||
|
||||
bool IsValidMD5(LPBYTE pbMd5);
|
||||
|
|
@ -146,29 +164,38 @@ void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_ha
|
|||
//-----------------------------------------------------------------------------
|
||||
// Handle validation functions
|
||||
|
||||
bool IsValidMpqHandle(TMPQArchive * ha);
|
||||
bool IsValidFileHandle(TMPQFile * hf);
|
||||
TMPQArchive * IsValidMpqHandle(HANDLE hMpq);
|
||||
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 * GetNextHashEntry(TMPQArchive * ha, TMPQHash * pFirstHash, TMPQHash * pPrevHash);
|
||||
DWORD AllocateHashEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
||||
DWORD AllocateHetEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
||||
TMPQHash * AllocateHashEntry(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
|
||||
int LoadMpqDataBitmap(TMPQArchive * ha, ULONGLONG FileSize, bool * pbFileIsComplete);
|
||||
TMPQHash * LoadHashTable(TMPQArchive * ha);
|
||||
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
|
||||
int CreateHashTable(TMPQArchive * ha, DWORD dwHashTableSize);
|
||||
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);
|
||||
|
||||
TMPQHetTable * CreateHetTable(DWORD dwMaxFileCount, DWORD dwHashBitSize, bool bCreateEmpty);
|
||||
TMPQHetTable * CreateHetTable(DWORD dwEntryCount, DWORD dwTotalCount, DWORD dwHashBitSize, LPBYTE pbSrcData);
|
||||
void FreeHetTable(TMPQHetTable * pHetTable);
|
||||
|
||||
TMPQBetTable * CreateBetTable(DWORD dwMaxFileCount);
|
||||
|
|
@ -181,23 +208,37 @@ TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID l
|
|||
TFileEntry * GetFileEntryByIndex(TMPQArchive * ha, DWORD dwIndex);
|
||||
|
||||
// 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
|
||||
TFileEntry * FindFreeFileEntry(TMPQArchive * ha);
|
||||
TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID lcLocale);
|
||||
int RenameFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry, const char * szNewFileName);
|
||||
void ClearFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
||||
int FreeFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
||||
void DeleteFileEntry(TMPQArchive * ha, TFileEntry * pFileEntry);
|
||||
|
||||
// Invalidates entries for (listfile) and (attributes)
|
||||
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
|
||||
|
||||
TMPQFile * CreateMpqFile(TMPQArchive * ha);
|
||||
int LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, void * pvTable, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey);
|
||||
TMPQFile * CreateFileHandle(TMPQArchive * ha, TFileEntry * pFileEntry);
|
||||
void * LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey);
|
||||
int AllocateSectorBuffer(TMPQFile * hf);
|
||||
int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile);
|
||||
int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile);
|
||||
|
|
@ -208,44 +249,48 @@ int WriteSectorOffsets(TMPQFile * hf);
|
|||
int WriteSectorChecksums(TMPQFile * hf);
|
||||
int WriteMemDataMD5(TFileStream * pStream, ULONGLONG RawDataOffs, void * pvRawData, DWORD dwRawDataSize, DWORD dwChunkSize, LPDWORD pcbTotalSize);
|
||||
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);
|
||||
int PatchFileData(TMPQFile * hf);
|
||||
|
||||
void FreeMPQArchive(TMPQArchive *& ha);
|
||||
void FreeArchiveHandle(TMPQArchive *& ha);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Utility functions
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
|
||||
int SFileAddFile_Init(
|
||||
TMPQArchive * ha,
|
||||
const char * szArchivedName,
|
||||
ULONGLONG ft,
|
||||
DWORD dwFileSize,
|
||||
LCID lcLocale,
|
||||
DWORD dwFlags,
|
||||
TMPQFile ** phf
|
||||
);
|
||||
TMPQArchive * ha,
|
||||
const char * szArchivedName,
|
||||
ULONGLONG ft,
|
||||
DWORD dwFileSize,
|
||||
LCID lcLocale,
|
||||
DWORD dwFlags,
|
||||
TMPQFile ** phf
|
||||
);
|
||||
|
||||
int SFileAddFile_Write(
|
||||
TMPQFile * hf,
|
||||
const void * pvData,
|
||||
DWORD dwSize,
|
||||
DWORD dwCompression
|
||||
);
|
||||
TMPQFile * hf,
|
||||
const void * pvData,
|
||||
DWORD dwSize,
|
||||
DWORD dwCompression
|
||||
);
|
||||
|
||||
int SFileAddFile_Finish(
|
||||
TMPQFile * hf
|
||||
);
|
||||
TMPQFile * hf
|
||||
);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Attributes support
|
||||
|
|
@ -258,6 +303,12 @@ int SAttrFileSaveToMpq(TMPQArchive * ha);
|
|||
|
||||
int SListFileSaveToMpq(TMPQArchive * ha);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Weak signature support
|
||||
|
||||
int SSignFileCreate(TMPQArchive * ha);
|
||||
int SSignFileFinish(TMPQArchive * ha);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 */
|
||||
/* 22.11.06 1.04 Sam Mac OS X compatibility (for StormLib 6.0) */
|
||||
/* 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__
|
||||
#define __STORMPORT_H__
|
||||
|
||||
#ifndef __cplusplus
|
||||
#define bool char
|
||||
#define true 1
|
||||
#define false 0
|
||||
#define bool char
|
||||
#define true 1
|
||||
#define false 0
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Defines for Windows
|
||||
|
||||
#if !defined(PLATFORM_DEFINED) && (defined(WIN32) || defined(WIN64))
|
||||
|
||||
// In MSVC 8.0, there are some functions declared as deprecated.
|
||||
#if _MSC_VER >= 1400
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
#define _CRT_NON_CONFORMING_SWPRINTFS
|
||||
#endif
|
||||
// In MSVC 8.0, there are some functions declared as deprecated.
|
||||
#if _MSC_VER >= 1400
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
#define _CRT_NON_CONFORMING_SWPRINTFS
|
||||
#endif
|
||||
|
||||
#include <tchar.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#define PLATFORM_LITTLE_ENDIAN
|
||||
#include <tchar.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <wininet.h>
|
||||
#define PLATFORM_LITTLE_ENDIAN
|
||||
|
||||
#ifdef WIN64
|
||||
#define PLATFORM_64BIT
|
||||
#else
|
||||
#define PLATFORM_32BIT
|
||||
#endif
|
||||
#ifdef WIN64
|
||||
#define PLATFORM_64BIT
|
||||
#else
|
||||
#define PLATFORM_32BIT
|
||||
#endif
|
||||
|
||||
#define PLATFORM_WINDOWS
|
||||
#define PLATFORM_DEFINED // The platform is known now
|
||||
#define PLATFORM_WINDOWS
|
||||
#define PLATFORM_DEFINED // The platform is known now
|
||||
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Defines for Mac
|
||||
|
||||
#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API
|
||||
|
||||
// Macintosh
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
// Macintosh
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define PKEXPORT
|
||||
#define __SYS_ZLIB
|
||||
#define __SYS_BZLIB
|
||||
// Support for PowerPC on Max OS X
|
||||
#if (__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1)
|
||||
#include <stdint.h>
|
||||
#include <CoreFoundation/CFByteOrder.h>
|
||||
#endif
|
||||
|
||||
#ifndef __BIG_ENDIAN__
|
||||
#define PLATFORM_LITTLE_ENDIAN
|
||||
#endif
|
||||
#define PKEXPORT
|
||||
#define __SYS_ZLIB
|
||||
#define __SYS_BZLIB
|
||||
|
||||
#define PLATFORM_MAC
|
||||
#define PLATFORM_DEFINED // The platform is known now
|
||||
#ifndef __BIG_ENDIAN__
|
||||
#define PLATFORM_LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
#define PLATFORM_MAC
|
||||
#define PLATFORM_DEFINED // The platform is known now
|
||||
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Assumption: we are not on Windows nor Macintosh, so this must be linux *grin*
|
||||
|
||||
#if !defined(PLATFORM_DEFINED)
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define PLATFORM_LITTLE_ENDIAN
|
||||
#define PLATFORM_LINUX
|
||||
#define PLATFORM_DEFINED
|
||||
#define PLATFORM_LITTLE_ENDIAN
|
||||
#define PLATFORM_LINUX
|
||||
#define PLATFORM_DEFINED
|
||||
|
||||
#endif
|
||||
|
||||
// Definition of Windows-specific structures for non-Windows platforms
|
||||
//-----------------------------------------------------------------------------
|
||||
// Definition of Windows-specific types for non-Windows platforms
|
||||
|
||||
#ifndef PLATFORM_WINDOWS
|
||||
#if __LP64__
|
||||
#define PLATFORM_64BIT
|
||||
#else
|
||||
#define PLATFORM_32BIT
|
||||
#endif
|
||||
#if __LP64__
|
||||
#define PLATFORM_64BIT
|
||||
#else
|
||||
#define PLATFORM_32BIT
|
||||
#endif
|
||||
|
||||
// Typedefs for ANSI C
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short USHORT;
|
||||
typedef int LONG;
|
||||
typedef unsigned int DWORD;
|
||||
typedef unsigned long DWORD_PTR;
|
||||
typedef long LONG_PTR;
|
||||
typedef long INT_PTR;
|
||||
typedef long long LONGLONG;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef void * HANDLE;
|
||||
typedef void * LPOVERLAPPED; // Unsupported on Linux and Mac
|
||||
typedef char TCHAR;
|
||||
typedef unsigned int LCID;
|
||||
typedef LONG * PLONG;
|
||||
typedef DWORD * LPDWORD;
|
||||
typedef BYTE * LPBYTE;
|
||||
// Typedefs for ANSI C
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short USHORT;
|
||||
typedef int LONG;
|
||||
typedef unsigned int DWORD;
|
||||
typedef unsigned long DWORD_PTR;
|
||||
typedef long LONG_PTR;
|
||||
typedef long INT_PTR;
|
||||
typedef long long LONGLONG;
|
||||
typedef unsigned long long ULONGLONG;
|
||||
typedef void * HANDLE;
|
||||
typedef void * LPOVERLAPPED; // Unsupported on Linux and Mac
|
||||
typedef char TCHAR;
|
||||
typedef unsigned int LCID;
|
||||
typedef LONG * PLONG;
|
||||
typedef DWORD * LPDWORD;
|
||||
typedef BYTE * LPBYTE;
|
||||
|
||||
#ifdef PLATFORM_32BIT
|
||||
#define _LZMA_UINT32_IS_ULONG
|
||||
#endif
|
||||
#ifdef PLATFORM_32BIT
|
||||
#define _LZMA_UINT32_IS_ULONG
|
||||
#endif
|
||||
|
||||
// Some Windows-specific defines
|
||||
#ifndef MAX_PATH
|
||||
#define MAX_PATH 1024
|
||||
#endif
|
||||
// Some Windows-specific defines
|
||||
#ifndef MAX_PATH
|
||||
#define MAX_PATH 1024
|
||||
#endif
|
||||
|
||||
#define WINAPI
|
||||
#define WINAPI
|
||||
|
||||
#define FILE_BEGIN SEEK_SET
|
||||
#define FILE_CURRENT SEEK_CUR
|
||||
#define FILE_END SEEK_END
|
||||
#define FILE_BEGIN SEEK_SET
|
||||
#define FILE_CURRENT SEEK_CUR
|
||||
#define FILE_END SEEK_END
|
||||
|
||||
#define _T(x) x
|
||||
#define _tcslen strlen
|
||||
#define _tcscpy strcpy
|
||||
#define _tcscat strcat
|
||||
#define _tcsrchr strrchr
|
||||
#define _tprintf printf
|
||||
#define _stprintf sprintf
|
||||
#define _tremove remove
|
||||
#define _T(x) x
|
||||
#define _tcslen strlen
|
||||
#define _tcscpy strcpy
|
||||
#define _tcscat strcat
|
||||
#define _tcschr strchr
|
||||
#define _tcsrchr strrchr
|
||||
#define _tcsstr strstr
|
||||
#define _tprintf printf
|
||||
#define _stprintf sprintf
|
||||
#define _tremove remove
|
||||
|
||||
#define _stricmp strcasecmp
|
||||
#define _strnicmp strncasecmp
|
||||
#define _tcsnicmp strncasecmp
|
||||
#define _stricmp strcasecmp
|
||||
#define _strnicmp strncasecmp
|
||||
#define _tcsicmp strcasecmp
|
||||
#define _tcsnicmp strncasecmp
|
||||
|
||||
#endif // !WIN32
|
||||
#endif // !PLATFORM_WINDOWS
|
||||
|
||||
// 64-bit calls are supplied by "normal" calls on Mac
|
||||
#if defined(PLATFORM_MAC)
|
||||
#define stat64 stat
|
||||
#define fstat64 fstat
|
||||
#define lseek64 lseek
|
||||
#define off64_t off_t
|
||||
#define O_LARGEFILE 0
|
||||
#define stat64 stat
|
||||
#define fstat64 fstat
|
||||
#define lseek64 lseek
|
||||
#define ftruncate64 ftruncate
|
||||
#define off64_t off_t
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
|
||||
// Platform-specific error codes for UNIX-based platforms
|
||||
#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX)
|
||||
#define ERROR_SUCCESS 0
|
||||
#define ERROR_FILE_NOT_FOUND ENOENT
|
||||
#define ERROR_ACCESS_DENIED EPERM
|
||||
#define ERROR_INVALID_HANDLE EBADF
|
||||
#define ERROR_NOT_ENOUGH_MEMORY ENOMEM
|
||||
#define ERROR_BAD_FORMAT 105 // No such error code under Linux
|
||||
#define ERROR_NO_MORE_FILES 106
|
||||
#define ERROR_HANDLE_EOF 107 // No such error code under Linux
|
||||
#define ERROR_NOT_SUPPORTED ENOTSUP
|
||||
#define ERROR_INVALID_PARAMETER EINVAL
|
||||
#define ERROR_DISK_FULL ENOSPC
|
||||
#define ERROR_ALREADY_EXISTS EEXIST
|
||||
#define ERROR_CAN_NOT_COMPLETE 108 // No such error code under Linux
|
||||
#define ERROR_FILE_CORRUPT 109 // No such error code under Linux
|
||||
#define ERROR_INSUFFICIENT_BUFFER ENOBUFS
|
||||
#define ERROR_SUCCESS 0
|
||||
#define ERROR_FILE_NOT_FOUND ENOENT
|
||||
#define ERROR_ACCESS_DENIED EPERM
|
||||
#define ERROR_INVALID_HANDLE EBADF
|
||||
#define ERROR_NOT_ENOUGH_MEMORY ENOMEM
|
||||
#define ERROR_NOT_SUPPORTED ENOTSUP
|
||||
#define ERROR_INVALID_PARAMETER EINVAL
|
||||
#define ERROR_DISK_FULL ENOSPC
|
||||
#define ERROR_ALREADY_EXISTS EEXIST
|
||||
#define ERROR_INSUFFICIENT_BUFFER ENOBUFS
|
||||
#define ERROR_BAD_FORMAT 1000 // No such error code under Linux
|
||||
#define ERROR_NO_MORE_FILES 1001 // No such error code under Linux
|
||||
#define ERROR_HANDLE_EOF 1002 // No such error code under Linux
|
||||
#define ERROR_CAN_NOT_COMPLETE 1003 // No such error code under Linux
|
||||
#define ERROR_FILE_CORRUPT 1004 // No such error code under Linux
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Swapping functions
|
||||
|
||||
#ifdef PLATFORM_LITTLE_ENDIAN
|
||||
#define BSWAP_INT16_UNSIGNED(a) (a)
|
||||
#define BSWAP_INT16_SIGNED(a) (a)
|
||||
#define BSWAP_INT32_UNSIGNED(a) (a)
|
||||
#define BSWAP_INT32_SIGNED(a) (a)
|
||||
#define BSWAP_INT64_SIGNED(a) (a)
|
||||
#define BSWAP_INT64_UNSIGNED(a) (a)
|
||||
#define BSWAP_ARRAY16_UNSIGNED(a,b) {}
|
||||
#define BSWAP_ARRAY32_UNSIGNED(a,b) {}
|
||||
#define BSWAP_ARRAY64_UNSIGNED(a,b) {}
|
||||
#define BSWAP_PART_HEADER(a) {}
|
||||
#define BSWAP_TMPQUSERDATA(a) {}
|
||||
#define BSWAP_TMPQHEADER(a) {}
|
||||
#define BSWAP_INT16_UNSIGNED(a) (a)
|
||||
#define BSWAP_INT16_SIGNED(a) (a)
|
||||
#define BSWAP_INT32_UNSIGNED(a) (a)
|
||||
#define BSWAP_INT32_SIGNED(a) (a)
|
||||
#define BSWAP_INT64_SIGNED(a) (a)
|
||||
#define BSWAP_INT64_UNSIGNED(a) (a)
|
||||
#define BSWAP_ARRAY16_UNSIGNED(a,b) {}
|
||||
#define BSWAP_ARRAY32_UNSIGNED(a,b) {}
|
||||
#define BSWAP_ARRAY64_UNSIGNED(a,b) {}
|
||||
#define BSWAP_PART_HEADER(a) {}
|
||||
#define BSWAP_TMPQHEADER(a,b) {}
|
||||
#define BSWAP_TMPKHEADER(a) {}
|
||||
#else
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
int16_t SwapInt16(uint16_t);
|
||||
uint16_t SwapUInt16(uint16_t);
|
||||
int32_t SwapInt32(uint32_t);
|
||||
uint32_t SwapUInt32(uint32_t);
|
||||
int64_t SwapInt64(uint64_t);
|
||||
uint64_t SwapUInt64(uint64_t);
|
||||
void ConvertUInt16Buffer(void * ptr, size_t length);
|
||||
void ConvertUInt32Buffer(void * ptr, size_t length);
|
||||
void ConvertUInt64Buffer(void * ptr, size_t length);
|
||||
void ConvertPartHeader(void * partHeader);
|
||||
void ConvertTMPQUserData(void *userData);
|
||||
void ConvertTMPQHeader(void *header);
|
||||
int16_t SwapInt16(uint16_t);
|
||||
uint16_t SwapUInt16(uint16_t);
|
||||
int32_t SwapInt32(uint32_t);
|
||||
uint32_t SwapUInt32(uint32_t);
|
||||
int64_t SwapInt64(uint64_t);
|
||||
uint64_t SwapUInt64(uint64_t);
|
||||
void ConvertUInt16Buffer(void * ptr, size_t length);
|
||||
void ConvertUInt32Buffer(void * ptr, size_t length);
|
||||
void ConvertUInt64Buffer(void * ptr, size_t length);
|
||||
void ConvertTMPQUserData(void *userData);
|
||||
void ConvertTMPQHeader(void *header, uint16_t wPart);
|
||||
void ConvertTMPKHeader(void *header);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#define BSWAP_INT16_SIGNED(a) SwapInt16((a))
|
||||
#define BSWAP_INT16_UNSIGNED(a) SwapUInt16((a))
|
||||
#define BSWAP_INT32_SIGNED(a) SwapInt32((a))
|
||||
#define BSWAP_INT32_UNSIGNED(a) SwapUInt32((a))
|
||||
#define BSWAP_INT64_SIGNED(a) SwapInt64((a))
|
||||
#define BSWAP_INT64_UNSIGNED(a) SwapUInt64((a))
|
||||
#define BSWAP_ARRAY16_UNSIGNED(a,b) ConvertUInt16Buffer((a),(b))
|
||||
#define BSWAP_ARRAY32_UNSIGNED(a,b) ConvertUInt32Buffer((a),(b))
|
||||
#define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b))
|
||||
#define BSWAP_PART_HEADER(a) ConvertPartHeader(a)
|
||||
#define BSWAP_TMPQUSERDATA(a) ConvertTMPQUserData((a))
|
||||
#define BSWAP_TMPQHEADER(a) ConvertTMPQHeader((a))
|
||||
#define BSWAP_INT16_SIGNED(a) SwapInt16((a))
|
||||
#define BSWAP_INT16_UNSIGNED(a) SwapUInt16((a))
|
||||
#define BSWAP_INT32_SIGNED(a) SwapInt32((a))
|
||||
#define BSWAP_INT32_UNSIGNED(a) SwapUInt32((a))
|
||||
#define BSWAP_INT64_SIGNED(a) SwapInt64((a))
|
||||
#define BSWAP_INT64_UNSIGNED(a) SwapUInt64((a))
|
||||
#define BSWAP_ARRAY16_UNSIGNED(a,b) ConvertUInt16Buffer((a),(b))
|
||||
#define BSWAP_ARRAY32_UNSIGNED(a,b) ConvertUInt32Buffer((a),(b))
|
||||
#define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b))
|
||||
#define BSWAP_TMPQHEADER(a,b) ConvertTMPQHeader((a),(b))
|
||||
#define BSWAP_TMPKHEADER(a) ConvertTMPKHeader((a))
|
||||
#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__
|
||||
|
|
|
|||
|
|
@ -10,349 +10,389 @@
|
|||
/* 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 */
|
||||
/* 10.01.13 3.00 Lad Refactored, beautified, documented :-) */
|
||||
/*****************************************************************************/
|
||||
|
||||
#include "../StormPort.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
|
||||
|
||||
static long Table1503F120[] =
|
||||
static int NextStepTable[] =
|
||||
{
|
||||
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
|
||||
-1, 0, -1, 4, -1, 2, -1, 6,
|
||||
-1, 1, -1, 5, -1, 3, -1, 7,
|
||||
-1, 1, -1, 5, -1, 3, -1, 7,
|
||||
-1, 2, -1, 4, -1, 6, -1, 8
|
||||
};
|
||||
|
||||
static long step_table[] =
|
||||
static int StepSizeTable[] =
|
||||
{
|
||||
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
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
16, 17, 19, 21, 23, 25, 28, 31,
|
||||
34, 37, 41, 45, 50, 55, 60, 66,
|
||||
73, 80, 88, 97, 107, 118, 130, 143,
|
||||
157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658,
|
||||
724, 796, 876, 963, 1060, 1166, 1282, 1411,
|
||||
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
|
||||
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
|
||||
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
|
||||
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
|
||||
int CompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, short * pwInBuffer, int dwInLength, int nChannels, int nCmpLevel)
|
||||
// ECX EDX
|
||||
static inline short GetNextStepIndex(int StepIndex, unsigned int EncodedSample)
|
||||
{
|
||||
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;
|
||||
// Get the next step index
|
||||
StepIndex = StepIndex + NextStepTable[EncodedSample & 0x1F];
|
||||
|
||||
// If less than 2 bytes remain, don't decompress anything
|
||||
// pbSaveOutBuffer = pbOutBuffer;
|
||||
out.pb = pbOutBuffer;
|
||||
if(nBytesRemains < 2)
|
||||
return 2;
|
||||
// Don't make the step index overflow
|
||||
if(StepIndex < 0)
|
||||
StepIndex = 0;
|
||||
else if(StepIndex > 88)
|
||||
StepIndex = 88;
|
||||
|
||||
Wcmp.b[1] = (unsigned char)(nCmpLevel - 1);
|
||||
Wcmp.b[0] = (unsigned char)0;
|
||||
return (short)StepIndex;
|
||||
}
|
||||
|
||||
*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++)
|
||||
static inline int UpdatePredictedSample(int PredictedSample, int EncodedSample, int Difference)
|
||||
{
|
||||
// Is the sign bit set?
|
||||
if(EncodedSample & 0x40)
|
||||
{
|
||||
nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++);
|
||||
*out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord);
|
||||
SInt32Array2[i] = nOneWord;
|
||||
PredictedSample -= Difference;
|
||||
if(PredictedSample <= -32768)
|
||||
PredictedSample = -32768;
|
||||
}
|
||||
else
|
||||
{
|
||||
PredictedSample += Difference;
|
||||
if(PredictedSample >= 32767)
|
||||
PredictedSample = 32767;
|
||||
}
|
||||
|
||||
// Weird. But it's there
|
||||
nLength = dwInLength;
|
||||
if(nLength < 0) // mov eax, dwInLength; cdq; sub eax, edx;
|
||||
nLength++;
|
||||
return PredictedSample;
|
||||
}
|
||||
|
||||
nLength = (nLength / 2) - (int)(out.pb - pbOutBuffer);
|
||||
nLength = (nLength < 0) ? 0 : nLength;
|
||||
static inline int DecodeSample(int PredictedSample, int EncodedSample, int StepSize, int Difference)
|
||||
{
|
||||
if(EncodedSample & 0x01)
|
||||
Difference += (StepSize >> 0);
|
||||
|
||||
nIndex = nChannels - 1; // edi
|
||||
nWordsRemains = dwInLength / 2; // eax
|
||||
if(EncodedSample & 0x02)
|
||||
Difference += (StepSize >> 1);
|
||||
|
||||
// ebx - nChannels
|
||||
// ecx - pwOutPos
|
||||
for(chnl = nChannels; chnl < nWordsRemains; chnl++)
|
||||
{
|
||||
// 1500F030
|
||||
if((out.pb - pbOutBuffer + 2) > nBytesRemains)
|
||||
return (int)(out.pb - pbOutBuffer + 2);
|
||||
if(EncodedSample & 0x04)
|
||||
Difference += (StepSize >> 2);
|
||||
|
||||
// Switch index
|
||||
if(nChannels == 2)
|
||||
nIndex = (nIndex == 0) ? 1 : 0;
|
||||
if(EncodedSample & 0x08)
|
||||
Difference += (StepSize >> 3);
|
||||
|
||||
// Load one word from the input stream
|
||||
nOneWord = BSWAP_INT16_SIGNED(*pwInBuffer++); // ecx - nOneWord
|
||||
SInt32Array3[nIndex] = nOneWord;
|
||||
if(EncodedSample & 0x10)
|
||||
Difference += (StepSize >> 4);
|
||||
|
||||
// esi - SInt32Array2[nIndex]
|
||||
// eax - nValue
|
||||
nValue = nOneWord - SInt32Array2[nIndex];
|
||||
nValue = (nValue < 0) ? ((nValue ^ 0xFFFFFFFF) + 1) : nValue;
|
||||
if(EncodedSample & 0x20)
|
||||
Difference += (StepSize >> 5);
|
||||
|
||||
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);
|
||||
return UpdatePredictedSample(PredictedSample, EncodedSample, Difference);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// DecompressADPCM
|
||||
// Compression routine
|
||||
|
||||
// 1500F230
|
||||
int DecompressADPCM(unsigned char * pbOutBuffer, int dwOutLength, unsigned char * pbInBuffer, int dwInLength, int nChannels)
|
||||
int CompressADPCM(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount, int CompressionLevel)
|
||||
{
|
||||
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;
|
||||
TADPCMStream os(pvOutBuffer, cbOutBuffer); // The output stream
|
||||
TADPCMStream is(pvInBuffer, cbInBuffer); // The input stream
|
||||
unsigned char BitShift = (unsigned char)(CompressionLevel - 1);
|
||||
short PredictedSamples[MAX_ADPCM_CHANNEL_COUNT];// Predicted samples for each channel
|
||||
short StepIndexes[MAX_ADPCM_CHANNEL_COUNT]; // Step indexes for each channel
|
||||
short InputSample; // Input sample for the current channel
|
||||
int TotalStepSize;
|
||||
int ChannelIndex;
|
||||
int AbsDifference;
|
||||
int Difference;
|
||||
int MaxBitMask;
|
||||
int StepSize;
|
||||
|
||||
SInt32Array1[0] = SInt32Array1[1] = 0x2C;
|
||||
out.pb = pbOutBuffer;
|
||||
in.pb = pbInBuffer;
|
||||
in.pw++;
|
||||
// _tprintf(_T("== CMPR Started ==============\n"));
|
||||
|
||||
// Fill the Uint32Array2 array by channel values.
|
||||
for(i = 0; i < nChannels; i++)
|
||||
// First byte in the output stream contains zero. The second one contains the compression level
|
||||
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++);
|
||||
SInt32Array2[i] = nOneWord;
|
||||
if(dwOutLength < 2)
|
||||
return (int)(out.pb - pbOutBuffer);
|
||||
// Get the initial sample from the input stream
|
||||
if(!is.ReadWordSample(InputSample))
|
||||
return os.LengthProcessed(pvOutBuffer);
|
||||
|
||||
*out.pw++ = BSWAP_INT16_SIGNED((short)nOneWord);
|
||||
dwOutLength -= sizeof(short);
|
||||
// Store the initial sample to our sample array
|
||||
PredictedSamples[i] = InputSample;
|
||||
|
||||
// Also store the loaded sample to the output stream
|
||||
if(!os.WriteWordSample(InputSample))
|
||||
return os.LengthProcessed(pvOutBuffer);
|
||||
}
|
||||
|
||||
// Get the initial index
|
||||
nIndex = nChannels - 1;
|
||||
ChannelIndex = ChannelCount - 1;
|
||||
|
||||
// Perform the decompression
|
||||
while(in.pb < pbInBufferEnd)
|
||||
// Now keep reading the input data as long as there is something in the input buffer
|
||||
while(is.ReadWordSample(InputSample))
|
||||
{
|
||||
unsigned char nOneByte = *in.pb++;
|
||||
int EncodedSample = 0;
|
||||
|
||||
// Switch index
|
||||
if(nChannels == 2)
|
||||
nIndex = (nIndex == 0) ? 1 : 0;
|
||||
// If we have two channels, we need to flip the channel index
|
||||
ChannelIndex = (ChannelIndex + 1) % ChannelCount;
|
||||
|
||||
// 1500F2A2: Get one byte from input buffer
|
||||
if(nOneByte & 0x80)
|
||||
// Get the difference from the previous sample.
|
||||
// If the difference is negative, set the sign bit to the encoded sample
|
||||
AbsDifference = InputSample - PredictedSamples[ChannelIndex];
|
||||
if(AbsDifference < 0)
|
||||
{
|
||||
switch(nOneByte & 0x7F)
|
||||
{
|
||||
case 0: // 1500F315
|
||||
if(SInt32Array1[nIndex] != 0)
|
||||
SInt32Array1[nIndex]--;
|
||||
AbsDifference = -AbsDifference;
|
||||
EncodedSample |= 0x40;
|
||||
}
|
||||
|
||||
if(dwOutLength < 2)
|
||||
return (int)(out.pb - pbOutBuffer);
|
||||
// If the difference is too low (higher that difference treshold),
|
||||
// write a step index modifier marker
|
||||
StepSize = StepSizeTable[StepIndexes[ChannelIndex]];
|
||||
if(AbsDifference < (StepSize >> CompressionLevel))
|
||||
{
|
||||
if(StepIndexes[ChannelIndex] != 0)
|
||||
StepIndexes[ChannelIndex]--;
|
||||
|
||||
*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;
|
||||
}
|
||||
os.WriteByteSample(0x80);
|
||||
}
|
||||
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)
|
||||
// If the difference is too high, write marker that
|
||||
// indicates increase in step size
|
||||
while(AbsDifference > (StepSize << 1))
|
||||
{
|
||||
temp3 = temp3 - temp2;
|
||||
if(temp3 <= -32768)
|
||||
temp3 = -32768;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp3 = temp3 + temp2;
|
||||
if(temp3 >= 32767)
|
||||
temp3 = 32767;
|
||||
if(StepIndexes[ChannelIndex] >= 0x58)
|
||||
break;
|
||||
|
||||
// Modify the step index
|
||||
StepIndexes[ChannelIndex] += 8;
|
||||
if(StepIndexes[ChannelIndex] > 0x58)
|
||||
StepIndexes[ChannelIndex] = 0x58;
|
||||
|
||||
// Write the "modify step index" marker
|
||||
StepSize = StepSizeTable[StepIndexes[ChannelIndex]];
|
||||
os.WriteByteSample(0x81);
|
||||
}
|
||||
|
||||
SInt32Array2[nIndex] = temp3;
|
||||
if(dwOutLength < 2)
|
||||
// Get the limit bit value
|
||||
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;
|
||||
|
||||
// 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;
|
||||
// Calculates the step index to use for the next encode
|
||||
StepIndexes[ChannelIndex] = GetNextStepIndex(StepIndexes[ChannelIndex], EncodedSample);
|
||||
}
|
||||
}
|
||||
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__
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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__
|
||||
|
|
|
|||
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
|
|
@ -13,21 +13,11 @@
|
|||
#ifndef __HUFFMAN_H__
|
||||
#define __HUFFMAN_H__
|
||||
|
||||
#include "../StormPort.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Defines
|
||||
|
||||
#define INSERT_ITEM 1
|
||||
#define SWITCH_ITEMS 2 // Switch the item1 and item2
|
||||
|
||||
#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
|
||||
#define HUFF_ITEM_COUNT 0x203 // Number of items in the item pool
|
||||
#define LINK_ITEM_COUNT 0x80 // Maximum number of quick-link items
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Structures and classes
|
||||
|
|
@ -37,66 +27,77 @@ class TInputStream
|
|||
{
|
||||
public:
|
||||
|
||||
unsigned long GetBit();
|
||||
unsigned long Get7Bits();
|
||||
unsigned long Get8Bits();
|
||||
TInputStream(void * pvInBuffer, size_t cbInBuffer);
|
||||
unsigned int Get1Bit();
|
||||
unsigned int Peek7Bits();
|
||||
unsigned int Get8Bits();
|
||||
void SkipBits(unsigned int BitCount);
|
||||
|
||||
unsigned char * pbInBuffer; // Input data
|
||||
unsigned char * pbInBufferEnd; // End of the input buffer
|
||||
unsigned long BitBuffer; // Input bit buffer
|
||||
unsigned int BitCount; // Number of bits remaining in 'dwBitBuff'
|
||||
unsigned char * pbInBufferEnd; // End position in the the input buffer
|
||||
unsigned char * pbInBuffer; // Current position in the the input buffer
|
||||
unsigned int BitBuffer; // Input bit buffer
|
||||
unsigned int BitCount; // Number of bits remaining in 'dwBitBuff'
|
||||
};
|
||||
|
||||
|
||||
// Output stream for Huffmann compression
|
||||
class TOutputStream
|
||||
{
|
||||
public:
|
||||
|
||||
void PutBits(unsigned long dwBuff, unsigned int nPutBits);
|
||||
TOutputStream(void * pvOutBuffer, size_t cbOutLength);
|
||||
void PutBits(unsigned int dwValue, unsigned int nBitCount);
|
||||
void Flush();
|
||||
|
||||
unsigned char * pbOutBuffer; // 00 : Output buffer
|
||||
unsigned long cbOutSize; // 04 : Size of output buffer
|
||||
unsigned char * pbOutPos; // 08 : Current output position
|
||||
unsigned long dwBitBuff; // 0C : Bit buffer
|
||||
unsigned long nBits; // 10 : Number of bits in the bit buffer
|
||||
unsigned char * pbOutBufferEnd; // End position in the output buffer
|
||||
unsigned char * pbOutBuffer; // Current position in the output 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
|
||||
{
|
||||
THTreeItem * Call1501DB70(THTreeItem * pLast);
|
||||
THTreeItem * GetPrevItem(LONG_PTR value);
|
||||
void ClearItemLinks();
|
||||
void RemoveItem();
|
||||
THTreeItem() { pPrev = pNext = NULL;}
|
||||
// ~THTreeItem() { RemoveItem(); }
|
||||
|
||||
THTreeItem * next; // 00 - Pointer to next THTreeItem
|
||||
THTreeItem * prev; // 04 - Pointer to prev THTreeItem (< 0 if none)
|
||||
unsigned long dcmpByte; // 08 - Index of this item in item pointer array, decompressed byte value
|
||||
unsigned long byteValue; // 0C - Some byte value
|
||||
THTreeItem * parent; // 10 - Pointer to parent THTreeItem (NULL if none)
|
||||
THTreeItem * child; // 14 - Pointer to child THTreeItem
|
||||
int addressMultiplier; // -1 if object on negative address (>0x80000000), +1 if positive
|
||||
void RemoveItem();
|
||||
// void RemoveEntry();
|
||||
|
||||
THTreeItem * pNext; // Pointer to lower-weight tree item
|
||||
THTreeItem * pPrev; // Pointer to higher-weight item
|
||||
unsigned int DecompressedValue; // 08 - Decompressed byte value (also index in the array)
|
||||
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.
|
||||
// After each walk through Huffman tree are filled all entries which are
|
||||
// multiplies of number of bits loaded from input stream. These entries
|
||||
// contain number of bits and result value. At the next 7 bits is tested this
|
||||
// structure first. If corresponding entry found, decompression routine will
|
||||
// not walk through Huffman tree and directly stores output byte to output stream.
|
||||
struct TQDecompress
|
||||
|
||||
// Structure used for quick navigating in the huffmann tree.
|
||||
// Allows skipping up to 7 bits in the compressed stream, thus
|
||||
// decompressing a bit faster. Sometimes it can even get the decompressed
|
||||
// byte directly.
|
||||
struct TQuickLink
|
||||
{
|
||||
unsigned long offs00; // 00 - 1 if resolved
|
||||
unsigned long nBits; // 04 - Bit count
|
||||
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
|
||||
union
|
||||
{
|
||||
unsigned long dcmpByte; // 08 - Byte value for decompress (if bitCount <= 7)
|
||||
THTreeItem * pItem; // 08 - THTreeItem (if number of bits is greater than 7
|
||||
THTreeItem * pItem; // Pointer to the item within the Huffmann tree
|
||||
unsigned int DecompressedValue; // Value for direct decompression
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// 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
|
||||
// tree. If someone knows the decompression details, please let me know
|
||||
|
|
@ -104,39 +105,39 @@ class THuffmannTree
|
|||
{
|
||||
public:
|
||||
|
||||
THuffmannTree();
|
||||
void InitTree(bool bCompression);
|
||||
void BuildTree(unsigned int nCmpType);
|
||||
// void ModifyTree(unsigned long dwIndex);
|
||||
// void UninitTree();
|
||||
THuffmannTree(bool bCompression);
|
||||
~THuffmannTree();
|
||||
|
||||
// void Call15007010(Bit32 dwInLength, THTreeItem * item);
|
||||
THTreeItem * Call1500E740(unsigned int nValue);
|
||||
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);
|
||||
void LinkTwoItems(THTreeItem * pItem1, THTreeItem * pItem2);
|
||||
void InsertItem(THTreeItem * item, TInsertPoint InsertPoint, THTreeItem * item2);
|
||||
|
||||
unsigned long bIsCmp0; // 0000 - 1 if compression type 0
|
||||
unsigned long offs0004; // 0004 - Some flag
|
||||
THTreeItem items0008[0x203]; // 0008 - HTree items
|
||||
THTreeItem * FindHigherOrEqualItem(THTreeItem * pItem, unsigned int Weight);
|
||||
THTreeItem * CreateNewItem(unsigned int DecompressedValue, unsigned int Weight, TInsertPoint InsertPoint);
|
||||
|
||||
//- Sometimes used as HTree item -----------
|
||||
THTreeItem * pItem3050; // 3050 - Always NULL (?)
|
||||
THTreeItem * pItem3054; // 3054 - Pointer to Huffman tree item
|
||||
THTreeItem * pItem3058; // 3058 - Pointer to Huffman tree item (< 0 if invalid)
|
||||
unsigned int FixupItemPosByWeight(THTreeItem * pItem, unsigned int MaxWeight);
|
||||
void BuildTree(unsigned int CompressionType);
|
||||
|
||||
//- Sometimes used as HTree item -----------
|
||||
THTreeItem * pItem305C; // 305C - Usually NULL
|
||||
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
|
||||
void IncWeightsAndRebalance(THTreeItem * pItem);
|
||||
void InsertNewBranchAndRebalance(unsigned int Value1, unsigned int Value2);
|
||||
|
||||
//-------------------------------------------
|
||||
THTreeItem * items306C[0x102]; // 306C - THTreeItem pointer array
|
||||
TQDecompress qd3474[0x80]; // 3474 - Array for quick decompression
|
||||
int addressMultiplier; // -1 if object on negative address (>0x80000000), +1 if positive
|
||||
void EncodeOneByte(TOutputStream * os, THTreeItem * pItem);
|
||||
unsigned int DecodeOneByte(TInputStream * is);
|
||||
|
||||
static unsigned char Table1502A630[];// Some table
|
||||
unsigned int Compress(TOutputStream * os, void * pvInBuffer, int cbInBuffer, int nCmpType);
|
||||
unsigned int Decompress(void * pvOutBuffer, unsigned int cbOutLength, TInputStream * is);
|
||||
|
||||
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
|
||||
|
||||
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__
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
|
|||
u.ptr = key;
|
||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
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) */
|
||||
while (length > 12)
|
||||
|
|
@ -336,7 +336,7 @@ uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
|
|||
|
||||
#else /* make valgrind happy */
|
||||
|
||||
k8 = (const uint8_t *)k;
|
||||
const uint8_t *k8 = (const uint8_t *)k;
|
||||
switch(length)
|
||||
{
|
||||
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)) {
|
||||
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 */
|
||||
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 */
|
||||
k8 = (const uint8_t *)k;
|
||||
const uint8_t *k8 = (const uint8_t *)k;
|
||||
switch(length)
|
||||
{
|
||||
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
|
||||
|
|
@ -477,7 +477,7 @@ void hashlittle2(
|
|||
u.ptr = key;
|
||||
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
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) */
|
||||
while (length > 12)
|
||||
|
|
@ -521,7 +521,7 @@ void hashlittle2(
|
|||
|
||||
#else /* make valgrind happy */
|
||||
|
||||
k8 = (const uint8_t *)k;
|
||||
const uint8_t *k8 = (const uint8_t *)k;
|
||||
switch(length)
|
||||
{
|
||||
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;
|
||||
if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||
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) */
|
||||
while (length > 12)
|
||||
|
|
@ -698,7 +698,7 @@ uint32_t hashbig( const void *key, size_t length, uint32_t initval)
|
|||
|
||||
#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 */
|
||||
{
|
||||
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@
|
|||
*/
|
||||
#include "../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file hash_memory.c
|
||||
Hash memory helper, Tom St Denis
|
||||
*/
|
||||
|
||||
/*
|
||||
/**
|
||||
Hash a block of memory and store the digest.
|
||||
@param hash The index of the hash you wish to use
|
||||
@param in The data you wish to hash
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#include "../headers/tomcrypt.h"
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
@file md5.c
|
||||
LTC_MD5 hash function by Tom St Denis
|
||||
*/
|
||||
|
|
@ -225,7 +225,7 @@ static int md5_compress(hash_state *md, unsigned char *buf)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
/**
|
||||
Initialize the hash state
|
||||
@param md The hash state you wish to initialize
|
||||
@return CRYPT_OK if successful
|
||||
|
|
@ -242,7 +242,7 @@ int md5_init(hash_state * md)
|
|||
return CRYPT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
Process a block of memory though the hash
|
||||
@param md The hash state
|
||||
@param in The data to hash
|
||||
|
|
@ -251,7 +251,7 @@ int md5_init(hash_state * md)
|
|||
*/
|
||||
HASH_PROCESS(md5_process, md5_compress, md5, 64)
|
||||
|
||||
/*
|
||||
/**
|
||||
Terminate the hash to get the digest
|
||||
@param md The hash state
|
||||
@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;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
Self-test the hash
|
||||
@return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file sha1.c
|
||||
LTC_SHA1 code by Tom St Denis
|
||||
*/
|
||||
|
|
@ -157,7 +157,7 @@ static int sha1_compress(hash_state *md, unsigned char *buf)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
/**
|
||||
Initialize the hash state
|
||||
@param md The hash state you wish to initialize
|
||||
@return CRYPT_OK if successful
|
||||
|
|
@ -175,7 +175,7 @@ int sha1_init(hash_state * md)
|
|||
return CRYPT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
Process a block of memory though the hash
|
||||
@param md The hash state
|
||||
@param in The data to hash
|
||||
|
|
@ -184,7 +184,7 @@ int sha1_init(hash_state * md)
|
|||
*/
|
||||
HASH_PROCESS(sha1_process, sha1_compress, sha1, 64)
|
||||
|
||||
/*
|
||||
/**
|
||||
Terminate the hash to get the digest
|
||||
@param md The hash state
|
||||
@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;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
Self-test the hash
|
||||
@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},
|
||||
};
|
||||
|
||||
/*
|
||||
/**
|
||||
Convert a MPI error to a LTC error (Possibly the most powerful function ever! Oh wait... no)
|
||||
@param err The error to convert
|
||||
@return The equivalent LTC error code or CRYPT_ERROR if none found
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file rand_prime.c
|
||||
Generate a random prime, Tom St Denis
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file base64_decode.c
|
||||
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 };
|
||||
|
||||
/*
|
||||
/**
|
||||
base64 decode a block of memory
|
||||
@param in The base64 data to decode
|
||||
@param inlen The length of the base64 data
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
#include "../headers/tomcrypt.h"
|
||||
#include <signal.h>
|
||||
|
||||
/*
|
||||
/**
|
||||
@file crypt_argchk.c
|
||||
Perform argument checking, Tom St Denis
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@
|
|||
*/
|
||||
#include "../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file crypt_find_hash.c
|
||||
Find a hash, Tom St Denis
|
||||
*/
|
||||
|
||||
/*
|
||||
/**
|
||||
Find a registered hash by name
|
||||
@param name The name of the hash to look for
|
||||
@return >= 0 if found, -1 if not present
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@
|
|||
*/
|
||||
#include "../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file crypt_find_prng.c
|
||||
Find a PRNG, Tom St Denis
|
||||
*/
|
||||
|
||||
/*
|
||||
/**
|
||||
Find a registered PRNG by name
|
||||
@param name The name of the PRNG to look for
|
||||
@return >= 0 if found, -1 if not present
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file crypt_hash_descriptor.c
|
||||
Stores the hash descriptor table, Tom St Denis
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file crypt_hash_is_valid.c
|
||||
Determine if hash is valid, Tom St Denis
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file crypt_prng_descriptor.c
|
||||
Stores the PRNG descriptors, Tom St Denis
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file crypt_prng_is_valid.c
|
||||
Determine if PRNG is valid, Tom St Denis
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@
|
|||
*/
|
||||
#include "../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file crypt_register_hash.c
|
||||
Register a HASH, Tom St Denis
|
||||
*/
|
||||
|
||||
/*
|
||||
/**
|
||||
Register a hash with the descriptor table
|
||||
@param hash The hash you wish to register
|
||||
@return value >= 0 if successfully added (or already present), -1 if unsuccessful
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@
|
|||
*/
|
||||
#include "../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file crypt_register_prng.c
|
||||
Register a PRNG, Tom St Denis
|
||||
*/
|
||||
|
||||
/*
|
||||
/**
|
||||
Register a PRNG with the descriptor table
|
||||
@param prng The PRNG you wish to register
|
||||
@return value >= 0 if successfully added (or already present), -1 if unsuccessful
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@
|
|||
*/
|
||||
#include "../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file zeromem.c
|
||||
Zero a block of memory, Tom St Denis
|
||||
*/
|
||||
|
||||
/*
|
||||
/**
|
||||
Zero a block of memory
|
||||
@param out The destination of the area to zero
|
||||
@param outlen The length of the area to zero (octets)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_bit_string.c
|
||||
ASN.1 DER, encode a BIT STRING, Tom St Denis
|
||||
*/
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#ifdef LTC_DER
|
||||
|
||||
/*
|
||||
/**
|
||||
Store a BIT STRING
|
||||
@param in The DER encoded BIT STRING
|
||||
@param inlen The size of the DER BIT STRING
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_boolean.c
|
||||
ASN.1 DER, decode a BOOLEAN, Tom St Denis
|
||||
*/
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#ifdef LTC_DER
|
||||
|
||||
/*
|
||||
/**
|
||||
Read a BOOLEAN
|
||||
@param in The destination for the DER encoded BOOLEAN
|
||||
@param inlen The size of the DER BOOLEAN
|
||||
|
|
|
|||
|
|
@ -10,14 +10,14 @@
|
|||
*/
|
||||
#include "../../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_choice.c
|
||||
ASN.1 DER, decode a CHOICE, Tom St Denis
|
||||
*/
|
||||
|
||||
#ifdef LTC_DER
|
||||
|
||||
/*
|
||||
/**
|
||||
Decode a CHOICE
|
||||
@param in The DER encoded input
|
||||
@param inlen [in/out] The size of the input and resulting size of read type
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_ia5_string.c
|
||||
ASN.1 DER, encode a IA5 STRING, Tom St Denis
|
||||
*/
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#ifdef LTC_DER
|
||||
|
||||
/*
|
||||
/**
|
||||
Store a IA5 STRING
|
||||
@param in The DER encoded IA5 STRING
|
||||
@param inlen The size of the DER IA5 STRING
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_integer.c
|
||||
ASN.1 DER, decode an integer, Tom St Denis
|
||||
*/
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#ifdef LTC_DER
|
||||
|
||||
/*
|
||||
/**
|
||||
Read a mp_int integer
|
||||
@param in The DER encoded data
|
||||
@param inlen Size of DER encoded data
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@
|
|||
*/
|
||||
#include "../../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_object_identifier.c
|
||||
ASN.1 DER, Decode Object Identifier, Tom St Denis
|
||||
*/
|
||||
|
||||
#ifdef LTC_DER
|
||||
/*
|
||||
/**
|
||||
Decode OID data and store the array of integers in words
|
||||
@param in The OID DER encoded data
|
||||
@param inlen The length of the OID data
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_octet_string.c
|
||||
ASN.1 DER, encode a OCTET STRING, Tom St Denis
|
||||
*/
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#ifdef LTC_DER
|
||||
|
||||
/*
|
||||
/**
|
||||
Store a OCTET STRING
|
||||
@param in The DER encoded OCTET STRING
|
||||
@param inlen The size of the DER OCTET STRING
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_printable_string.c
|
||||
ASN.1 DER, encode a printable STRING, Tom St Denis
|
||||
*/
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#ifdef LTC_DER
|
||||
|
||||
/*
|
||||
/**
|
||||
Store a printable STRING
|
||||
@param in The DER encoded printable STRING
|
||||
@param inlen The size of the DER printable STRING
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@
|
|||
#include <stdarg.h>
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_sequence_ex.c
|
||||
ASN.1 DER, decode a SEQUENCE, Tom St Denis
|
||||
*/
|
||||
|
||||
#ifdef LTC_DER
|
||||
|
||||
/*
|
||||
/**
|
||||
Decode a SEQUENCE
|
||||
@param in The DER encoded input
|
||||
@param inlen The size of the input
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_sequence_flexi.c
|
||||
ASN.1 DER, decode an array of ASN.1 types with a flexi parser, Tom St Denis
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@
|
|||
#include <stdarg.h>
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_sequence_multi.c
|
||||
ASN.1 DER, decode a SEQUENCE, Tom St Denis
|
||||
*/
|
||||
|
||||
#ifdef LTC_DER
|
||||
|
||||
/*
|
||||
/**
|
||||
Decode a SEQUENCE type using a VA list
|
||||
@param in Input buffer
|
||||
@param inlen Length of input in octets
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_short_integer.c
|
||||
ASN.1 DER, decode an integer, Tom St Denis
|
||||
*/
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#ifdef LTC_DER
|
||||
|
||||
/*
|
||||
/**
|
||||
Read a short integer
|
||||
@param in The DER encoded data
|
||||
@param inlen Size of data
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_utctime.c
|
||||
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; \
|
||||
x += 2;
|
||||
|
||||
/*
|
||||
/**
|
||||
Decodes a UTC time structure in DER format (reads all 6 valid encoding formats)
|
||||
@param in Input buffer
|
||||
@param inlen Length of input buffer in octets
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
#include "../../headers/tomcrypt.h"
|
||||
|
||||
/*
|
||||
/**
|
||||
@file der_decode_utf8_string.c
|
||||
ASN.1 DER, encode a UTF8 STRING, Tom St Denis
|
||||
*/
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#ifdef LTC_DER
|
||||
|
||||
/*
|
||||
/**
|
||||
Store a UTF8 STRING
|
||||
@param in The DER encoded 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 $ */
|
||||
|
|
@ -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_printable_string.c
|
||||
ASN.1 DER, encode a printable STRING, Tom St Denis
|
||||
*/
|
||||
|
||||
#ifdef LTC_DER
|
||||
|
||||
/**
|
||||
Store an printable STRING
|
||||
@param in The array of printable to store (one per char)
|
||||
@param inlen The number of printable to store
|
||||
@param out [out] The destination for the DER encoded printable STRING
|
||||
@param outlen [in/out] The max size and resulting size of the DER printable STRING
|
||||
@return CRYPT_OK if successful
|
||||
*/
|
||||
int der_encode_printable_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_printable_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++] = 0x13;
|
||||
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_printable_char_encode(in[y]);
|
||||
}
|
||||
|
||||
/* retun length */
|
||||
*outlen = x;
|
||||
|
||||
return CRYPT_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_encode_printable_string.c,v $ */
|
||||
/* $Revision: 1.5 $ */
|
||||
/* $Date: 2006/12/28 01:27:24 $ */
|
||||
|
|
@ -0,0 +1,335 @@
|
|||
/* 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"
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
/**
|
||||
@file der_encode_sequence_ex.c
|
||||
ASN.1 DER, encode a SEQUENCE, Tom St Denis
|
||||
*/
|
||||
|
||||
#ifdef LTC_DER
|
||||
|
||||
/**
|
||||
Encode a SEQUENCE
|
||||
@param list The list of items to encode
|
||||
@param inlen The number of items in the list
|
||||
@param out [out] The destination
|
||||
@param outlen [in/out] The size of the output
|
||||
@param type_of LTC_ASN1_SEQUENCE or LTC_ASN1_SET/LTC_ASN1_SETOF
|
||||
@return CRYPT_OK on success
|
||||
*/
|
||||
int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen,
|
||||
unsigned char *out, unsigned long *outlen, int type_of)
|
||||
{
|
||||
int err, type;
|
||||
unsigned long size, x, y, z, i;
|
||||
void *data;
|
||||
|
||||
LTC_ARGCHK(list != NULL);
|
||||
LTC_ARGCHK(out != NULL);
|
||||
LTC_ARGCHK(outlen != NULL);
|
||||
|
||||
/* get size of output that will be required */
|
||||
y = 0;
|
||||
for (i = 0; i < inlen; i++) {
|
||||
type = list[i].type;
|
||||
size = list[i].size;
|
||||
data = list[i].data;
|
||||
|
||||
if (type == LTC_ASN1_EOL) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case LTC_ASN1_BOOLEAN:
|
||||
if ((err = der_length_boolean(&x)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
y += x;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_INTEGER:
|
||||
if ((err = der_length_integer(data, &x)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
y += x;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_SHORT_INTEGER:
|
||||
if ((err = der_length_short_integer(*((unsigned long*)data), &x)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
y += x;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_BIT_STRING:
|
||||
if ((err = der_length_bit_string(size, &x)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
y += x;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_OCTET_STRING:
|
||||
if ((err = der_length_octet_string(size, &x)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
y += x;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_NULL:
|
||||
y += 2;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_OBJECT_IDENTIFIER:
|
||||
if ((err = der_length_object_identifier(data, size, &x)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
y += x;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_IA5_STRING:
|
||||
if ((err = der_length_ia5_string(data, size, &x)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
y += x;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_PRINTABLE_STRING:
|
||||
if ((err = der_length_printable_string(data, size, &x)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
y += x;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_UTF8_STRING:
|
||||
if ((err = der_length_utf8_string(data, size, &x)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
y += x;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_UTCTIME:
|
||||
if ((err = der_length_utctime(data, &x)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
y += x;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_SET:
|
||||
case LTC_ASN1_SETOF:
|
||||
case LTC_ASN1_SEQUENCE:
|
||||
if ((err = der_length_sequence(data, size, &x)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
y += x;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = CRYPT_INVALID_ARG;
|
||||
goto LBL_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* calc header size */
|
||||
z = y;
|
||||
if (y < 128) {
|
||||
y += 2;
|
||||
} else if (y < 256) {
|
||||
/* 0x30 0x81 LL */
|
||||
y += 3;
|
||||
} else if (y < 65536UL) {
|
||||
/* 0x30 0x82 LL LL */
|
||||
y += 4;
|
||||
} else if (y < 16777216UL) {
|
||||
/* 0x30 0x83 LL LL LL */
|
||||
y += 5;
|
||||
} else {
|
||||
err = CRYPT_INVALID_ARG;
|
||||
goto LBL_ERR;
|
||||
}
|
||||
|
||||
/* too big ? */
|
||||
if (*outlen < y) {
|
||||
*outlen = y;
|
||||
err = CRYPT_BUFFER_OVERFLOW;
|
||||
goto LBL_ERR;
|
||||
}
|
||||
|
||||
/* store header */
|
||||
x = 0;
|
||||
out[x++] = (type_of == LTC_ASN1_SEQUENCE) ? 0x30 : 0x31;
|
||||
|
||||
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>>8UL)&255);
|
||||
out[x++] = (unsigned char)(z&255);
|
||||
} else if (z < 16777216UL) {
|
||||
out[x++] = 0x83;
|
||||
out[x++] = (unsigned char)((z>>16UL)&255);
|
||||
out[x++] = (unsigned char)((z>>8UL)&255);
|
||||
out[x++] = (unsigned char)(z&255);
|
||||
}
|
||||
|
||||
/* store data */
|
||||
*outlen -= x;
|
||||
for (i = 0; i < inlen; i++) {
|
||||
type = list[i].type;
|
||||
size = list[i].size;
|
||||
data = list[i].data;
|
||||
|
||||
if (type == LTC_ASN1_EOL) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case LTC_ASN1_BOOLEAN:
|
||||
z = *outlen;
|
||||
if ((err = der_encode_boolean(*((int *)data), out + x, &z)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
x += z;
|
||||
*outlen -= z;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_INTEGER:
|
||||
z = *outlen;
|
||||
if ((err = der_encode_integer(data, out + x, &z)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
x += z;
|
||||
*outlen -= z;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_SHORT_INTEGER:
|
||||
z = *outlen;
|
||||
if ((err = der_encode_short_integer(*((unsigned long*)data), out + x, &z)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
x += z;
|
||||
*outlen -= z;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_BIT_STRING:
|
||||
z = *outlen;
|
||||
if ((err = der_encode_bit_string(data, size, out + x, &z)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
x += z;
|
||||
*outlen -= z;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_OCTET_STRING:
|
||||
z = *outlen;
|
||||
if ((err = der_encode_octet_string(data, size, out + x, &z)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
x += z;
|
||||
*outlen -= z;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_NULL:
|
||||
out[x++] = 0x05;
|
||||
out[x++] = 0x00;
|
||||
*outlen -= 2;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_OBJECT_IDENTIFIER:
|
||||
z = *outlen;
|
||||
if ((err = der_encode_object_identifier(data, size, out + x, &z)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
x += z;
|
||||
*outlen -= z;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_IA5_STRING:
|
||||
z = *outlen;
|
||||
if ((err = der_encode_ia5_string(data, size, out + x, &z)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
x += z;
|
||||
*outlen -= z;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_PRINTABLE_STRING:
|
||||
z = *outlen;
|
||||
if ((err = der_encode_printable_string(data, size, out + x, &z)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
x += z;
|
||||
*outlen -= z;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_UTF8_STRING:
|
||||
z = *outlen;
|
||||
if ((err = der_encode_utf8_string(data, size, out + x, &z)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
x += z;
|
||||
*outlen -= z;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_UTCTIME:
|
||||
z = *outlen;
|
||||
if ((err = der_encode_utctime(data, out + x, &z)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
x += z;
|
||||
*outlen -= z;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_SET:
|
||||
z = *outlen;
|
||||
if ((err = der_encode_set(data, size, out + x, &z)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
x += z;
|
||||
*outlen -= z;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_SETOF:
|
||||
z = *outlen;
|
||||
if ((err = der_encode_setof(data, size, out + x, &z)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
x += z;
|
||||
*outlen -= z;
|
||||
break;
|
||||
|
||||
case LTC_ASN1_SEQUENCE:
|
||||
z = *outlen;
|
||||
if ((err = der_encode_sequence_ex(data, size, out + x, &z, type)) != CRYPT_OK) {
|
||||
goto LBL_ERR;
|
||||
}
|
||||
x += z;
|
||||
*outlen -= z;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = CRYPT_INVALID_ARG;
|
||||
goto LBL_ERR;
|
||||
}
|
||||
}
|
||||
*outlen = x;
|
||||
err = CRYPT_OK;
|
||||
|
||||
LBL_ERR:
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif
|
||||
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