This commit is contained in:
Kulyadev 2021-12-28 12:17:51 +03:00
commit 068b923299
4319 changed files with 2080299 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.ini
.vscode*/

4
.hgeol Normal file
View file

@ -0,0 +1,4 @@
[patterns]
**.cpp = LF
**.h = LF
**.sql = LF

5
.hgignore Normal file
View file

@ -0,0 +1,5 @@
build.*/.*
syntax: glob
*.orig
*.rej

48
.travis.yml Normal file
View file

@ -0,0 +1,48 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
language: cpp
git:
depth: 1
branches:
only:
- master
compiler:
- clang
# - gcc ## Uncomment when we are up to full c++11 standards.
before_install:
- sudo add-apt-repository ppa:kalakris/cmake -y
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
- sudo apt-get update -qq
- sudo apt-get install libace-dev
- sudo apt-get install libncurses5-dev
- sudo apt-get install build-essential autoconf libtool make cmake git-core patch wget links zip unzip unrar
- sudo apt-get install openssl libssl-dev mysql-server mysql-client libmysqlclient15-dev libmysql++-dev libreadline6-dev zlib1g-dev libbz2-dev
- if [ "$CXX" = "clang++" ]; then sudo apt-get install -qq libstdc++-4.8-dev; fi
- if [ "$CXX" = "g++" ]; then sudo apt-get install -qq g++-4.8; fi
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
script:
- mkdir build
- cd build
- cmake ../ -DSCRIPTS=1 -DTOOLS=1
- make -j8
notifications:
irc:
channels:
- "irc.rizon.net#project_skyfire"
on_success: always
on_failure: always

103
CMakeLists.txt Normal file
View file

@ -0,0 +1,103 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/>
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# Before project or language initialization
if (UNIX)
if (EXISTS "/usr/bin/gcc-4.9")
message (STATUS "gcc 4.9 found. Force switching to 4.8")
if (NOT EXISTS "/usr/bin/gcc-4.8")
message (FATAL_ERROR "gcc 4.8 not found")
endif()
if (NOT EXISTS "/usr/bin/g++-4.8")
message (FATAL_ERROR "g++ 4.8 not installed")
endif()
set (CMAKE_C_COMPILER "/usr/bin/gcc-4.8")
set (CMAKE_CXX_COMPILER "/usr/bin/g++-4.8")
endif()
endif()
# Set projectname (must be done AFTER setting configurationtypes)
project(Project_Skyfire)
# CMake policies (can not be handled elsewhere)
cmake_minimum_required(VERSION 2.8.9)
cmake_policy(SET CMP0005 OLD)
if(POLICY CMP0043)
cmake_policy(SET CMP0043 OLD) # Disable 'Ignore COMPILE_DEFINITIONS_<Config> properties'
endif(POLICY CMP0043)
# add this options before PROJECT keyword
set(CMAKE_DISABLE_SOURCE_CHANGES ON)
set(CMAKE_DISABLE_IN_SOURCE_BUILD ON)
# Set RPATH-handing (CMake parameters)
set(CMAKE_SKIP_BUILD_RPATH 0)
set(CMAKE_BUILD_WITH_INSTALL_RPATH 0)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH 1)
# set macro-directory
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/macros")
# build in Release-mode by default if not explicitly set
if( NOT CMAKE_BUILD_TYPE )
set(CMAKE_BUILD_TYPE "Release")
endif()
include(CheckCXXSourceRuns)
include(CheckIncludeFiles)
# set default buildoptions and print them
include(cmake/options.cmake)
# turn off PCH totally if enabled (hidden setting, mainly for devs)
if( NOPCH )
set(USE_COREPCH 0)
set(USE_SCRIPTPCH 0)
endif()
include(CheckPlatform)
# basic packagesearching and setup (further support will be needed, this is a preliminary release!)
set(OPENSSL_EXPECTED_VERSION 1.0.0)
set(ACE_EXPECTED_VERSION 5.8.3)
find_package(PCHSupport)
find_package(ACE REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(Threads REQUIRED)
if( NOT USE_MYSQL_SOURCES )
find_package(MySQL REQUIRED)
endif()
if( UNIX )
find_package(Readline)
find_package(ZLIB)
find_package(BZip2)
endif()
set(Boost_USE_STATIC_LIBS ON)
find_package(Boost COMPONENTS system locale filesystem thread regex
REQUIRED
)
# Find revision ID and hash of the sourcetree
include(cmake/genrev.cmake)
# print out the results before continuing
include(cmake/showoptions.cmake)
# add dependencies
add_subdirectory(dep)
# add core sources
add_subdirectory(src)

339
COPYING.md Normal file
View file

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

24
PreLoad.cmake Normal file
View file

@ -0,0 +1,24 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# This file is run right before CMake starts configuring the sourcetree
# Example: Force CMAKE_INSTALL_PREFIX to be preloaded with something before
# doing the actual first "configure"-part - allows for hardforcing
# destinations elsewhere in the CMake buildsystem (commented out on purpose)
# Override CMAKE_INSTALL_PREFIX on Windows platforms
#if( WIN32 )
# if( NOT CYGWIN )
# set(CMAKE_INSTALL_PREFIX
# "" CACHE PATH "Default install path")
# endif()
#endif()

52
README.md Normal file
View file

@ -0,0 +1,52 @@
**MoP 5.4.8 Build 18414**
## Client Patch
[SkyFire-Community-Tools](https://github.com/ProjectSkyfire/SkyFire-Community-Tools).
## Build Status
# [![Build Status](https://travis-ci.org/ProjectSkyfire/SkyFire.548.png)](https://travis-ci.org/ProjectSkyfire/SkyFire.548)
## Requirements
+ Platform: Linux, Windows or Mac
+ Processor with SSE2 support
+ boost_1_63_0-msvc-14.0-64
+ MySQL = 5.7.0 (included for Windows)
+ cmake-3.19.4-win64-x64 (Windows / Linux)
+ OpenSS L= Win64OpenSSL-1_0_2d
+ MS Visual Studio = 15 (2017) (Windows only)
## Install
Detailed installation guides are available in the wiki for
[Windows](http://wiki.projectskyfire.org/index.php?title=Installation_Windows),
[Linux](http://wiki.projectskyfire.org/index.php?title=Installation_Linux) and
[Mac OSX](http://wiki.projectskyfire.org/index.php?title=Installation_Mac_OS_X).
## Reporting issues
Please use the search function before you report issues.
[SkyFire Issue Tracker](https://github.com/ProjectSkyfire/SkyFire.548/issues).
## Submitting fixes
Fixes are submitted as pull requests via Github.
[SkyFire Pull Request](https://github.com/ProjectSkyfire/SkyFire.548/pulls)
## Copyright
License: GPL 3.0
Read file [COPYING](COPYING.md)
## Authors &amp; Contributors
Read file [THANKS](THANKS.md)
## To-Do List
Read File [TO-DO](TODO.md)
## Links
Forum [http://www.projectskyfire.org](http://www.projectskyfire.org)
Database [http://www.projectskyfire.org/index.php?/files/](http://www.projectskyfire.org/index.php?/files/)
Wiki [http://wiki.projectskyfire.org](http://wiki.projectskyfire.org)

103
THANKS.md Normal file
View file

@ -0,0 +1,103 @@
= TrinityCore -- Thanks/credits file =
TrinityCore is a derivation/rewrite of MaNGOS, which was originally written
by Team Python and the WoW Daemon Team. Many people further helped Trinity Core
by submitting bug reports, code patches, and suggestions. Thanks to the
community!
Special thanks should go out to the WowwoW team. We have gained help from
them many times in the creation of this project. Keep up the good work guys.
Thanks to the ScriptDev2 team (http://www.scriptdev2.com) for scripts.
Thanks to the WCell team (especially Ralek) for research on realm reconnect
sequence, item scaling stats algorithm, gameobject rotation issues.
Thanks go out to the following people for various patches/code (listed in the
order they were added) (there may be duplicates or invalid names, most of them
were extracted from commits):
w12x, dythzer, XEQT, death420, balrok, TOM_RUS,
runningnak3d, Seline, KingPin, raczman, bogie, Neo2003, NoFantasy, Derex,
freghar, toilet1, megamage, MadJack, WarHead, gvcoman, Ntsc, arrai, jrkpote,
Seraphim, vagoth, KerchumA222, Rognar, Blaymoira, DragonHunter, Molius, ApoC,
SeT, hunuza, Wyk3d, VladimirMangos, rj686, Arthorius, QAston, Muhaha, dereka,
Kaldorei, NuRRi, Biglad, Machiavelli, Nivelo, Paradox, Aokromes, EleGoS,
Visagalis, reno, Cybrax, GriffonHeart, fgenesis, rilex, XTElite1, Anubisss, eL,
Iskander, arclite, Bladex, EIFEL, Klaimmore, XTZGZoReX, panaut0lordv, DearScorpion,
BlueSteel, AlexDereka, Drahy, krz, Xeptor, Sethoso, Sarjuuk, pasdVn, nissen,
Triply, `win, Fog, emsy, McBitter, Lukaasm, maikash, Wormheart, DonTomika,
DiSlord, Tiretunderl, Ramses_II, cccyril, Cha0S2, miebaik, Trazom, KiriX,
darkEvil, Sorken, Kudlaty, Charlie2025, Medwise, Rat, Lutik, domingo,
TrullyONE, rastikzzz, Tassader, Lightguard, KAPATEJIb, Tux`Volant, zhanhang03,
Asael, Elron, False, nos4r2zod, Disassembler, thumsoul, rvnth, throneinc,
MrTux, Luniz2k1, Dani, BroodWyrm, raven_coda, Bagsac, Thraxx, Trogvar,
teacher4, zhenya, Albrecht de Endrau, Phacops, Naicisum, thenecromancer, shax,
cryingcloud, freeganja, Foks, daveh, Az@zel, evilstar, Corfen, Astellar, Maxxie,
DEN_North, StarJoker, Nezemnoy, X-Savior, subhuman_bob, Ambal, Brueggus, sparc,
jojo, Trojan, tvaroh, Genars, BombermaG, Bulkin, nesocip, xILOSWag, SilverIce,
Uruviel, timmit, Seizerkiller, Stokrotka, JoN0, Tanatos, Hunteee, Alyen,
Farah, Them, DaGNU, arthurcik, BudIcePenguin, Amok, Amit86, onkelz28, Azrael,
Larva, Roland, DerDyddye, Vicos, PSZ, CRAZyBUg, irish, Sephiroth1984,
mike753, Xlybriem, Paytheo, ArticDevil, FearX, Beaste, bufferoverflow, Jeniczek,
Den, bobaz, crackm, seirge, D_Skywalk, mknjc, Christyan, Saeba, Nevan, tlexii,
liszt, duckman, Joro, Charlie, smellbee, bigjohnson4, maxdestroyer, Destalker,
ckegg, Drethek, yad02, Win32, NetSky, Zcuron, Necroo, ogeraisi, Coldblooded,
Edder, riddick, Craker, NeoLithicX, srounet, SLG, Tidus, neo0608, SyRiOCoP,
F636y623, Patro, mobel, simak, hectolight, Riccardo, GodsdoG, Gomez, kamir86, fredi,
qubix, Deafboy, Authorius, DarkRabbit, mrbungle, netoya, peldor, eumario, Alex,
Moandor, ebx, DasBlub, guenex, Brats, Lucy, arcx, Enril, oiler2112,
Wizz, Elminster, 123qwe, NeatElves, Turk3y, deicide, vladonix, nugu100,
Skystar, Reve, jorooo, FrozenDB, miranda.conrado, Tequila, Gommes, Zerg2000,
aerione_alt, Rastik, FrenchW, wilibald09, Velorien, kancaras, fisherman,
Aviram, Mufik, loop69, multiplexer, Koani, rechapa79, kozelo, MeanMachine,
fregh, adrycasillo, IncoGnito, Alez, Itch, Kuteur, MaS0n, peaceman, manuel,
Gendalph, Lynx3d, raftom, Infinity, Ebrithil, Sorya, HP1, Prince, redcore,
Boogie, Necro, Thyros, simon, MrSmite, horogandris, Stryker, MaXiMiUS, kaell,
totoro, Forgiven, Big, Oculus, Lorac, Nemesis, Epsik, iadus3, durotar, hoshie,
fukifat, imbecile, Nafsih, Meldanor, Turok, Naturamen, Themris, Sundark,
Azuritus, jotapdiez, EnderGT, Curuad, oc_redfox, rockzOr, Darkshines,
BlackYoghurt, McLovin, Gyullo, kaxap, Hawthorne, nanouniko, new001, Opterman,
Typhoon, Cleave, HiZed, The_Game_Master, Athor, Veras, Menia, Jolan, BlackOne,
johnholiver, Spp, Drevi, kb_z, Tartalo, Shendor, Demonx, Taliesin, defacer,
SoulForge, Jackpoz, Cass, QuaLiT1, exul182, sunwell, AniRB, clotza, Tommassino,
dracula70, alexsot, RedSonja, Cnillidan, Proofzor, aqs999, Sony, amsjunior123,
Sisif, Joshh, alex_1983, arez, RammboNr5, Insider, bodompelle, lobuz, Azazel,
footman, elron103, make_the_king, destros, MetaphysicalDrama, disassebler,
Malcrom, Vladmim<69>r Lipt<70>k, retriman, hyriuu, Smakapotatis, PainKiller,
bkhorizon, n0n4m3, Chesterfield, Frankir, Wowka321, Morpheux, p0wer,
Ouden, toshik, laise, yavi, Splinter, Syntec, Arthas, denyde, unholy,
Vaughner, blackmanos, edrinn, Liberate, Shauren, dr.tenma, click,
SupaBad, Xanadu, Socolin, Shocker, Silinoron, 0xFuture, xK1, Ramus, menke,
wowgargamel, Dark0r, Kierkegaard, Leak, ge0rg, StormByte, joshwhedon,
BlackCat0110, JuliuSZS, n4rk0, filip.havlicek, m.ax, laviniu, LordJZ, Scazzato88,
svannon, jurkovic.nikola, Willian Krueger, BioHazard, Ille000, Erocoloco,
terrorbringer, antihrists, Havenard, scarymovie87, D3VIL, FaTe753, PrinceCreed,
spgm, Dakeyras, sombre88, 19Maxx83, moriquendu, Ille, breakerfly,
zthoreen, clement.roussel, p.alexej, Ceris, Nayre, Kiper, announce, thmarth,
Ner'zhul, DarkXuan, linencloth, SnakeIce, Tome, Nay, Kaelima,
Subv, Tuxity, tibbi, Gigatotem, Nexflame, trickerer, zxbiohazardzx, w1sht0l1v3,
Warpten, CeIa, Kandera, horn, Dimitro, thesensei, Sovak, Vincent-Michael,
Xanvial, faq, Gacko, Geodar, hexa-, MacWarrior, cyberbrest, Myran2, Northstrider,
AFROM, CrYser, Kretol, Bizzy, nelegalno, Kinzcool, armano2, Tomas, Aristoo, Trista,
e@cacaw.net, oMadMano, Kiddie, blub, Santiago, tobmaps, Mik43l, danik, Souler,
joschiwald, CDawg, WishToDie, gecko32, gadge, neuro_999, Stefo, dr.skull, NTX, Hexit,
Stalker-Riddick, thomas33, Manuel Carrasco, Imprtat, Chaplain, teyrnon, zorix, Greymane,
Venugh, e000, Amit, Alternative, Exodius, Jorge, shlomi1515, tharaca, Alestaan, Valcorb,
Odyssey, Chipsi, Pesthuf, stfx, Yaki Khadafi, David Klepáček, SignFinder, unknown,
NNN666, AliveShiro, 4m1g0, mweinelt, Abdollah Hasan, Xees, zori, warriorpoetex, qaywsx,
ghost, Discovered, faq_, Jildor, The Game, Kezo90, Helias, Frca, Ramusik, elecyb, Joni,
Multivitamin, faramir118, cookta2012, Gyx, ShinDarth, Lopin, Martin Weinelt,
Bezo, frozenarmor, E. van Harten, LiMCrosS, Albis, Go6o, Pitcrawler, Matthew Goff, neurorulez,
Ka0z, glkrlos, zwerg, Merlin2010, furion, Giuseppe Montesanto, LihO, Bootz, PKX, burnham,
Drake Fish, Vlad, Discover, Heisenberg, Kapoeira, Emo Norfik, tREAk, zoidmann, Wilds, weclub,
Jon, Cron, Alexander, Vinolentus, LaserJet, mns, Stalker_Riddick, Yelvann, draco, Geekotron,
bytewarrior, Fredi Machado, Studioworks, 3kids, idostyle@zoit, Sebastián Orellana, DemiDroL,
n4ndo, et2012, Rochet2, Grobi, Google, Wispeckt, hacknowledge, wonopon, Naga,
Joeri Thissen, Payn, Fest, insider42, DrTenma, L30m4nc3r, Akama, Jesper Meyer, Kiperr,
maanuel, vcrx6, Furion89, Mark07, lost-illusion, SeTM, TCKiper, vlad852, tehmarto, boom,
Per Wilhelmsen, telsamat, Various, erimioa, Sawiner, zergtmn, SimonDMII, pek2011, alexbolotsin,
laly, mrquickfx, Koord, amnell, GWRde, EdwinDW, gildor, darkstalker, Feanordev, _manuel_,
thymuswisewood, sohrab, Lazzalf, Taser, Ottowayne, Exordian, nucleartux, John Holiver, j4r0d,
Trazom62, Brian, enjoi, teacher, Alex Bolotsin, Troy, silver1ce, nihal, arcanzic, Chaz Brown,
pendragon, aven_coda, cca220v, ArcticDevil, derex_tri, AlterEgo, AriDEV, Fabian, Rawaho, Projectcore,
Zalito12, Palabola, caffeine239, mansemino

View file

@ -0,0 +1,26 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# Set build-directive (used in core to tell which buildtype we used)
add_definitions(-D_BUILD_DIRECTIVE='"$(CONFIGURATION)"')
if(WITH_WARNINGS)
set(WARNING_FLAGS "-W -Wall -Wextra -Winit-self -Wfatal-errors")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} -Woverloaded-virtual")
message(STATUS "Clang: All warnings enabled")
endif()
if(WITH_COREDEBUG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3")
message(STATUS "Clang: Debug-flags set (-g3)")
endif()

View file

@ -0,0 +1,53 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# Set build-directive (used in core to tell which buildtype we used)
add_definitions(-D_BUILD_DIRECTIVE='"${CMAKE_BUILD_TYPE}"')
if(PLATFORM EQUAL 32)
# Required on 32-bit systems to enable SSE2 (standard on x64)
set(SSE_FLAGS "-msse2 -mfpmath=sse")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SSE_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SSE_FLAGS}")
endif()
add_definitions(-DHAVE_SSE2 -D__SSE2__)
message(STATUS "GCC: SFMT enabled, SSE2 flags forced")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(COMPILER_FLAGS "")
if (WITH_SANITIZER)
set(COMPILER_FLAGS "${COMPILER_FLAGS} -fsanitize=address -fsanitize-recover=address")
endif()
if (BUILD_DEPLOY)
set(COMPILER_FLAGS "${COMPILER_FLAGS} -march=native -fno-strict-aliasing -g3")
if (NOT INSTALL_PREFIX)
set(INSTALL_PREFIX "/server/wow/horizon")
endif()
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILER_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILER_FLAGS}")
if( WITH_WARNINGS )
set(WARNING_FLAGS "-W -Wall -Wextra -Winit-self -Winvalid-pch -Wfatal-errors")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} -Woverloaded-virtual")
message(STATUS "GCC: All warnings enabled")
endif()
if( WITH_COREDEBUG )
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3")
message(STATUS "GCC: Debug-flags set (-g3)")
endif()

View file

@ -0,0 +1,29 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# Set build-directive (used in core to tell which buildtype we used)
add_definitions(-D_BUILD_DIRECTIVE="'${CMAKE_BUILD_TYPE}'")
if(PLATFORM EQUAL 32)
add_definitions(-axSSE2)
else()
add_definitions(-xSSE2)
endif()
if( WITH_WARNINGS )
add_definitions(-w1)
message(STATUS "ICC: All warnings enabled")
endif()
if( WITH_COREDEBUG )
add_definitions(-g)
message(STATUS "ICC: Debug-flag set (-g)")
endif()

View file

@ -0,0 +1,38 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# Set build-directive (used in core to tell which buildtype we used)
add_definitions(-D_BUILD_DIRECTIVE=\\"${CMAKE_BUILD_TYPE}\\")
if(PLATFORM EQUAL 32)
# Required on 32-bit systems to enable SSE2 (standard on x64)
set(SSE_FLAGS "-msse2 -mfpmath=sse")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SSE_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SSE_FLAGS}")
endif()
add_definitions(-DHAVE_SSE2 -D__SSE2__)
message(STATUS "GCC: SFMT enabled, SSE2 flags forced")
if( WITH_WARNINGS )
set(WARNING_FLAGS "-W -Wall -Wextra -Winit-self -Winvalid-pch -Wfatal-errors")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} -Woverloaded-virtual")
message(STATUS "GCC: All warnings enabled")
endif()
if( WITH_COREDEBUG )
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3")
message(STATUS "GCC: Debug-flags set (-g3)")
endif()

View file

@ -0,0 +1,89 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(MSVC_EXPECTED_VERSION 18.0)
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS MSVC_EXPECTED_VERSION)
message(FATAL_ERROR "MSVC: SkyFire requires version ${MSVC_EXPECTED_VERSION} (MSVC 2013) to build but found ${CMAKE_CXX_COMPILER_VERSION}")
endif()
# set up output paths ofr static libraries etc (commented out - shown here as an example only)
#set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
#set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
if(PLATFORM EQUAL 64)
# This definition is necessary to work around a bug with Intellisense described
# here: http://tinyurl.com/2cb428. Syntax highlighting is important for proper
# debugger functionality.
add_definitions("-D_WIN64")
message(STATUS "MSVC: 64-bit platform, enforced -D_WIN64 parameter")
#Enable extended object support for debug compiles on X64 (not required on X86)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
message(STATUS "MSVC: Enabled extended object-support for debug-compiles")
else()
# mark 32 bit executables large address aware so they can use > 2GB address space
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE")
message(STATUS "MSVC: Enabled large address awareness")
add_definitions(/arch:SSE2)
message(STATUS "MSVC: Enabled SSE2 support")
endif()
# Set build-directive (used in core to tell which buildtype we used)
add_definitions(-D_BUILD_DIRECTIVE=\\"$(ConfigurationName)\\")
# multithreaded compiling on VS
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
# Define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES - eliminates the warning by changing the strcpy call to strcpy_s, which prevents buffer overruns
add_definitions(-D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
message(STATUS "MSVC: Overloaded standard names")
# Ignore warnings about older, less secure functions
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
message(STATUS "MSVC: Disabled NON-SECURE warnings")
#Ignore warnings about POSIX deprecation
add_definitions(-D_CRT_NONSTDC_NO_WARNINGS)
message(STATUS "MSVC: Disabled POSIX warnings")
if(NOT WITH_WARNINGS)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267 /wd4619 /wd4512")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4996 /wd4355 /wd4244 /wd4985 /wd4267 /wd4619 /wd4512")
message(STATUS "MSVC: Disabled generic compiletime warnings")
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*)
string(REGEX REPLACE "/Zm[0-9]+ *" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zm500")
# Enable and treat as errors the following warnings to easily detect virtual function signature failures:
# 'function' : member function does not override any base class virtual member function
# 'virtual_function' : no override available for virtual member function from base 'class'; function is hidden
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /we4263 /we4264")
macro(ApplyFlags variable)
string(REGEX REPLACE "/INCREMENTAL *" "/INCREMENTAL:NO" ${variable} "${${variable}}")
endmacro()
if (BUILD_DEV)
ApplyFlags(CMAKE_EXE_LINKER_FLAGS_DEBUG)
ApplyFlags(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO)
ApplyFlags(CMAKE_SHARED_LINKER_FLAGS_DEBUG)
ApplyFlags(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:inline")
endif()

81
cmake/genrev.cmake Normal file
View file

@ -0,0 +1,81 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
execute_process(
COMMAND hg id -n
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE rev_id_str
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
execute_process(
COMMAND hg id -i
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE rev_hash_str
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
execute_process(
COMMAND hg tip --template "{date|shortdate}"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE rev_date
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(EXISTS ${CMAKE_SOURCE_DIR}/.hg_archival.txt)
file(READ
${CMAKE_SOURCE_DIR}/.hg_archival.txt rev_hash_str
LIMIT 10
OFFSET 7
NEWLINE_CONSUME
)
string(STRIP ${rev_hash_str} rev_hash_str)
set(rev_id_str "Archive")
set(rev_id "0")
set(rev_hash ${rev_hash_str})
endif()
# Last minute check - ensure that we have a proper revision
# If everything above fails (means the user has erased the mercurial revisional control directory, or runs archive and erased their .hg_archival.txt)
if(NOT rev_id_str)
message("")
message(STATUS "WARNING - No revision-information found - have you been tampering with the sources?")
# Ok, since we have no valid ways of finding/setting the revision, let's force some defaults
set(rev_hash_str "Archive")
set(rev_hash "0")
set(rev_id_str "0")
set(rev_id "0")
endif()
# Strip off excess strings (shows when the source is actually modified)
if(NOT rev_id_str MATCHES "Archive")
string(REPLACE "+" "" rev_id ${rev_id_str})
endif()
string(REPLACE "+" "" rev_hash ${rev_hash_str})
# Its not set during initial run
if(NOT BUILDDIR)
set(BUILDDIR ${CMAKE_BINARY_DIR})
endif()
# Create the actual revision.h file from the above params
if(NOT "${rev_id_cached}" MATCHES "${rev_id_str}")
configure_file(
"${CMAKE_SOURCE_DIR}/revision.h.in.cmake"
"${BUILDDIR}/revision.h"
@ONLY
)
set(rev_id_cached "${rev_id_str}" CACHE INTERNAL "Cached revision ID")
endif()

View file

@ -0,0 +1,24 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# Force out-of-source build
#
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" BUILDING_IN_SOURCE)
if( BUILDING_IN_SOURCE )
message(FATAL_ERROR "
This project requires an out of source build. Remove the file 'CMakeCache.txt'
found in this directory before continuing, create a separate build directory
and run 'cmake path_to_project [options]' from there.
")
endif()

View file

@ -0,0 +1,27 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# check what platform we're on (64-bit or 32-bit), and create a simpler test than CMAKE_SIZEOF_VOID_P
if(CMAKE_SIZEOF_VOID_P MATCHES 8)
set(PLATFORM 64)
MESSAGE(STATUS "Detected 64-bit platform")
else()
set(PLATFORM 32)
MESSAGE(STATUS "Detected 32-bit platform")
endif()
include("${CMAKE_SOURCE_DIR}/cmake/platform/settings.cmake")
if(WIN32)
include("${CMAKE_SOURCE_DIR}/cmake/platform/win/settings.cmake")
elseif(UNIX)
include("${CMAKE_SOURCE_DIR}/cmake/platform/unix/settings.cmake")
endif()

View file

@ -0,0 +1,126 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# This file defines the following macros for developers to use in ensuring
# that installed software is of the right version:
#
# ENSURE_VERSION - test that a version number is greater than
# or equal to some minimum
# ENSURE_VERSION_RANGE - test that a version number is greater than
# or equal to some minimum and less than some
# maximum
# ENSURE_VERSION2 - deprecated, do not use in new code
#
# ENSURE_VERSION
# This macro compares version numbers of the form "x.y.z" or "x.y"
# ENSURE_VERSION( FOO_MIN_VERSION FOO_VERSION_FOUND FOO_VERSION_OK)
# will set FOO_VERSION_OK to true if FOO_VERSION_FOUND >= FOO_MIN_VERSION
# Leading and trailing text is ok, e.g.
# ENSURE_VERSION( "2.5.31" "flex 2.5.4a" VERSION_OK)
# which means 2.5.31 is required and "flex 2.5.4a" is what was found on the system
# Copyright (c) 2006, David Faure, <faure@kde.org>
# Copyright (c) 2007, Will Stephenson <wstephenson@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
# ENSURE_VERSION_RANGE
# This macro ensures that a version number of the form
# "x.y.z" or "x.y" falls within a range defined by
# min_version <= found_version < max_version.
# If this expression holds, FOO_VERSION_OK will be set TRUE
#
# Example: ENSURE_VERSION_RANGE3( "0.1.0" ${FOOCODE_VERSION} "0.7.0" FOO_VERSION_OK )
#
# This macro will break silently if any of x,y,z are greater than 100.
#
# Copyright (c) 2007, Will Stephenson <wstephenson@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
# NORMALIZE_VERSION
# Helper macro to convert version numbers of the form "x.y.z"
# to an integer equal to 10^4 * x + 10^2 * y + z
#
# This macro will break silently if any of x,y,z are greater than 100.
#
# Copyright (c) 2006, David Faure, <faure@kde.org>
# Copyright (c) 2007, Will Stephenson <wstephenson@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
# CHECK_RANGE_INCLUSIVE_LOWER
# Helper macro to check whether x <= y < z
#
# Copyright (c) 2007, Will Stephenson <wstephenson@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
MACRO(NORMALIZE_VERSION _requested_version _normalized_version)
STRING(REGEX MATCH "[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" _threePartMatch "${_requested_version}")
if (_threePartMatch)
# parse the parts of the version string
STRING(REGEX REPLACE "[^0-9]*([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" _major_vers "${_requested_version}")
STRING(REGEX REPLACE "[^0-9]*[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" _minor_vers "${_requested_version}")
STRING(REGEX REPLACE "[^0-9]*[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" _patch_vers "${_requested_version}")
else (_threePartMatch)
STRING(REGEX REPLACE "([0-9]+)\\.[0-9]+" "\\1" _major_vers "${_requested_version}")
STRING(REGEX REPLACE "[0-9]+\\.([0-9]+)" "\\1" _minor_vers "${_requested_version}")
set(_patch_vers "0")
endif (_threePartMatch)
# compute an overall version number which can be compared at once
MATH(EXPR ${_normalized_version} "${_major_vers}*10000 + ${_minor_vers}*100 + ${_patch_vers}")
ENDMACRO(NORMALIZE_VERSION)
MACRO(CHECK_RANGE_INCLUSIVE_LOWER _lower_limit _value _upper_limit _ok)
if (${_value} LESS ${_lower_limit})
set( ${_ok} FALSE )
elseif (${_value} EQUAL ${_lower_limit})
set( ${_ok} TRUE )
elseif (${_value} EQUAL ${_upper_limit})
set( ${_ok} FALSE )
elseif (${_value} GREATER ${_upper_limit})
set( ${_ok} FALSE )
else (${_value} LESS ${_lower_limit})
set( ${_ok} TRUE )
endif (${_value} LESS ${_lower_limit})
ENDMACRO(CHECK_RANGE_INCLUSIVE_LOWER)
MACRO(ENSURE_VERSION requested_version found_version var_too_old)
NORMALIZE_VERSION( ${requested_version} req_vers_num )
NORMALIZE_VERSION( ${found_version} found_vers_num )
if (found_vers_num LESS req_vers_num)
set( ${var_too_old} FALSE )
else (found_vers_num LESS req_vers_num)
set( ${var_too_old} TRUE )
endif (found_vers_num LESS req_vers_num)
ENDMACRO(ENSURE_VERSION)
MACRO(ENSURE_VERSION2 requested_version2 found_version2 var_too_old2)
ENSURE_VERSION( ${requested_version2} ${found_version2} ${var_too_old2})
ENDMACRO(ENSURE_VERSION2)
MACRO(ENSURE_VERSION_RANGE min_version found_version max_version var_ok)
NORMALIZE_VERSION( ${min_version} req_vers_num )
NORMALIZE_VERSION( ${found_version} found_vers_num )
NORMALIZE_VERSION( ${max_version} max_vers_num )
CHECK_RANGE_INCLUSIVE_LOWER( ${req_vers_num} ${found_vers_num} ${max_vers_num} ${var_ok})
ENDMACRO(ENSURE_VERSION_RANGE)

View file

@ -0,0 +1,95 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# Find the ACE client includes and library
#
# This module defines
# ACE_INCLUDE_DIR, where to find ace.h
# ACE_LIBRARIES, the libraries to link against
# ACE_FOUND, if false, you cannot build anything that requires ACE
# also defined, but not for general use are
# ACE_LIBRARY, where to find the ACE library.
set( ACE_FOUND 0 )
if ( UNIX )
if (NOT ACE_INCLUDE_DIR)
FIND_PATH( ACE_INCLUDE_DIR
NAMES
ace/ACE.h
PATHS
/usr/include
/usr/include/ace
/usr/local/include
/usr/local/include/ace
$ENV{ACE_ROOT}
$ENV{ACE_ROOT}/ace
$ENV{ACE_ROOT}/include
${CMAKE_SOURCE_DIR}/externals/ace
DOC
"Specify include-directories that might contain ace.h here."
)
endif()
if (NOT ACE_LIBRARY)
FIND_LIBRARY( ACE_LIBRARY
NAMES
ace ACE
PATHS
/usr/lib
/usr/lib/ace
/usr/local/lib
/usr/local/lib/ace
/usr/local/ace/lib
$ENV{ACE_ROOT}/lib
$ENV{ACE_ROOT}
DOC "Specify library-locations that might contain the ACE library here."
)
# FIND_LIBRARY( ACE_EXTRA_LIBRARIES
# NAMES
# z zlib
# PATHS
# /usr/lib
# /usr/local/lib
# DOC
# "if more libraries are necessary to link into ACE, specify them here."
# )
endif()
if ( ACE_LIBRARY )
if ( ACE_INCLUDE_DIR )
if (_ACE_VERSION)
set(ACE_VERSION "${_ACE_VERSION}")
else (_ACE_VERSION)
file(STRINGS "${ACE_INCLUDE_DIR}/ace/Version.h" ACE_VERSION_STR REGEX "^#define ACE_VERSION \".*\"")
string(REGEX REPLACE "^.*ACE_VERSION \"([0-9].[0-9].[0-9a-z]).*$"
"\\1" ACE_VERSION "${ACE_VERSION_STR}")
endif (_ACE_VERSION)
include(EnsureVersion)
ENSURE_VERSION( "${ACE_EXPECTED_VERSION}" "${ACE_VERSION}" ACE_FOUND)
if (NOT ACE_FOUND)
message(FATAL_ERROR "TrinityCore needs ACE version ${ACE_EXPECTED_VERSION} but found version ${ACE_VERSION}")
endif()
message( STATUS "Found ACE library: ${ACE_LIBRARY}")
message( STATUS "Found ACE headers: ${ACE_INCLUDE_DIR}")
else ( ACE_INCLUDE_DIR )
message(FATAL_ERROR "Could not find ACE headers! Please install ACE libraries and headers")
endif ( ACE_INCLUDE_DIR )
endif ( ACE_LIBRARY )
mark_as_advanced( ACE_FOUND ACE_LIBRARY ACE_EXTRA_LIBRARIES ACE_INCLUDE_DIR )
endif (UNIX)

View file

@ -0,0 +1,303 @@
#
# Find the MySQL client includes and library
#
# This module defines
# MYSQL_INCLUDE_DIR, where to find mysql.h
# MYSQL_LIBRARIES, the libraries to link against to connect to MySQL
# MYSQL_FOUND, if false, you cannot build anything that requires MySQL.
# also defined, but not for general use are
# MYSQL_LIBRARY, where to find the MySQL library.
set( MYSQL_FOUND 0 )
if(WIN32)
# read environment variables and change \ to /
SET(PROGRAM_FILES_32 $ENV{ProgramFiles})
if (${PROGRAM_FILES_32})
STRING(REPLACE "\\\\" "/" PROGRAM_FILES_32 ${PROGRAM_FILES_32})
endif(${PROGRAM_FILES_32})
SET(PROGRAM_FILES_64 $ENV{ProgramW6432})
if (${PROGRAM_FILES_64})
STRING(REPLACE "\\\\" "/" PROGRAM_FILES_64 ${PROGRAM_FILES_64})
endif(${PROGRAM_FILES_64})
endif(WIN32)
# Find MariaDB for Windows
if (WIN32)
# Set know versions MariaDB
set(_MARIADB_KNOWN_VERSIONS "MariaDB 10.5" "MariaDB 10.4" "MariaDB 10.3" "MariaDB 10.2")
# Set default options
set(MARIADB_FOUND_LIB 0)
set(MARIADB_FOUND_INCLUDE 0)
set(MARIADB_FOUND_EXECUTABLE 0)
set(MARIADB_FOUND 0)
macro(FindLibMariaDB MariaDBVersion)
# Find include
find_path(MYSQL_INCLUDE_DIR
NAMES
mysql.h
PATHS
${MYSQL_ADD_INCLUDE_PATH}
"${PROGRAM_FILES_64}/${MariaDBVersion}/include/mysql"
"${PROGRAM_FILES_32}/${MariaDBVersion}/include/mysql"
"${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/include/mysql"
DOC
"Specify the directory containing mysql.h."
)
if(MYSQL_INCLUDE_DIR)
set(MARIADB_FOUND_INCLUDE 1)
endif()
find_library(MYSQL_LIBRARY
NAMES
libmariadb
PATHS
${MYSQL_ADD_LIBRARIES_PATH}
"${PROGRAM_FILES_64}/${MariaDBVersion}/lib"
"${PROGRAM_FILES_64}/${MariaDBVersion}/lib/opt"
"${PROGRAM_FILES_32}/${MariaDBVersion}/lib"
"${PROGRAM_FILES_32}/${MariaDBVersion}/lib/opt"
"$ENV{ProgramFiles}/${MariaDBVersion}/lib/opt"
"$ENV{SystemDrive}/${MariaDBVersion}/lib/opt"
"${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib"
DOC
"Specify the location of the mysql library here."
)
if(MYSQL_LIBRARY)
set(MARIADB_FOUND_LIB 1)
endif()
find_program(MYSQL_EXECUTABLE mysql
PATHS
"${PROGRAM_FILES_64}/${MariaDBVersion}/bin"
"${PROGRAM_FILES_64}/${MariaDBVersion}/bin/opt"
"${PROGRAM_FILES_32}/${MariaDBVersion}/bin"
"${PROGRAM_FILES_32}/${MariaDBVersion}/bin/opt"
"$ENV{ProgramFiles}/${MariaDBVersion}/bin/opt"
"$ENV{SystemDrive}/${MariaDBVersion}/bin/opt"
DOC
"path to your mysql binary.")
if (MYSQL_LIBRARY AND MYSQL_INCLUDE_DIR AND MYSQL_EXECUTABLE)
set(MARIADB_FOUND 1)
endif()
endmacro(FindLibMariaDB)
foreach(version ${_MARIADB_KNOWN_VERSIONS})
if (NOT MARIADB_FOUND)
FindLibMariaDB(${version})
endif()
endforeach()
endif()
if( UNIX )
set(MYSQL_CONFIG_PREFER_PATH "$ENV{MYSQL_HOME}/bin" CACHE FILEPATH
"preferred path to MySQL (mysql_config)"
)
find_program(MYSQL_CONFIG mysql_config
${MYSQL_CONFIG_PREFER_PATH}
/usr/local/mysql/bin/
/usr/local/bin/
/usr/bin/
)
if( MYSQL_CONFIG )
message(STATUS "Using mysql-config: ${MYSQL_CONFIG}")
# set INCLUDE_DIR
exec_program(${MYSQL_CONFIG}
ARGS --include
OUTPUT_VARIABLE MY_TMP
)
string(REGEX REPLACE "-I([^ ]*)( .*)?" "\\1" MY_TMP "${MY_TMP}")
set(MYSQL_ADD_INCLUDE_PATH ${MY_TMP} CACHE FILEPATH INTERNAL)
#message("[DEBUG] MYSQL ADD_INCLUDE_PATH : ${MYSQL_ADD_INCLUDE_PATH}")
# set LIBRARY_DIR
exec_program(${MYSQL_CONFIG}
ARGS --libs_r
OUTPUT_VARIABLE MY_TMP
)
set(MYSQL_ADD_LIBRARIES "")
string(REGEX MATCHALL "-l[^ ]*" MYSQL_LIB_LIST "${MY_TMP}")
foreach(LIB ${MYSQL_LIB_LIST})
string(REGEX REPLACE "[ ]*-l([^ ]*)" "\\1" LIB "${LIB}")
list(APPEND MYSQL_ADD_LIBRARIES "${LIB}")
#message("[DEBUG] MYSQL ADD_LIBRARIES : ${MYSQL_ADD_LIBRARIES}")
endforeach(LIB ${MYSQL_LIB_LIST})
set(MYSQL_ADD_LIBRARIES_PATH "")
string(REGEX MATCHALL "-L[^ ]*" MYSQL_LIBDIR_LIST "${MY_TMP}")
foreach(LIB ${MYSQL_LIBDIR_LIST})
string(REGEX REPLACE "[ ]*-L([^ ]*)" "\\1" LIB "${LIB}")
list(APPEND MYSQL_ADD_LIBRARIES_PATH "${LIB}")
#message("[DEBUG] MYSQL ADD_LIBRARIES_PATH : ${MYSQL_ADD_LIBRARIES_PATH}")
endforeach(LIB ${MYSQL_LIBS})
else( MYSQL_CONFIG )
set(MYSQL_ADD_LIBRARIES "")
list(APPEND MYSQL_ADD_LIBRARIES "mysqlclient_r")
endif( MYSQL_CONFIG )
endif( UNIX )
find_path(MYSQL_INCLUDE_DIR
NAMES
mysql.h
PATHS
${MYSQL_ADD_INCLUDE_PATH}
/usr/include
/usr/include/mysql
/usr/local/include
/usr/local/include/mysql
/usr/local/mysql/include
"C:/Program Files/MySQL/MySQL Server 8.0/include"
"C:/Program Files/MySQL/MySQL Server 5.7/include"
"C:/Program Files/MySQL/MySQL Server 5.6/include"
"C:/Program Files/MySQL/include"
"C:/MySQL/include"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 8.0;Location]/include"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.7;Location]/include"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.6;Location]/include"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 8.0;Location]/include"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.7;Location]/include"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.6;Location]/include"
"$ENV{ProgramFiles}/MySQL/*/include"
"$ENV{SystemDrive}/MySQL/*/include"
"c:/msys/local/include"
"$ENV{MYSQL_INCLUDE_DIR}"
DOC
"Specify the directory containing mysql.h."
)
if( UNIX )
foreach(LIB ${MYSQL_ADD_LIBRARIES})
find_library( MYSQL_LIBRARY
NAMES
mysql libmysql ${LIB}
PATHS
${MYSQL_ADD_LIBRARIES_PATH}
/usr/lib
/usr/lib/mysql
/usr/local/lib
/usr/local/lib/mysql
/usr/local/mysql/lib
DOC "Specify the location of the mysql library here."
)
endforeach(LIB ${MYSQL_ADD_LIBRARY})
endif( UNIX )
if( WIN32 )
find_library( MYSQL_LIBRARY
NAMES
libmysql
PATHS
${MYSQL_ADD_LIBRARIES_PATH}
"C:/Program Files/MySQL/MySQL Server 8.0/lib"
"C:/Program Files/MySQL/MySQL Server 8.0/lib/opt"
"C:/Program Files/MySQL/MySQL Server 5.7/lib/opt"
"C:/Program Files/MySQL/MySQL Server 5.6/lib/opt"
"C:/Program Files/MySQL/lib"
"C:/MySQL/lib/debug"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 8.0;Location]/lib"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 8.0;Location]/lib/opt"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.7;Location]/lib"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.7;Location]/lib/opt"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.6;Location]/lib"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.6;Location]/lib/opt"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 8.0;Location]/lib"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 8.0;Location]/lib/opt"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.7;Location]/lib"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.7;Location]/lib/opt"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.6;Location]/lib"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.6;Location]/lib/opt"
"$ENV{ProgramFiles}/MySQL/*/lib/opt"
"$ENV{SystemDrive}/MySQL/*/lib/opt"
"c:/msys/local/include"
"$ENV{MYSQL_LIBRARY}"
DOC "Specify the location of the mysql library here."
)
endif( WIN32 )
# On Windows you typically don't need to include any extra libraries
# to build MYSQL stuff.
if( NOT WIN32 )
find_library( MYSQL_EXTRA_LIBRARIES
NAMES
z zlib
PATHS
/usr/lib
/usr/local/lib
DOC
"if more libraries are necessary to link in a MySQL client (typically zlib), specify them here."
)
else( NOT WIN32 )
set( MYSQL_EXTRA_LIBRARIES "" )
endif( NOT WIN32 )
if( UNIX )
find_program(MYSQL_EXECUTABLE mysql
PATHS
${MYSQL_CONFIG_PREFER_PATH}
/usr/local/mysql/bin/
/usr/local/bin/
/usr/bin/
DOC
"path to your mysql binary."
)
endif( UNIX )
if( WIN32 )
find_program(MYSQL_EXECUTABLE mysql
PATHS
"${PROGRAM_FILES_64}/MySQL/MySQL Server 8.0/bin"
"${PROGRAM_FILES_64}/MySQL/MySQL Server 5.7/bin"
"${PROGRAM_FILES_64}/MySQL/MySQL Server 8.0/bin/opt"
"${PROGRAM_FILES_64}/MySQL/MySQL Server 5.7/bin/opt"
"${PROGRAM_FILES_64}/MySQL/bin"
"${PROGRAM_FILES_32}/MySQL/MySQL Server 8.0/bin"
"${PROGRAM_FILES_32}/MySQL/MySQL Server 5.7/bin"
"${PROGRAM_FILES_32}/MySQL/MySQL Server 8.0/bin/opt"
"${PROGRAM_FILES_32}/MySQL/MySQL Server 5.7/bin/opt"
"${PROGRAM_FILES_32}/MySQL/bin"
"C:/MySQL/bin/debug"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 8.0;Location]/bin"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.7;Location]/bin"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 8.0;Location]/bin/opt"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.7;Location]/bin/opt"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 8.0;Location]/bin"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.7;Location]/bin"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 8.0;Location]/bin/opt"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.7;Location]/bin/opt"
"$ENV{ProgramFiles}/MySQL/MySQL Server 8.0/bin/opt"
"$ENV{ProgramFiles}/MySQL/MySQL Server 5.7/bin/opt"
"$ENV{SystemDrive}/MySQL/MySQL Server 8.0/bin/opt"
"$ENV{SystemDrive}/MySQL/MySQL Server 5.7/bin/opt"
"c:/msys/local/include"
"$ENV{MYSQL_ROOT}/bin"
DOC
"path to your mysql binary.")
endif( WIN32 )
if( MYSQL_LIBRARY )
if( MYSQL_INCLUDE_DIR )
set( MYSQL_FOUND 1 )
message(STATUS "Found MySQL library: ${MYSQL_LIBRARY}")
message(STATUS "Found MySQL headers: ${MYSQL_INCLUDE_DIR}")
else( MYSQL_INCLUDE_DIR )
message(FATAL_ERROR "Could not find MySQL headers! Please install the development libraries and headers")
endif( MYSQL_INCLUDE_DIR )
if( MYSQL_EXECUTABLE )
message(STATUS "Found MySQL executable: ${MYSQL_EXECUTABLE}")
endif( MYSQL_EXECUTABLE )
mark_as_advanced( MYSQL_FOUND MYSQL_LIBRARY MYSQL_EXTRA_LIBRARIES MYSQL_INCLUDE_DIR MYSQL_EXECUTABLE )
else( MYSQL_LIBRARY )
message(FATAL_ERROR "Could not find the MySQL libraries! Please install the development libraries and headers")
endif( MYSQL_LIBRARY )

View file

@ -0,0 +1,195 @@
# - Try to find the OpenSSL encryption library
# Once done this will define
#
# OPENSSL_ROOT_DIR - Set this variable to the root installation of OpenSSL
#
# Read-Only variables:
# OPENSSL_FOUND - system has the OpenSSL library
# OPENSSL_INCLUDE_DIR - the OpenSSL include directory
# OPENSSL_LIBRARIES - The libraries needed to use OpenSSL
#=============================================================================
# Copyright 2006-2009 Kitware, Inc.
# Copyright 2006 Alexander Neundorf <neundorf@kde.org>
# Copyright 2009-2010 Mathieu Malaterre <mathieu.malaterre@gmail.com>
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distributed this file outside of CMake, substitute the full
# License text for the above reference.)
# http://www.slproweb.com/products/Win32OpenSSL.html
SET(_OPENSSL_ROOT_HINTS
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]"
"$ENV{OPENSSL_ROOT_DIR}"
"${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}"
)
IF(PLATFORM EQUAL 64)
SET(_OPENSSL_ROOT_PATHS
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;InstallLocation]"
"C:/OpenSSL-Win64/"
"C:/OpenSSL/"
"C:/Program Files/OpenSSL-Win64/"
)
ELSE()
SET(_OPENSSL_ROOT_PATHS
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;InstallLocation]"
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;InstallLocation]"
"C:/OpenSSL/"
"C:/Program Files/OpenSSL/"
)
ENDIF()
FIND_PATH(OPENSSL_ROOT_DIR
NAMES
include/openssl/ssl.h
HINTS
${_OPENSSL_ROOT_HINTS}
PATHS
${_OPENSSL_ROOT_PATHS}
)
MARK_AS_ADVANCED(OPENSSL_ROOT_DIR)
# Re-use the previous path:
FIND_PATH(OPENSSL_INCLUDE_DIR openssl/ssl.h
${OPENSSL_ROOT_DIR}/include
)
IF(WIN32 AND NOT CYGWIN)
# MINGW should go here too
IF(MSVC)
# /MD and /MDd are the standard values - if someone wants to use
# others, the libnames have to change here too
# use also ssl and ssleay32 in debug as fallback for openssl < 0.9.8b
# TODO: handle /MT and static lib
# In Visual C++ naming convention each of these four kinds of Windows libraries has it's standard suffix:
# * MD for dynamic-release
# * MDd for dynamic-debug
# * MT for static-release
# * MTd for static-debug
# Implementation details:
# We are using the libraries located in the VC subdir instead of the parent directory eventhough :
# libeay32MD.lib is identical to ../libeay32.lib, and
# ssleay32MD.lib is identical to ../ssleay32.lib
# In 1.1 version of OpenSSL we have other names of libs: libcrypto32MTd.lib and libssl32MTd.lib
if( "${CMAKE_SIZEOF_VOID_P}" STREQUAL "8" )
set(_OPENSSL_MSVC_ARCH_SUFFIX "64")
else()
set(_OPENSSL_MSVC_ARCH_SUFFIX "32")
endif()
FIND_LIBRARY(LIB_EAY_DEBUG
NAMES
libcrypto${_OPENSSL_MSVC_ARCH_SUFFIX}MDd libcrypto libeay32MDd libeay32
PATHS
${OPENSSL_ROOT_DIR}/lib/VC
${OPENSSL_ROOT_DIR}/debug/lib
)
FIND_LIBRARY(LIB_EAY_RELEASE
NAMES
libcrypto${_OPENSSL_MSVC_ARCH_SUFFIX}MD libcrypto libeay32MD libeay32
PATHS
${OPENSSL_ROOT_DIR}/lib/VC
${OPENSSL_ROOT_DIR}/lib
)
FIND_LIBRARY(SSL_EAY_DEBUG
NAMES
libssl${_OPENSSL_MSVC_ARCH_SUFFIX}MDd libssl ssleay32MDd ssleay32 ssl
PATHS
${OPENSSL_ROOT_DIR}/lib/VC
${OPENSSL_ROOT_DIR}/debug/lib
)
FIND_LIBRARY(SSL_EAY_RELEASE
NAMES
libssl${_OPENSSL_MSVC_ARCH_SUFFIX}MD libssl ssleay32MD ssleay32 ssl
PATHS
${OPENSSL_ROOT_DIR}/lib/VC
${OPENSSL_ROOT_DIR}/lib
)
if( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE )
set( OPENSSL_LIBRARIES
optimized ${SSL_EAY_RELEASE} ${LIB_EAY_RELEASE}
debug ${SSL_EAY_DEBUG} ${LIB_EAY_DEBUG}
)
else()
set( OPENSSL_LIBRARIES
${SSL_EAY_RELEASE}
${LIB_EAY_RELEASE}
)
endif()
MARK_AS_ADVANCED(SSL_EAY_DEBUG SSL_EAY_RELEASE LIB_EAY_DEBUG LIB_EAY_RELEASE)
ELSEIF(MINGW)
# same player, for MingW
FIND_LIBRARY(LIB_EAY
NAMES
libeay32
PATHS
${OPENSSL_ROOT_DIR}/lib/MinGW
)
FIND_LIBRARY(SSL_EAY NAMES
NAMES
ssleay32
PATHS
${OPENSSL_ROOT_DIR}/lib/MinGW
)
MARK_AS_ADVANCED(SSL_EAY LIB_EAY)
set( OPENSSL_LIBRARIES
${SSL_EAY}
${LIB_EAY}
)
ELSE(MSVC)
# Not sure what to pick for -say- intel, let's use the toplevel ones and hope someone report issues:
FIND_LIBRARY(LIB_EAY
NAMES
libeay32
PATHS
${OPENSSL_ROOT_DIR}/lib
${OPENSSL_ROOT_DIR}/lib/VC
)
FIND_LIBRARY(SSL_EAY
NAMES
ssleay32
PATHS
${OPENSSL_ROOT_DIR}/lib
${OPENSSL_ROOT_DIR}/lib/VC
)
MARK_AS_ADVANCED(SSL_EAY LIB_EAY)
SET( OPENSSL_LIBRARIES ${SSL_EAY} ${LIB_EAY} )
ENDIF(MSVC)
ELSE(WIN32 AND NOT CYGWIN)
FIND_LIBRARY(OPENSSL_SSL_LIBRARIES NAMES ssl ssleay32 ssleay32MD)
FIND_LIBRARY(OPENSSL_CRYPTO_LIBRARIES NAMES crypto)
MARK_AS_ADVANCED(OPENSSL_CRYPTO_LIBRARIES OPENSSL_SSL_LIBRARIES)
SET(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES})
ENDIF(WIN32 AND NOT CYGWIN)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(OpenSSL DEFAULT_MSG
OPENSSL_LIBRARIES
OPENSSL_INCLUDE_DIR
)
MARK_AS_ADVANCED(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES)

View file

@ -0,0 +1,115 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
FUNCTION(GET_COMMON_PCH_PARAMS PCH_HEADER PCH_FE INCLUDE_PREFIX)
GET_FILENAME_COMPONENT(PCH_HEADER_N ${PCH_HEADER} NAME)
GET_DIRECTORY_PROPERTY(TARGET_INCLUDES INCLUDE_DIRECTORIES)
FOREACH(ITEM ${TARGET_INCLUDES})
LIST(APPEND INCLUDE_FLAGS_LIST "${INCLUDE_PREFIX}\"${ITEM}\" ")
ENDFOREACH(ITEM)
SET(PCH_HEADER_NAME ${PCH_HEADER_N} PARENT_SCOPE)
SET(PCH_HEADER_OUT ${CMAKE_CURRENT_BINARY_DIR}/${PCH_HEADER_N}.${PCH_FE} PARENT_SCOPE)
SET(INCLUDE_FLAGS ${INCLUDE_FLAGS_LIST} PARENT_SCOPE)
ENDFUNCTION(GET_COMMON_PCH_PARAMS)
FUNCTION(GENERATE_CXX_PCH_COMMAND TARGET_NAME INCLUDE_FLAGS IN PCH_SRC OUT)
IF (CMAKE_BUILD_TYPE)
STRING(TOUPPER _${CMAKE_BUILD_TYPE} CURRENT_BUILD_TYPE)
ENDIF ()
SET(COMPILE_FLAGS ${CMAKE_CXX_FLAGS${CURRENT_BUILD_TYPE}})
LIST(APPEND COMPILE_FLAGS ${CMAKE_CXX_FLAGS})
IF ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
IF (NOT "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "")
LIST(APPEND COMPILE_FLAGS "-arch ${CMAKE_OSX_ARCHITECTURES}")
ENDIF ()
IF (NOT "${CMAKE_OSX_SYSROOT}" STREQUAL "")
LIST(APPEND COMPILE_FLAGS "-isysroot ${CMAKE_OSX_SYSROOT}")
ENDIF ()
IF (NOT "${CMAKE_OSX_DEPLOYMENT_TARGET}" STREQUAL "")
LIST(APPEND COMPILE_FLAGS "-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
ENDIF ()
ENDIF ()
GET_DIRECTORY_PROPERTY(TARGET_DEFINITIONS COMPILE_DEFINITIONS)
FOREACH(ITEM ${TARGET_DEFINITIONS})
LIST(APPEND DEFINITION_FLAGS "-D${ITEM} ")
ENDFOREACH(ITEM)
SEPARATE_ARGUMENTS(COMPILE_FLAGS)
SEPARATE_ARGUMENTS(INCLUDE_FLAGS)
SEPARATE_ARGUMENTS(DEFINITION_FLAGS)
GET_FILENAME_COMPONENT(PCH_SRC_N ${PCH_SRC} NAME)
ADD_LIBRARY(${PCH_SRC_N}_dephelp MODULE ${PCH_SRC})
ADD_CUSTOM_COMMAND(
OUTPUT ${OUT}
COMMAND ${CMAKE_CXX_COMPILER}
ARGS ${DEFINITION_FLAGS} ${COMPILE_FLAGS} ${INCLUDE_FLAGS} -x c++-header ${IN} -o ${OUT}
DEPENDS ${IN} ${PCH_SRC_N}_dephelp
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
ADD_CUSTOM_TARGET(generate_${PCH_SRC_N}
DEPENDS ${OUT}
)
ADD_DEPENDENCIES(${TARGET_NAME} generate_${PCH_SRC_N})
ENDFUNCTION(GENERATE_CXX_PCH_COMMAND)
FUNCTION(ADD_CXX_PCH_GCC TARGET_NAME PCH_HEADER PCH_SOURCE)
GET_COMMON_PCH_PARAMS(${PCH_HEADER} "gch" "-I")
GENERATE_CXX_PCH_COMMAND(${TARGET_NAME} "${INCLUDE_FLAGS}" ${PCH_HEADER} ${PCH_SOURCE} ${PCH_HEADER_OUT})
SET_TARGET_PROPERTIES(
${TARGET_NAME} PROPERTIES
COMPILE_FLAGS "-include ${CMAKE_CURRENT_BINARY_DIR}/${PCH_HEADER_NAME}"
)
ENDFUNCTION(ADD_CXX_PCH_GCC)
FUNCTION(ADD_CXX_PCH_CLANG TARGET_NAME PCH_HEADER PCH_SOURCE)
GET_COMMON_PCH_PARAMS(${PCH_HEADER} "pch" "-I")
GENERATE_CXX_PCH_COMMAND(${TARGET_NAME} "${INCLUDE_FLAGS}" ${PCH_HEADER} ${PCH_SOURCE} ${PCH_HEADER_OUT})
SET_TARGET_PROPERTIES(
${TARGET_NAME} PROPERTIES
COMPILE_FLAGS "-include-pch ${PCH_HEADER_OUT}"
)
ENDFUNCTION(ADD_CXX_PCH_CLANG)
FUNCTION(ADD_CXX_PCH_MSVC TARGET_NAME PCH_HEADER PCH_SOURCE)
GET_COMMON_PCH_PARAMS(${PCH_HEADER} "pch" "/I")
SET_TARGET_PROPERTIES(
${TARGET_NAME} PROPERTIES
COMPILE_FLAGS "/FI${PCH_HEADER_NAME} /Yu${PCH_HEADER_NAME}"
)
SET_SOURCE_FILES_PROPERTIES(
${PCH_SOURCE} PROPERTIES
COMPILE_FLAGS "/Yc${PCH_HEADER_NAME}"
)
ENDFUNCTION(ADD_CXX_PCH_MSVC)
FUNCTION(ADD_CXX_PCH TARGET_NAME PCH_HEADER PCH_SOURCE)
IF (MSVC)
ADD_CXX_PCH_MSVC(${TARGET_NAME} ${PCH_HEADER} ${PCH_SOURCE})
ELSEIF ("${CMAKE_GENERATOR}" MATCHES "Xcode")
SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER YES
XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/${PCH_HEADER}"
)
ELSEIF ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
ADD_CXX_PCH_CLANG(${TARGET_NAME} ${PCH_HEADER} ${PCH_SOURCE})
ELSEIF ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
ADD_CXX_PCH_GCC(${TARGET_NAME} ${PCH_HEADER} ${PCH_SOURCE})
ENDIF ()
ENDFUNCTION(ADD_CXX_PCH)

View file

@ -0,0 +1,29 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# find Readline (terminal input library) includes and library
#
# READLINE_INCLUDE_DIR - where the directory containing the READLINE headers can be found
# READLINE_LIBRARY - full path to the READLINE library
# READLINE_FOUND - TRUE if READLINE was found
FIND_PATH(READLINE_INCLUDE_DIR readline/readline.h)
FIND_LIBRARY(READLINE_LIBRARY NAMES readline)
IF (READLINE_INCLUDE_DIR AND READLINE_LIBRARY)
SET(READLINE_FOUND TRUE)
MESSAGE(STATUS "Found Readline library: ${READLINE_LIBRARY}")
MESSAGE(STATUS "Include dir is: ${READLINE_INCLUDE_DIR}")
INCLUDE_DIRECTORIES(${READLINE_INCLUDE_DIR})
ELSE (READLINE_INCLUDE_DIR AND READLINE_LIBRARY)
SET(READLINE_FOUND FALSE)
MESSAGE(FATAL_ERROR "** Readline library not found!\n** Your distro may provide a binary for Readline e.g. for ubuntu try apt-get install libreadline5-dev")
ENDIF (READLINE_INCLUDE_DIR AND READLINE_LIBRARY)

25
cmake/options.cmake Normal file
View file

@ -0,0 +1,25 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
option(SERVERS "Build worldserver and authserver" 1)
option(SCRIPTS "Build core with scripts included" 1)
option(TOOLS "Build map/vmap/mmap extraction/assembler tools" 0)
option(USE_SCRIPTPCH "Use precompiled headers when compiling scripts" 1)
option(USE_COREPCH "Use precompiled headers when compiling servers" 1)
option(WITH_WARNINGS "Show all warnings during compile" 0)
option(WITH_COREDEBUG "Include additional debug-code in core" 0)
option(WITH_SANITIZER "Build with AddressSanitizer" 0)
option(AUTH_SERVER "Build authserver" 1)
option(UPDATER "Build updater" 0)
if (UNIX)
option(BUILD_DEPLOY "Option of a build for deployment" 1)
endif()
option(BUILD_DEV "Experimental build for development under Windows" 0)

View file

@ -0,0 +1,34 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# from cmake wiki
IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
STRING(REGEX REPLACE "\n" ";" files "${files}")
FOREACH(file ${files})
MESSAGE(STATUS "Uninstalling \"${file}\"")
IF(EXISTS "${file}")
EXEC_PROGRAM(
"@CMAKE_COMMAND@" ARGS "-E remove \"${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
IF("${rm_retval}" STREQUAL 0)
ELSE("${rm_retval}" STREQUAL 0)
MESSAGE(FATAL_ERROR "Problem when removing \"${file}\"")
ENDIF("${rm_retval}" STREQUAL 0)
ELSE(EXISTS "${file}")
MESSAGE(STATUS "File \"${file}\" does not exist.")
ENDIF(EXISTS "${file}")
ENDFOREACH(file)

View file

@ -0,0 +1,16 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# set installation prefix
if( PREFIX )
set(CMAKE_INSTALL_PREFIX "${PREFIX}")
endif()

View file

@ -0,0 +1,55 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# Package overloads - Linux
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
if (NOT NOJEM)
set(JEMALLOC_LIBRARY "jemalloc")
message(STATUS "UNIX: Using jemalloc")
endif()
endif()
# set default configuration directory
if( NOT CONF_DIR )
set(CONF_DIR ${CMAKE_INSTALL_PREFIX}/etc)
message(STATUS "UNIX: Using default configuration directory")
endif()
# set default library directory
if( NOT LIBSDIR )
set(LIBSDIR ${CMAKE_INSTALL_PREFIX}/lib)
message(STATUS "UNIX: Using default library directory")
endif()
# configure uninstaller
configure_file(
"${CMAKE_SOURCE_DIR}/cmake/platform/cmake_uninstall.in.cmake"
"${CMAKE_BINARY_DIR}/cmake_uninstall.cmake"
@ONLY
)
message(STATUS "UNIX: Configuring uninstall target")
# create uninstaller target (allows for using "make uninstall")
add_custom_target(uninstall
"${CMAKE_COMMAND}" -P "${CMAKE_BINARY_DIR}/cmake_uninstall.cmake"
)
message(STATUS "UNIX: Created uninstall target")
message(STATUS "UNIX: Detected compiler: ${CMAKE_C_COMPILER}")
if(CMAKE_C_COMPILER MATCHES "gcc" OR CMAKE_C_COMPILER_ID STREQUAL "GNU")
include(${CMAKE_SOURCE_DIR}/cmake/compiler/gcc/settings.cmake)
elseif(CMAKE_C_COMPILER MATCHES "icc")
include(${CMAKE_SOURCE_DIR}/cmake/compiler/icc/settings.cmake)
elseif(CMAKE_C_COMPILER MATCHES "clang")
include(${CMAKE_SOURCE_DIR}/cmake/compiler/clang/settings.cmake)
else()
add_definitions(-D_BUILD_DIRECTIVE='"${CMAKE_BUILD_TYPE}"')
endif()

View file

@ -0,0 +1,48 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# Platform-specfic options
option(USE_MYSQL_SOURCES "Use included MySQL-sources to build libraries" 0)
# Package overloads
set(ACE_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/dep/acelite)
set(ACE_LIBRARY "ace")
set(BZIP2_LIBRARIES "bzip2")
set(ZLIB_LIBRARIES "zlib")
if(MSVC_VERSION EQUAL 1900)
set( USE_MYSQL_SOURCES 0 )
message(STATUS "MySQL: Disabled supplied MySQL sources")
endif(MSVC_VERSION EQUAL 1900)
if( USE_MYSQL_SOURCES )
set(MYSQL_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/dep/mysqllite/include)
set(MYSQL_LIBRARY "libmysql")
set( MYSQL_FOUND 1 )
message(STATUS "Using supplied MySQL sources")
endif()
# check the CMake preload parameters (commented out by default)
# overload CMAKE_INSTALL_PREFIX if not being set properly
#if( WIN32 )
# if( NOT CYGWIN )
# if( NOT CMAKE_INSTALL_PREFIX )
# set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/bin")
# endif()
# endif()
#endif()
if ( MSVC )
include(${CMAKE_SOURCE_DIR}/cmake/compiler/msvc/settings.cmake)
elseif ( MINGW )
include(${CMAKE_SOURCE_DIR}/cmake/compiler/mingw/settings.cmake)
endif()

98
cmake/showoptions.cmake Normal file
View file

@ -0,0 +1,98 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# output generic information about the core and buildtype chosen
message("")
message("* SkyFire revision : ${rev_hash} ${rev_date} (${rev_branch} branch)")
if( UNIX )
message("* SkyFire buildtype : ${CMAKE_BUILD_TYPE}")
endif()
message("")
# output information about installation-directories and locations
message("* Install core to : ${CMAKE_INSTALL_PREFIX}")
if( UNIX )
message("* Install libraries to : ${LIBSDIR}")
message("* Install configs to : ${CONF_DIR}")
endif()
message("")
# Show infomation about the options selected during configuration
if( SERVERS )
message("* Build worldserver : Yes (default)")
if (AUTH_SERVER)
message("* Build authserver : Yes (default)")
else()
message("* Build authserver : No")
endif()
else()
message("* Build world/authserver : No")
endif()
if( SCRIPTS )
message("* Build with scripts : Yes (default)")
add_definitions(-DSCRIPTS)
else()
message("* Build with scripts : No")
endif()
if( TOOLS )
message("* Build map/vmap tools : Yes")
add_definitions(-DNO_CORE_FUNCS)
else()
message("* Build map/vmap tools : No (default)")
endif()
if( USE_COREPCH )
message("* Build core w/PCH : Yes (default)")
else()
message("* Build core w/PCH : No")
endif()
if( USE_SCRIPTPCH )
message("* Build scripts w/PCH : Yes (default)")
else()
message("* Build scripts w/PCH : No")
endif()
if( WITH_WARNINGS )
message("* Show all warnings : Yes")
else()
message("* Show compile-warnings : No (default)")
endif()
if( WITH_COREDEBUG )
message("* Use coreside debug : Yes")
add_definitions(-DTRINITY_DEBUG)
else()
message("* Use coreside debug : No (default)")
endif()
if( WIN32 )
if( USE_MYSQL_SOURCES )
message("* Use MySQL sourcetree : Yes (default)")
else()
message("* Use MySQL sourcetree : No")
endif()
endif( WIN32 )
if ( NOJEM )
message("")
message(" *** NOJEM - WARNING!")
message(" *** jemalloc linking has been disabled!")
message(" *** Please note that this is for DEBUGGING WITH VALGRIND only!")
message(" *** DO NOT DISABLE IT UNLESS YOU KNOW WHAT YOU'RE DOING!")
endif()
message("")

31
cmake/stack_direction.c Normal file
View file

@ -0,0 +1,31 @@
/* Copyright (C) 2009 Sun Microsystems, Inc
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/* Check stack direction (0-down, 1-up) */
int f(int *a)
{
int b;
return(&b > a)?1:0;
}
/*
Prevent compiler optimizations by calling function
through pointer.
*/
volatile int (*ptr_f)(int *) = f;
int main()
{
int a;
return ptr_f(&a);
}

View file

@ -0,0 +1,24 @@
@ECHO OFF
REM
REM
SETLOCAL
PUSHD %~dp0
SET COVDIR=C:\cov-analysis-win32-7.6.0
CALL "%VS120COMNTOOLS%..\..\VC\vcvarsall.bat" x86
SET MSBUILD_SWITCHES=/nologo
"%COVDIR%\bin\cov-build.exe" --dir cov-int MSBuild "Project_SkyFire.sln" %MSBUILD_SWITCHES%
GOTO End
:End
POPD
ECHO. & ECHO Press any key to close this window...
PAUSE >NUL
ENDLOCAL
EXIT /B

View file

@ -0,0 +1,29 @@
@ECHO OFF
REM
REM
SETLOCAL
PUSHD %~dp0
:tar
IF EXIST "Project_SkyFire.tgz" DEL "Project_SkyFire.tgz"
tar --version 1>&2 2>NUL || (ECHO. & ECHO ERROR: tar not found. try to use seven zip. & GOTO SevenZip)
tar czvf Project_SkyFire.tgz cov-int
GOTO End
:SevenZip
IF NOT EXIST "C:\Program Files (x86)\7-Zip\7z.exe" (ECHO. & ECHO ERROR: "C:\Program Files (x86)\7-Zip\7z.exe" not found & GOTO End)
IF EXIST "Project_SkyFire.tar" DEL "Project_SkyFire.tar"
IF EXIST "Project_SkyFire.tgz" DEL "Project_SkyFire.tgz"
"C:\Program Files (x86)\7-Zip\7z.exe" a -ttar Project_SkyFire.tar cov-int
"C:\Program Files (x86)\7-Zip\7z.exe" a -tgzip Project_SkyFire.tgz Project_SkyFire.tar
IF EXIST "Project_SkyFire.tar" DEL "Project_SkyFire.tar"
GOTO End
:End
POPD
ECHO. & ECHO Press any key to close this window...
PAUSE >NUL
ENDLOCAL
EXIT /B

View file

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptConverter", "ScriptConverter\ScriptConverter.csproj", "{AB4C10BF-53AD-44AC-8B7A-B3F406DF468A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AB4C10BF-53AD-44AC-8B7A-B3F406DF468A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AB4C10BF-53AD-44AC-8B7A-B3F406DF468A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AB4C10BF-53AD-44AC-8B7A-B3F406DF468A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AB4C10BF-53AD-44AC-8B7A-B3F406DF468A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,226 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
namespace ScriptConverter {
class Program {
static void Main(string[] args) {
if (args.Length != 1) {
Console.WriteLine("Usage: ScriptsConverter.exe [path_to_dir|path_to_file]");
} else {
string path = args[0];
if (File.Exists(path)) {
ProcessFile(path);
} else if (Directory.Exists(path)) {
ProcessDirectory(path);
} else {
Console.WriteLine("Invalid file or directory specified.\r\n\r\nUsage: ScriptsConverter.exe [path_to_dir|path_to_file]");
}
}
}
static void ProcessDirectory(string path) {
string[] files = Directory.GetFiles(path, "*.cpp");
foreach (string file in files) {
ProcessFile(file);
}
string[] dirs = Directory.GetDirectories(path);
foreach (string dir in dirs) {
ProcessDirectory(dir);
}
}
class ScriptData {
public int type = 0;
public string name;
public ArrayList methods = new ArrayList();
public string instanceName = null;
public string aiName = null;
public string[] special = new string[] { "GetAI_", "GetInstance_", "GetInstanceData_" };
public void AddMethod(string method) {
methods.Add(method);
int i = 0;
foreach (string s in special) {
++i;
int pos = method.IndexOf(s);
if (pos != -1) {
type = i;
string name = method.Substring(pos + s.Length);
if (i == 1) {
aiName = name + "AI";
}
if (i == 2 || i == 3)
instanceName = name;
}
}
}
public override string ToString() {
StringBuilder sb = new StringBuilder();
sb.AppendFormat("Script: {0}\n", name);
foreach (string method in methods)
sb.Append(" ").Append(method).Append("\n");
sb.Append("\n");
return sb.ToString();
}
}
static string GetMethod(string method, ref string txt, ref int minPos) {
string res = null;
Regex r = new Regex(method + "(\\s|:|[(])");
Match m = r.Match(txt);
if (m.Success) {
int pos = m.Index;
while (pos-- >= 0 && pos < txt.Length) {
if (txt[pos] == '\n') break;
}
//pos++;
int lastPos = txt.IndexOf("\n}", pos);
if (lastPos != -1) {
lastPos += 2;
while (lastPos++ >= 0 && lastPos < txt.Length) {
if (txt[lastPos] == '\n') break;
}
res = txt.Substring(pos, lastPos - pos);
txt = txt.Remove(pos, lastPos - pos);
}
if (pos < minPos)
minPos = pos;
}
return res;
}
static void ProcessFile(string filePath) {
Console.WriteLine(filePath);
string txt = File.ReadAllText(filePath);
string[] lines = File.ReadAllLines(filePath);
Array.Reverse(lines);
ArrayList scripts = new ArrayList();
ScriptData data = null;
bool scriptStart = false;
foreach (string line in lines) {
if (line.IndexOf("Script *") != -1) {
break;
}
if (line.IndexOf("->RegisterSelf();") != -1) {
scriptStart = true;
data = new ScriptData();
continue;
}
if (scriptStart) {
if (line.IndexOf("= new Script") != -1) {
scriptStart = false;
scripts.Add(data);
data = null;
continue;
}
Regex r = new Regex("newscript->([a-zA-Z]+) *= *&?([\"_a-zA-Z0-9]+);");
Match m = r.Match(line);
if (m.Success) {
if (m.Groups[1].Value.Equals("Name")) {
data.name = m.Groups[2].Value.Trim(new char[] { '"' });
} else {
data.AddMethod(m.Groups[2].Value);
}
}
continue;
}
}
if (scripts.Count != 0) {
string register = "";
foreach (ScriptData sd in scripts) {
string ss = "";
Console.WriteLine(sd);
int minPos = txt.Length;
foreach (string method in sd.methods) {
string s = GetMethod(method, ref txt, ref minPos);
ss += s + "\n";
}
if (sd.instanceName != null) {
string s = GetMethod("struct " + sd.instanceName, ref txt, ref minPos);
ss += s + "\n";
}
if (sd.aiName != null) {
string ai = GetMethod("struct " + sd.aiName, ref txt, ref minPos);
if (ai != null) {
string sm = null;
Regex r = new Regex("\\S+ " + sd.aiName + "::([^( ]+)");
while (r.IsMatch(txt)) {
Match m = r.Match(txt);
int startPos = m.Index;
int endPos = txt.IndexOf("\n}", startPos);
if (endPos != -1)
endPos += 2;
while (endPos++ >= 0 && endPos < txt.Length) {
if (txt[endPos] == '\n') break;
}
sm = txt.Substring(startPos, endPos - startPos);
txt = txt.Remove(startPos, endPos - startPos);
if (sm != null) {
sm = sm.Replace("\n", "\n ");
Regex r1 = new Regex("\\S+ " + m.Groups[1] + " *\\([^)]*\\) *;");
Match m1 = r1.Match(ai);
if (m1.Success) {
ai = r1.Replace(ai, sm);
}
}
}
ai = ai.Replace(sd.aiName + "::", "");
ss += ai + "\n";
}
}
if (ss.Length != 0) {
string typeName = "UnknownScript";
switch (sd.type) {
case 1: typeName = "CreatureScript"; break;
case 2: typeName = "InstanceMapScript"; break;
default:
if (sd.name.IndexOf("npc") == 0)
typeName = "CreatureScript";
else if (sd.name.IndexOf("mob") == 0)
typeName = "CreatureScript";
else if (sd.name.IndexOf("boss_") == 0)
typeName = "CreatureScript";
else if (sd.name.IndexOf("item_") == 0)
typeName = "ItemScript";
else if (sd.name.IndexOf("go_") == 0)
typeName = "GameObjectScript";
else if (sd.name.IndexOf("at_") == 0)
typeName = "AreaTriggerScript";
else if (sd.name.IndexOf("instance_") == 0)
typeName = "InstanceMapScript";
break;
}
if (sd.instanceName != null)
ss = ss.Replace(sd.instanceName, sd.instanceName + "_InstanceMapScript");
ss = ss.Replace("\n", "\n ");
ss = "class " + sd.name + " : public " + typeName + "\n{\npublic:\n " +
sd.name + "() : " + typeName + "(\"" + sd.name + "\") { }\n" + ss + "\n};";
ss = ss.Replace("_" + sd.name, "");
ss = ss.Replace("AIAI", "AI");
ss = ss.Replace(" \r\n", "\r\n");
ss = ss.Replace(" \n", "\n");
txt = txt.Insert(minPos, ss);
register = " new " + sd.name + "();\n" + register;
}
}
Regex r2 = new Regex("void +AddSC_([_a-zA-Z0-9]+)");
Match m2 = r2.Match(txt);
if (m2.Success) {
txt = txt.Remove(m2.Index);
txt += "void AddSC_" + m2.Groups[1].Value + "()\n{\n" + register + "}\n";
}
// File.Copy(filePath, filePath + ".bkp");
txt = txt.Replace("\r\n", "\n");
File.WriteAllText(filePath, txt);
}
}
}
}

View file

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ScriptConverter")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ScriptConverter")]
[assembly: AssemblyCopyright("Copyright © 2010")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("39b64a0a-a460-4177-925f-b9bdda4f2a1d")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{AB4C10BF-53AD-44AC-8B7A-B3F406DF468A}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ScriptConverter</RootNamespace>
<AssemblyName>ScriptConverter</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data.DataSetExtensions">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,6 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<StartArguments>
</StartArguments>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,201 @@
#!/usr/bin/python
import subprocess
import os
import sys
import json
import datetime
import getopt
import signal
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
# http://blog.mathieu-leplatre.info/colored-output-in-console-with-python.html
# following from Python cookbook, #475186
def has_colours(stream):
if not hasattr(stream, "isatty"):
return False
if not stream.isatty():
return False # auto color only on TTYs
try:
import curses
curses.setupterm()
return curses.tigetnum("colors") > 2
except:
# guess false in case of error
return False
has_colours = has_colours(sys.stdout)
def colorize(text, colour):
if has_colours:
return "\x1b[1;%dm" % (30+colour) + text + "\x1b[0m"
else:
return text
def showhelp():
print("Usage: {0}".format(sys.argv[0]))
print("Options:")
print(" -j N Parallel build, jobs count")
print(" --clean Run \"make clean\" before build")
print(" -c config Path to config file")
print(" -b bracnh Branch to update")
print(" -C Remove local changes before build")
print(" --authserver Build also authserver target")
print(" --no-sql Don't execute sql files")
print(" --no-build Don't run build")
print(" -h, --help Shows this message")
print(" -s Build with sanitizer")
sys.exit()
def runcmd(cmd):
p = subprocess.Popen(cmd.split())
p.wait()
if p.returncode != 0:
exit(-1)
def ensure_dir(path):
if not os.path.exists(path):
os.makedirs(path)
# read command line arguments, if any
threads = 8
cfgpath = "./config.json"
clean = False
cfg = { }
auth = 0
branch = ''
hgopt = ''
sanitize= 0
exe_sql = True
nobuild = False
opts, args = getopt.getopt(sys.argv[1:], "j:c:b:Chs",["clean", "authserver", "help", "no-sql", "no-build"])
for opt, arg in opts:
if opt == "-j":
threads = int(arg)
elif opt == "-c":
cfgpath = arg
elif opt == "--clean":
clean = True
elif opt == "--authserver":
auth = 1
elif opt == "-b":
branch = arg
elif opt == "-C":
hgopt += "-C "
elif opt == "--no-sql":
exe_sql = False
elif opt == "--no-build":
nobuild = True
elif opt == "-s":
sanitize = 1
elif opt == "-h" or opt == "--help":
showhelp()
# read config
if os.path.isfile(cfgpath):
f = open(cfgpath)
cfg = json.load(f);
else:
print("config not found")
sys.exit(-1)
install_dir = cfg.get('install_dir')
source_dir = cfg.get('source_dir')
build_dir = cfg.get('build_dir')
executable_name = cfg.get('executable_name')
enable_sql = cfg.get('enable_sql', True)
updater_path = cfg.get('updater_path', '')
updater_config = cfg.get('updater_config')
if branch == '':
branch = cfg.get('branch', 'default')
# setup commands
make = "make -j " + str(threads)
cmake = "cmake " + source_dir + " -DCMAKE_INSTALL_PREFIX=" + install_dir + " -DAUTH_SERVER=" + str(auth) + " -DWITH_SANITIZER=" + str(sanitize)
sqlupdate = "{0} -c {1}".format(updater_path,updater_config);
install_dir += "/bin/"
cmds = ["hg pull", "hg up " + hgopt + branch]
lockfile = os.getcwd() + '/build.lock'
# check lock
if (os.path.isfile(lockfile)):
print(colorize('Build already in progress by another instance', RED))
sys.exit(-1)
def exit(code = 0):
os.remove(lockfile)
sys.exit(code)
def interrupt_handler(signal, frame):
exit(-1)
# before file creation
signal.signal(signal.SIGINT, interrupt_handler)
# acquire lock
open(lockfile, 'w').close()
os.chdir(source_dir)
# execute all commands
for cmd in cmds:
runcmd(cmd)
if nobuild:
if not enable_sql:
print(colorize("--no-build used when SQL update disabled", RED))
exit(-1)
runcmd(sqlupdate)
exit() # okay...
# go to build directory, becasue cmake has no such option
ensure_dir(build_dir)
os.chdir(build_dir)
runcmd(cmake)
if clean:
runcmd("make clean")
starttime = datetime.datetime.now()
runcmd(make)
diff = datetime.datetime.now() - starttime
runcmd("make install")
# go to install directory
os.chdir(install_dir)
today = datetime.date.today()
template = "{0}{1:_%m_%d}".format(install_dir + executable_name, today)
old = template
if os.path.exists(old):
i = 2
old = template + "_" + str(i)
while os.path.exists(old):
i = i + 1
old = template + "_" + str(i)
if enable_sql and exe_sql:
runcmd(sqlupdate)
original = install_dir + executable_name;
target = install_dir + "worldserver"
# error in config
if not os.path.exists(target):
print(colorize("Ooops file {0} not found".format(target), RED))
exit(-1)
# just in case if the original file is not found, may be error in config
if os.path.exists(original):
os.rename(original, old)
print(colorize("\n{0} -> {1}".format(original, old), CYAN))
else:
print(colorize("Original file {0} not found!".format(original), RED))
os.rename(target, original)
print(colorize("{0} -> {1}".format(target, original), CYAN))
print(colorize("Built in {0}".format(diff), GREEN))
exit()

View file

@ -0,0 +1,9 @@
{
"install_dir" : "",
"source_dir" : "",
"build_dir" : "",
"updater_path" : "",
"updater_config" : "",
"target_name" : "",
"executable_name" : ""
}

View file

@ -0,0 +1,2 @@
#!/bin/bash
perl -wpi -e "s/\t/ /g" $1

View file

@ -0,0 +1,2 @@
#!/bin/bash
perl -wpi -e "s/ +$//g" $1

13
contrib/conf_merge/README Normal file
View file

@ -0,0 +1,13 @@
==== PHP merger (index.php + merge.php) ====
This is a PHP script for merging a new .dist file with your existing .conf file (worldserver.conf.dist and authserver.conf.dist)
It should also work with mangos dist/conf files as well.
It uses sessions so it is multi user safe, it adds any options that are removed to the bottom of the file,
commented out, just in case it removes something it shouldn't,
and, if you add all of your custom patch configs below "# Custom" they will be copied exactly as they are.
==== Perl merger (tc-conf-merger.pl) ====
Perl based command line merger script. This script feeds a .conf.dist file with variables that exist in an old .conf file,
comments and custom options are ignored.

View file

@ -0,0 +1,40 @@
<?php
/*
* Project Name: Config File Merge For Mangos/Trinity Server
* Date: 01.01.2010 inital version (0.0.1a)
* Author: Paradox
* Copyright: Paradox
* Email: iamparadox@netscape.net (paypal email)
* License: GNU General Public License v2(GPL)
*/
?>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<FORM enctype="multipart/form-data" ACTION="merge.php" METHOD="POST">
Dist File
<br />
<INPUT name="File1" TYPE="file">
<br />
Current Conf File
<br />
<INPUT name="File2" TYPE="file">
<br />
<INPUT TYPE=RADIO NAME="eol" VALUE="0" CHECKED >Win32 -
<INPUT TYPE=RADIO NAME="eol" VALUE="1" >UNIX/Linux
<br />
<INPUT TYPE="submit" VALUE="Submit">
<br />
If you have any custom settings, such as from patches,
<br />
make sure they are at the bottom of the file following
<br />
this block (add it if it's not there)
<br />
###############################################################################
<br />
# Custom
<br />
###############################################################################
<br />
<br />
</FORM>

View file

@ -0,0 +1,159 @@
<?php
/*
* Project Name: Config File Merge For Mangos/Trinity Server
* Date: 01.01.2010 inital version (0.0.1a)
* Author: Paradox
* Copyright: Paradox
* Email: iamparadox@netscape.net (paypal email)
* License: GNU General Public License v2(GPL)
*/
if (!empty($_FILES['File1']) && !empty($_FILES['File2']))
{
session_id();
session_start();
$basedir = "merge";
$eol = "\r\n";
if ($_POST['eol'])
$eol = "\n";
else
$eol = "\r\n";
if (!file_exists($basedir))
mkdir($basedir);
if (!file_exists($basedir."/".session_id()))
mkdir($basedir."/".session_id());
$upload1 = $basedir."/".session_id()."/".basename($_FILES['File1']['name']);
$upload2 = $basedir."/".session_id()."/".basename($_FILES['File2']['name']);
$newconfig = $basedir."/".session_id()."/trinitycore.conf.merged";
$out_file = fopen($newconfig, w);
$success = false;
if (move_uploaded_file($_FILES['File1']['tmp_name'], $upload1))
{
$success = true;
}
else
{
$success = false;
}
if (move_uploaded_file($_FILES['File2']['tmp_name'], $upload2))
{
$success = true;
}
else
{
$success = false;
}
if ($success)
{
$custom_found = false;
$in_file1 = fopen($upload1,r);
$in_file2 = fopen($upload2,r);
$array1 = array();
$array2 = array();
$line = trim(fgets($in_file1));
while (!feof($in_file1))
{
if ((substr($line,0,1) != '#' && substr($line,0,1) != ''))
{
list($key, $val) = explode("=",$line);
$key = trim($key);
$val = trim($val);
$array1[$key] = $val;
}
$line = trim(fgets($in_file1));
}
$line = trim(fgets($in_file2));
while (!feof($in_file2) && !$custom_found)
{
if (substr($line,0,1) != '#' && substr($line,0,1) != '')
{
list($key, $val) = explode("=",$line);
$key = trim($key);
$val = trim($val);
$array2[$key] = $val;
}
if (strtolower($line) == "# custom")
$custom_found = true;
else
$line = trim(fgets($in_file2));
}
fclose($in_file1);
foreach($array2 as $k => $v)
{
if (array_key_exists($k, $array1))
{
$array1[$k] = $v;
unset($array2[$k]);
}
}
$in_file1 = fopen($upload1,r);
$line = trim(fgets($in_file1));
while (!feof($in_file1))
{
if (substr($line,0,1) != '#' && substr($line,0,1) != '')
{
$array = array();
while (substr($line,0,1) != '#' && substr($line,0,1) != '')
{
list($key, $val) = explode("=",$line);
$key = trim($key);
$val = trim($val);
$array[$key] = $val;
$line = trim(fgets($in_file1));
}
foreach($array as $k => $v)
{
if (array_key_exists($k, $array1))
fwrite($out_file, $k."=".$array1[$k].$eol);
else
continue;
}
unset($array);
if (!feof($in_file1))
fwrite($out_file, $line.$eol);
}
else
fwrite($out_file, $line.$eol);
$line = trim(fgets($in_file1));
}
if ($custom_found)
{
fwrite($out_file, $eol);
fwrite($out_file, "###############################################################################".$eol);
fwrite($out_file, "# Custom".$eol);
$line = trim(fgets($in_file2));
while (!feof($in_file2))
{
fwrite($out_file, $line.$eol);
$line = trim(fgets($in_file2));
}
}
$first = true;
foreach($array2 as $k => $v)
{
if ($first)
{
fwrite($out_file, $eol);
fwrite($out_file, "###############################################################################".$eol);
fwrite($out_file, "# The Following values were removed from the config.".$eol);
$first = false;
}
fwrite($out_file, "# ".$k."=".$v.$eol);
}
unset($array1);
unset($array2);
fclose($in_file1);
fclose($in_file2);
fclose($out_file);
unlink($upload1);
unlink($upload2);
echo "Process done";
echo "<br /><a href=".$newconfig.">Click here to retrieve your merged conf</a>";
}
}
else
{
echo "An error has occurred";
}
?>

View file

@ -0,0 +1,43 @@
#!/usr/bin/perl -w
# Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
# Author: leak
# Date: 2010-12-06
# Note: Based on conf file format of rev 10507
use strict;
if (@ARGV != 3)
{
print("Usage:\ntc-conf-merger.pl <path to new .conf.dist> <path to old .conf> <path to output .conf>\n");
exit(1);
}
if (! -e $ARGV[0])
{
print("No file found at: ".$ARGV[0]);
exit(1);
}
elsif (! -e $ARGV[1])
{
print("No file found at: ".$ARGV[1]);
exit(1);
}
open CONFDIST, "<", $ARGV[0] or die "Error: Could not open ".$ARGV[0]."\n";
my $confdist = join "", <CONFDIST>;
close CONFDIST;
open CONFOLD, "<", $ARGV[1] or die "Error: Could not open ".$ARGV[1]."\n";
my $confold = join "", <CONFOLD>;
close CONFOLD;
while ($confold =~ m/^(?!#)(.*?)\s+?=\s+?(.*?)$/mg) {
my $key = $1, my $value = $2;
$confdist =~ s/^(\Q$key\E)(\s+?=\s+?)(.*)/$1$2$value/mg;
}
open OUTPUT, ">", $ARGV[2] or die "Error: Could not open ".$ARGV[2]."\n";
binmode(OUTPUT);
print OUTPUT $confdist;
close OUTPUT;

13
contrib/debugger/README Normal file
View file

@ -0,0 +1,13 @@
The included crashreport.gdb allows for semiautomated hunting of
crashes. The crashlog-file will be named backtrace.log and contains all
the commands required to partially automate a crashlog-creation with the
proper information.
Usage: gdb -x crashreport.gdb <executable file>
For creating an efficient backtrace, use -DCMAKE_BUILD_TYPE=Debug as a
parameter to CMake during configuration - this increases the filesize,
but includes all the needed information for a decent and efficient
crashreport.
-- Good luck, and happy crashhunting.

View file

@ -0,0 +1,18 @@
set logging overwrite on
set logging file backtrace.log
handle SIG33 pass nostop noprint
set pagination 0
set logging on
echo \n--- DEBUG: --- START\n\n
run
echo \n--- DEBUG: BACKTRACE FULL\n\n
backtrace full
echo \n--- DEBUG: INFO REGISTERS\n\n
info registers
echo \n--- DEBUG: CALLS (x/32i $pc)\n\n
x/32i $pc
echo \n--- DEBUG: THREAD APPLY ALL BACKTRACE\n
thread apply all backtrace
echo \n--- DEBUG: --- STOP\n\n
set logging off
quit

View file

@ -0,0 +1,116 @@
{
[1] ACE_Future::ready() race in WorldSession::ProcessQueryCallbacks(), a lock is used anyway in ACE_Future::get()/set()
Helgrind:Race
...
fun:_ZN14ACE_Future_RepIN7Trinity7AutoPtrI17PreparedResultSet16ACE_Thread_MutexEEE3setERKS4_R10ACE_FutureIS4_E
fun:_ZN10ACE_FutureIN7Trinity7AutoPtrI17PreparedResultSet16ACE_Thread_MutexEEE3setERKS4_
fun:_ZN21PreparedStatementTask7ExecuteEv
}
{
[1] ACE_Future::ready() race in WorldSession::ProcessQueryCallbacks(), a lock is used anyway in ACE_Future::get()/set()
Helgrind:Race
...
fun:_ZN14ACE_Future_RepIP14SQLQueryHolderE3setERKS1_R10ACE_FutureIS1_E
fun:_ZN10ACE_FutureIP14SQLQueryHolderE3setERKS1_
fun:_ZN18SQLQueryHolderTask7ExecuteEv
}
{
[2] ACE_Future::ready() race in WorldSession::ProcessQueryCallbacks(), a lock is used anyway in ACE_Future::get()/set()
Helgrind:Race
...
fun:_ZNK14ACE_Future_RepIP14SQLQueryHolderE5readyEv
fun:_ZNK10ACE_FutureIP14SQLQueryHolderE5readyEv
fun:_ZN12WorldSession21ProcessQueryCallbacksEv
}
{
[3] ACE_Future::attach()/detach() false positive in WorldSession::HandleCharEnumOpcode()
Helgrind:Race
...
fun:_ZN14ACE_Future_RepIN7Trinity7AutoPtrI17PreparedResultSet16ACE_Thread_MutexEEE6attachERPS5_
}
{
[4] ACE_Future::get() race in WorldSession::ProcessQueryCallbacks() , a lock is used anyway in ACE_Future::get()/set(), the only case when this is a race is if the same ACE_Future is reused by another thread
Helgrind:Race
...
fun:_ZNK14ACE_Future_RepIP14SQLQueryHolderE3getERS1_P14ACE_Time_Value
fun:_ZNK10ACE_FutureIP14SQLQueryHolderE3getERS1_P14ACE_Time_Value
fun:_ZN12WorldSession21ProcessQueryCallbacksEv
}
{
[4] ACE_Future::get() race in WorldSession::ProcessQueryCallbacks() , a lock is used anyway in ACE_Future::get()/set(), the only case when this is a race is if the same ACE_Future is reused by another thread
Helgrind:Race
...
fun:_ZNK10ACE_FutureIN7Trinity7AutoPtrI17PreparedResultSet16ACE_Thread_MutexEEE5readyEv
fun:_ZN12WorldSession21ProcessQueryCallbacksEv
}
{
[5] Race in WorldSession::ProcessQueryCallbacks(), added ASSERT(!m_result.ready())
Helgrind:Race
...
fun:_ZN6Player10LoadFromDBEjP14SQLQueryHolder
fun:_ZN12WorldSession17HandlePlayerLoginEP16LoginQueryHolder
fun:_ZN12WorldSession21ProcessQueryCallbacksEv
}
{
[5] Race in WorldSession::ProcessQueryCallbacks(), added ASSERT(!m_result.ready())
Helgrind:Race
...
fun:_ZN14SQLQueryHolder17GetPreparedResultEm
fun:_ZN12WorldSession17HandlePlayerLoginEP16LoginQueryHolder
fun:_ZN12WorldSession21ProcessQueryCallbacksEv
}
{
[5] Race in WorldSession::ProcessQueryCallbacks(), added ASSERT(!m_result.ready())
Helgrind:Race
...
fun:_ZN12WorldSession15LoadAccountDataEN7Trinity7AutoPtrI17PreparedResultSet16ACE_Thread_MutexEEj
fun:_ZN12WorldSession17HandlePlayerLoginEP16LoginQueryHolder
fun:_ZN12WorldSession21ProcessQueryCallbacksEv
}
{
[5] Race in WorldSession::ProcessQueryCallbacks(), added ASSERT(!m_result.ready())
Helgrind:Race
...
fun:_ZNK17PreparedResultSet5FetchEv
fun:_ZN12WorldSession17HandlePlayerLoginEP16LoginQueryHolder
fun:_ZN12WorldSession21ProcessQueryCallbacksEv
}
{
[5] Race in WorldSession::ProcessQueryCallbacks(), added ASSERT(!m_result.ready())
Helgrind:Race
...
fun:_ZNK5Field9GetUInt32Ev
fun:_ZN12WorldSession17HandlePlayerLoginEP16LoginQueryHolder
fun:_ZN12WorldSession21ProcessQueryCallbacksEv
}
{
[5] Race in WorldSession::ProcessQueryCallbacks(), added ASSERT(!m_result.ready())
Helgrind:Race
...
fun:_ZNK5Field8GetUInt8Ev
fun:_ZN12WorldSession17HandlePlayerLoginEP16LoginQueryHolder
fun:_ZN12WorldSession21ProcessQueryCallbacksEv
}
{
[6] False positive of possible race about ACE_Strong_Bound_Ptr
Helgrind:Race
...
fun:_ZN17PreparedResultSetD1Ev
fun:_ZN20ACE_Strong_Bound_PtrI17PreparedResultSet16ACE_Thread_MutexED1Ev
fun:_ZN7Trinity7AutoPtrI17PreparedResultSet16ACE_Thread_MutexED1Ev
fun:_ZN12WorldSession17HandlePlayerLoginEP16LoginQueryHolder
fun:_ZN12WorldSession21ProcessQueryCallbacksEv
}
{
[7] Race condition on bool in ACE, ignore
Helgrind:Race
fun:_ZN11WorldSocket12handle_closeEim
fun:_ZN20ACE_Dev_Poll_Reactor16remove_handler_iEimP17ACE_Event_Handler
}
{
[7] Race condition on bool in ACE, ignore
Helgrind:Race
fun:_ZNK11WorldSocket8IsClosedEv
fun:_ZN12WorldSession6UpdateEjR12PacketFilter
fun:_ZN5World14UpdateSessionsEj
}

44
dep/CMakeLists.txt Normal file
View file

@ -0,0 +1,44 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
if( CMAKE_COMPILER_IS_GNUCXX )
add_definitions(--no-warnings)
elseif( MSVC )
add_definitions(/W0)
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
if(SERVERS AND NOT NOJEM)
add_subdirectory(jemalloc)
endif()
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
add_subdirectory(acelite)
if(USE_MYSQL_SOURCES)
add_subdirectory(mysqllite)
endif()
if(TOOLS)
add_subdirectory(bzip2)
endif()
add_subdirectory(zlib)
endif()
add_subdirectory(g3dlite)
add_subdirectory(recastnavigation)
if(SERVERS)
add_subdirectory(gsoap)
endif()
if(TOOLS)
add_subdirectory(StormLib)
endif()

45
dep/PackageList.txt Normal file
View file

@ -0,0 +1,45 @@
SkyFire uses (parts of or in whole) the following opensource software :
ACE (ADAPTIVE Communication Environment)
http://www.cs.wustl.edu/~schmidt/ACE.html
Version: 6.1.4
bzip2 (a freely available, patent free, high-quality data compressor)
http://www.bzip.org/
Version: 1.0.6
G3D (a commercial-grade C++ 3D engine available as Open Source (BSD License)
http://g3d.sourceforge.net/
Version: 8.01-Release
jemalloc (a general-purpose scalable concurrent malloc-implementation)
http://www.canonware.com/jemalloc/
Version: 3.3.1
MySQL (the world's most popular open source database software)
http://www.mysql.com/
Version: 5.5.9 (GA)
SFMT (SIMD-oriented Fast Mersenne Twister)
Based on http://agner.org/random/
Version: 2010-Aug-03
utf8-cpp (UTF-8 with C++ in a Portable Way)
http://utfcpp.sourceforge.net/
Version: 2.3.4
zlib (A Massively Spiffy Yet Delicately Unobtrusive Compression Library)
http://www.zlib.net/
Version: 1.2.7
gSOAP (a portable development toolkit for C and C++ XML Web services and XML data bindings)
http://gsoap2.sourceforge.net/
Version: 2.8.10
recastnavigation (Recast is state of the art navigation mesh construction toolset for games)
http://code.google.com/p/recastnavigation/
Version: 1.4
StormLib (a pack of modules, written in C++, which are able to read and also to write files from/to the MPQ archives)
http://www.zezula.net/en/mpq/stormlib.html
Version: 8.04

357
dep/SFMT/SFMT.h Normal file
View file

@ -0,0 +1,357 @@
/*
* Copyright notice
* ================
* GNU General Public License http://www.gnu.org/licenses/gpl.html
* This C++ implementation of SFMT contains parts of the original C code
* which was published under the following BSD license, which is therefore
* in effect in addition to the GNU General Public License.
* Copyright (c) 2006, 2007 by Mutsuo Saito, Makoto Matsumoto and Hiroshima University.
* Copyright (c) 2008 by Agner Fog.
* Copyright (c) 2008-2013 Trinity Core
*
* BSD License:
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* > Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* > Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* > Neither the name of the Hiroshima University nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SFMT_H
#define SFMT_H
#include <emmintrin.h> // Define SSE2 intrinsics
#include "randomc.h" // Define integer types etc
#include <time.h>
#include <new>
// Choose one of the possible Mersenne exponents.
// Higher values give longer cycle length and use more memory:
//#define MEXP 607
//#define MEXP 1279
//#define MEXP 2281
//#define MEXP 4253
#define MEXP 11213
//#define MEXP 19937
//#define MEXP 44497
// Define constants for the selected Mersenne exponent:
#if MEXP == 44497
#define SFMT_N 348 // Size of state vector
#define SFMT_M 330 // Position of intermediate feedback
#define SFMT_SL1 5 // Left shift of W[N-1], 32-bit words
#define SFMT_SL2 3 // Left shift of W[0], *8, 128-bit words
#define SFMT_SR1 9 // Right shift of W[M], 32-bit words
#define SFMT_SR2 3 // Right shift of W[N-2], *8, 128-bit words
#define SFMT_MASK 0xeffffffb,0xdfbebfff,0xbfbf7bef,0x9ffd7bff // AND mask
#define SFMT_PARITY 1,0,0xa3ac4000,0xecc1327a // Period certification vector
#elif MEXP == 19937
#define SFMT_N 156 // Size of state vector
#define SFMT_M 122 // Position of intermediate feedback
#define SFMT_SL1 18 // Left shift of W[N-1], 32-bit words
#define SFMT_SL2 1 // Left shift of W[0], *8, 128-bit words
#define SFMT_SR1 11 // Right shift of W[M], 32-bit words
#define SFMT_SR2 1 // Right shift of W[N-2], *8, 128-bit words
#define SFMT_MASK 0xdfffffef,0xddfecb7f,0xbffaffff,0xbffffff6 // AND mask
#define SFMT_PARITY 1,0,0,0x13c9e684 // Period certification vector
#elif MEXP == 11213
#define SFMT_N 88 // Size of state vector
#define SFMT_M 68 // Position of intermediate feedback
#define SFMT_SL1 14 // Left shift of W[N-1], 32-bit words
#define SFMT_SL2 3 // Left shift of W[0], *8, 128-bit words
#define SFMT_SR1 7 // Right shift of W[M], 32-bit words
#define SFMT_SR2 3 // Right shift of W[N-2], *8, 128-bit words
#define SFMT_MASK 0xeffff7fb,0xffffffef,0xdfdfbfff,0x7fffdbfd // AND mask
#define SFMT_PARITY 1,0,0xe8148000,0xd0c7afa3 // Period certification vector
#elif MEXP == 4253
#define SFMT_N 34 // Size of state vector
#define SFMT_M 17 // Position of intermediate feedback
#define SFMT_SL1 20 // Left shift of W[N-1], 32-bit words
#define SFMT_SL2 1 // Left shift of W[0], *8, 128-bit words
#define SFMT_SR1 7 // Right shift of W[M], 32-bit words
#define SFMT_SR2 1 // Right shift of W[N-2], *8, 128-bit words
#define SFMT_MASK 0x9f7bffff, 0x9fffff5f, 0x3efffffb, 0xfffff7bb // AND mask
#define SFMT_PARITY 0xa8000001, 0xaf5390a3, 0xb740b3f8, 0x6c11486d // Period certification vector
#elif MEXP == 2281
#define SFMT_N 18 // Size of state vector
#define SFMT_M 12 // Position of intermediate feedback
#define SFMT_SL1 19 // Left shift of W[N-1], 32-bit words
#define SFMT_SL2 1 // Left shift of W[0], *8, 128-bit words
#define SFMT_SR1 5 // Right shift of W[M], 32-bit words
#define SFMT_SR2 1 // Right shift of W[N-2], *8, 128-bit words
#define SFMT_MASK 0xbff7ffbf, 0xfdfffffe, 0xf7ffef7f, 0xf2f7cbbf // AND mask
#define SFMT_PARITY 0x00000001, 0x00000000, 0x00000000, 0x41dfa600 // Period certification vector
#elif MEXP == 1279
#define SFMT_N 10 // Size of state vector
#define SFMT_M 7 // Position of intermediate feedback
#define SFMT_SL1 14 // Left shift of W[N-1], 32-bit words
#define SFMT_SL2 3 // Left shift of W[0], *8, 128-bit words
#define SFMT_SR1 5 // Right shift of W[M], 32-bit words
#define SFMT_SR2 1 // Right shift of W[N-2], *8, 128-bit words
#define SFMT_MASK 0xf7fefffd, 0x7fefcfff, 0xaff3ef3f, 0xb5ffff7f // AND mask
#define SFMT_PARITY 0x00000001, 0x00000000, 0x00000000, 0x20000000 // Period certification vector
#elif MEXP == 607
#define SFMT_N 5 // Size of state vector
#define SFMT_M 2 // Position of intermediate feedback
#define SFMT_SL1 15 // Left shift of W[N-1], 32-bit words
#define SFMT_SL2 3 // Left shift of W[0], *8, 128-bit words
#define SFMT_SR1 13 // Right shift of W[M], 32-bit words
#define SFMT_SR2 3 // Right shift of W[N-2], *8, 128-bit words
#define SFMT_MASK 0xfdff37ff, 0xef7f3f7d, 0xff777b7d, 0x7ff7fb2f // AND mask
#define SFMT_PARITY 0x00000001, 0x00000000, 0x00000000, 0x5986f054 // Period certification vector
#endif
// Functions used by SFMTRand::RandomInitByArray (UNUSED AND COMMENTED OUT)
/*
static uint32_t func1(uint32_t x) {
return (x ^ (x >> 27)) * 1664525U;
}
static uint32_t func2(uint32_t x) {
return (x ^ (x >> 27)) * 1566083941U;
}
*/
// Subfunction for the sfmt algorithm
static inline __m128i sfmt_recursion(__m128i const &a, __m128i const &b,
__m128i const &c, __m128i const &d, __m128i const &mask) {
__m128i a1, b1, c1, d1, z1, z2;
b1 = _mm_srli_epi32(b, SFMT_SR1);
a1 = _mm_slli_si128(a, SFMT_SL2);
c1 = _mm_srli_si128(c, SFMT_SR2);
d1 = _mm_slli_epi32(d, SFMT_SL1);
b1 = _mm_and_si128(b1, mask);
z1 = _mm_xor_si128(a, a1);
z2 = _mm_xor_si128(b1, d1);
z1 = _mm_xor_si128(z1, c1);
z2 = _mm_xor_si128(z1, z2);
return z2;
}
// Class for SFMT generator
class SFMTRand { // Encapsulate random number generator
friend class ACE_TSS<SFMTRand>;
public:
SFMTRand()
{
LastInterval = 0;
RandomInit((int)(time(0)));
}
void RandomInit(int seed) // Re-seed
{
// Re-seed
uint32_t i; // Loop counter
uint32_t y = seed; // Temporary
uint32_t statesize = SFMT_N*4; // Size of state vector
// Fill state vector with random numbers from seed
((uint32_t*)state)[0] = y;
const uint32_t factor = 1812433253U;// Multiplication factor
for (i = 1; i < statesize; i++) {
y = factor * (y ^ (y >> 30)) + i;
((uint32_t*)state)[i] = y;
}
// Further initialization and period certification
Init2();
}
int32_t IRandom(int32_t min, int32_t max) // Output random integer
{
// Output random integer in the interval min <= x <= max
// Slightly inaccurate if (max-min+1) is not a power of 2
if (max <= min) {
if (max == min) return min; else return 0x80000000;
}
// Assume 64 bit integers supported. Use multiply and shift method
uint32_t interval; // Length of interval
uint64_t longran; // Random bits * interval
uint32_t iran; // Longran / 2^32
interval = (uint32_t)(max - min + 1);
longran = (uint64_t)BRandom() * interval;
iran = (uint32_t)(longran >> 32);
// Convert back to signed and return result
return (int32_t)iran + min;
}
uint32_t URandom(uint32_t min, uint32_t max)
{
// Output random integer in the interval min <= x <= max
// Slightly inaccurate if (max-min+1) is not a power of 2
if (max <= min) {
if (max == min) return min; else return 0;
}
// Assume 64 bit integers supported. Use multiply and shift method
uint32_t interval; // Length of interval
uint64_t longran; // Random bits * interval
uint32_t iran; // Longran / 2^32
interval = (uint32_t)(max - min + 1);
longran = (uint64_t)BRandom() * interval;
iran = (uint32_t)(longran >> 32);
// Convert back to signed and return result
return iran + min;
}
double Random() // Output random floating point number
{
// Output random floating point number
if (ix >= SFMT_N*4-1) {
// Make sure we have at least two 32-bit numbers
Generate();
}
uint64_t r = *(uint64_t*)((uint32_t*)state+ix);
ix += 2;
// 52 bits resolution for compatibility with assembly version:
return (int64_t)(r >> 12) * (1./(67108864.0*67108864.0));
}
uint32_t BRandom() // Output random bits
{
// Output 32 random bits
uint32_t y;
if (ix >= SFMT_N*4) {
Generate();
}
y = ((uint32_t*)state)[ix++];
return y;
}
private:
void Init2() // Various initializations and period certification
{
// Various initializations and period certification
uint32_t i, j, temp;
// Initialize mask
static const uint32_t maskinit[4] = {SFMT_MASK};
mask = _mm_loadu_si128((__m128i*)maskinit);
// Period certification
// Define period certification vector
static const uint32_t parityvec[4] = {SFMT_PARITY};
// Check if parityvec & state[0] has odd parity
temp = 0;
for (i = 0; i < 4; i++)
temp ^= parityvec[i] & ((uint32_t*)state)[i];
for (i = 16; i > 0; i >>= 1) temp ^= temp >> i;
if (!(temp & 1)) {
// parity is even. Certification failed
// Find a nonzero bit in period certification vector
for (i = 0; i < 4; i++) {
if (parityvec[i]) {
for (j = 1; j; j <<= 1) {
if (parityvec[i] & j) {
// Flip the corresponding bit in state[0] to change parity
((uint32_t*)state)[i] ^= j;
// Done. Exit i and j loops
i = 5; break;
}
}
}
}
}
// Generate first random numbers and set ix = 0
Generate();
}
void Generate() // Fill state array with new random numbers
{
// Fill state array with new random numbers
int i;
__m128i r, r1, r2;
r1 = state[SFMT_N - 2];
r2 = state[SFMT_N - 1];
for (i = 0; i < SFMT_N - SFMT_M; i++) {
r = sfmt_recursion(state[i], state[i + SFMT_M], r1, r2, mask);
state[i] = r;
r1 = r2;
r2 = r;
}
for (; i < SFMT_N; i++) {
r = sfmt_recursion(state[i], state[i + SFMT_M - SFMT_N], r1, r2, mask);
state[i] = r;
r1 = r2;
r2 = r;
}
ix = 0;
}
void* operator new(size_t size, std::nothrow_t const&)
{
return _mm_malloc(size, 16);
}
void operator delete(void* ptr, std::nothrow_t const&)
{
_mm_free(ptr);
}
void* operator new(size_t size)
{
return _mm_malloc(size, 16);
}
void operator delete(void* ptr)
{
_mm_free(ptr);
}
void* operator new[](size_t size, std::nothrow_t const&)
{
return _mm_malloc(size, 16);
}
void operator delete[](void* ptr, std::nothrow_t const&)
{
_mm_free(ptr);
}
void* operator new[](size_t size)
{
return _mm_malloc(size, 16);
}
void operator delete[](void* ptr)
{
_mm_free(ptr);
}
__m128i mask; // AND mask
__m128i state[SFMT_N]; // State vector for SFMT generator
uint32_t ix; // Index into state array
uint32_t LastInterval; // Last interval length for IRandom
uint32_t RLimit; // Rejection limit used by IRandom
};
#endif // SFMT_H

65
dep/SFMT/randomc.h Normal file
View file

@ -0,0 +1,65 @@
/*
* Copyright notice
* ================
* GNU General Public License http://www.gnu.org/licenses/gpl.html
* This C++ implementation of SFMT contains parts of the original C code
* which was published under the following BSD license, which is therefore
* in effect in addition to the GNU General Public License.
* Copyright (c) 2006, 2007 by Mutsuo Saito, Makoto Matsumoto and Hiroshima University.
* Copyright (c) 2008 by Agner Fog.
* Copyright (c) 2008-2013 Trinity Core
*
* BSD License:
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* > Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* > Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* > Neither the name of the Hiroshima University nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef RANDOMC_H
#define RANDOMC_H
// Define integer types with known size: int32_t, uint32_t, int64_t, uint64_t.
// If this doesn't work then insert compiler-specific definitions here:
#if defined(__GNUC__)
// Compilers supporting C99 or C++0x have inttypes.h defining these integer types
#include <inttypes.h>
#define INT64_SUPPORTED // Remove this if the compiler doesn't support 64-bit integers
#elif defined(_WIN16) || defined(__MSDOS__) || defined(_MSDOS)
// 16 bit systems use long int for 32 bit integer
typedef signed long int int32_t;
typedef unsigned long int uint32_t;
#elif defined(_MSC_VER)
// Microsoft have their own definition
typedef signed __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#define INT64_SUPPORTED // Remove this if the compiler doesn't support 64-bit integers
#else
// This works with most compilers
typedef signed int int32_t;
typedef unsigned int uint32_t;
typedef long long int64_t;
typedef unsigned long long uint64_t;
#define INT64_SUPPORTED // Remove this if the compiler doesn't support 64-bit integers
#endif
#endif // RANDOMC_H

283
dep/StormLib/CMakeLists.txt Normal file
View file

@ -0,0 +1,283 @@
# Copyright (C) 2011-2016 Project SkyFire <http://www.projectskyfire.org/>
# Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
set(SRC_FILES
src/adpcm/adpcm.cpp
src/huffman/huff.cpp
src/jenkins/lookup3.c
src/lzma/C/LzFind.c
src/lzma/C/LzmaDec.c
src/lzma/C/LzmaEnc.c
src/pklib/explode.c
src/pklib/implode.c
src/sparse/sparse.cpp
src/FileStream.cpp
src/SBaseCommon.cpp
src/SBaseFileTable.cpp
src/SCompression.cpp
src/SFileAddFile.cpp
src/SFileAttributes.cpp
src/SFileCompactArchive.cpp
src/SFileCreateArchive.cpp
src/SFileExtractFile.cpp
src/SFileFindFile.cpp
src/SFileListFile.cpp
src/SFileOpenArchive.cpp
src/SFileOpenFileEx.cpp
src/SFilePatchArchives.cpp
src/SFileReadFile.cpp
src/SFileVerify.cpp
)
set(TOMCRYPT_FILES
src/libtomcrypt/src/hashes/hash_memory.c
src/libtomcrypt/src/hashes/md5.c
src/libtomcrypt/src/hashes/sha1.c
src/libtomcrypt/src/math/ltm_desc.c
src/libtomcrypt/src/math/multi.c
src/libtomcrypt/src/math/rand_prime.c
src/libtomcrypt/src/misc/base64_decode.c
src/libtomcrypt/src/misc/crypt_argchk.c
src/libtomcrypt/src/misc/crypt_find_hash.c
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
src/libtomcrypt/src/misc/crypt_register_hash.c
src/libtomcrypt/src/misc/crypt_register_prng.c
src/libtomcrypt/src/misc/zeromem.c
src/libtomcrypt/src/pk/asn1/der_decode_bit_string.c
src/libtomcrypt/src/pk/asn1/der_decode_boolean.c
src/libtomcrypt/src/pk/asn1/der_decode_choice.c
src/libtomcrypt/src/pk/asn1/der_decode_ia5_string.c
src/libtomcrypt/src/pk/asn1/der_decode_integer.c
src/libtomcrypt/src/pk/asn1/der_decode_object_identifier.c
src/libtomcrypt/src/pk/asn1/der_decode_octet_string.c
src/libtomcrypt/src/pk/asn1/der_decode_printable_string.c
src/libtomcrypt/src/pk/asn1/der_decode_sequence_ex.c
src/libtomcrypt/src/pk/asn1/der_decode_sequence_flexi.c
src/libtomcrypt/src/pk/asn1/der_decode_sequence_multi.c
src/libtomcrypt/src/pk/asn1/der_decode_short_integer.c
src/libtomcrypt/src/pk/asn1/der_decode_utctime.c
src/libtomcrypt/src/pk/asn1/der_decode_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
src/libtomcrypt/src/pk/asn1/der_length_integer.c
src/libtomcrypt/src/pk/asn1/der_length_object_identifier.c
src/libtomcrypt/src/pk/asn1/der_length_octet_string.c
src/libtomcrypt/src/pk/asn1/der_length_printable_string.c
src/libtomcrypt/src/pk/asn1/der_length_sequence.c
src/libtomcrypt/src/pk/asn1/der_length_utctime.c
src/libtomcrypt/src/pk/asn1/der_sequence_free.c
src/libtomcrypt/src/pk/asn1/der_length_utf8_string.c
src/libtomcrypt/src/pk/asn1/der_length_short_integer.c
src/libtomcrypt/src/pk/ecc/ltc_ecc_map.c
src/libtomcrypt/src/pk/ecc/ltc_ecc_mul2add.c
src/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod.c
src/libtomcrypt/src/pk/ecc/ltc_ecc_points.c
src/libtomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c
src/libtomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c
src/libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c
src/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c
src/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c
src/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.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_verify_hash.c
src/libtomcrypt/src/pk/rsa/rsa_verify_simple.c
)
set(TOMMATH_FILES
src/libtommath/bncore.c
src/libtommath/bn_fast_mp_invmod.c
src/libtommath/bn_fast_mp_montgomery_reduce.c
src/libtommath/bn_fast_s_mp_mul_digs.c
src/libtommath/bn_fast_s_mp_mul_high_digs.c
src/libtommath/bn_fast_s_mp_sqr.c
src/libtommath/bn_mp_2expt.c
src/libtommath/bn_mp_abs.c
src/libtommath/bn_mp_add.c
src/libtommath/bn_mp_addmod.c
src/libtommath/bn_mp_add_d.c
src/libtommath/bn_mp_and.c
src/libtommath/bn_mp_clamp.c
src/libtommath/bn_mp_clear.c
src/libtommath/bn_mp_clear_multi.c
src/libtommath/bn_mp_cmp.c
src/libtommath/bn_mp_cmp_d.c
src/libtommath/bn_mp_cmp_mag.c
src/libtommath/bn_mp_cnt_lsb.c
src/libtommath/bn_mp_copy.c
src/libtommath/bn_mp_count_bits.c
src/libtommath/bn_mp_div.c
src/libtommath/bn_mp_div_2.c
src/libtommath/bn_mp_div_2d.c
src/libtommath/bn_mp_div_3.c
src/libtommath/bn_mp_div_d.c
src/libtommath/bn_mp_dr_is_modulus.c
src/libtommath/bn_mp_dr_reduce.c
src/libtommath/bn_mp_dr_setup.c
src/libtommath/bn_mp_exch.c
src/libtommath/bn_mp_exptmod.c
src/libtommath/bn_mp_exptmod_fast.c
src/libtommath/bn_mp_expt_d.c
src/libtommath/bn_mp_exteuclid.c
src/libtommath/bn_mp_fread.c
src/libtommath/bn_mp_fwrite.c
src/libtommath/bn_mp_gcd.c
src/libtommath/bn_mp_get_int.c
src/libtommath/bn_mp_grow.c
src/libtommath/bn_mp_init.c
src/libtommath/bn_mp_init_copy.c
src/libtommath/bn_mp_init_multi.c
src/libtommath/bn_mp_init_set.c
src/libtommath/bn_mp_init_set_int.c
src/libtommath/bn_mp_init_size.c
src/libtommath/bn_mp_invmod.c
src/libtommath/bn_mp_invmod_slow.c
src/libtommath/bn_mp_is_square.c
src/libtommath/bn_mp_jacobi.c
src/libtommath/bn_mp_karatsuba_mul.c
src/libtommath/bn_mp_karatsuba_sqr.c
src/libtommath/bn_mp_lcm.c
src/libtommath/bn_mp_lshd.c
src/libtommath/bn_mp_mod.c
src/libtommath/bn_mp_mod_2d.c
src/libtommath/bn_mp_mod_d.c
src/libtommath/bn_mp_montgomery_calc_normalization.c
src/libtommath/bn_mp_montgomery_reduce.c
src/libtommath/bn_mp_montgomery_setup.c
src/libtommath/bn_mp_mul.c
src/libtommath/bn_mp_mulmod.c
src/libtommath/bn_mp_mul_2.c
src/libtommath/bn_mp_mul_2d.c
src/libtommath/bn_mp_mul_d.c
src/libtommath/bn_mp_neg.c
src/libtommath/bn_mp_n_root.c
src/libtommath/bn_mp_or.c
src/libtommath/bn_mp_prime_fermat.c
src/libtommath/bn_mp_prime_is_divisible.c
src/libtommath/bn_mp_prime_is_prime.c
src/libtommath/bn_mp_prime_miller_rabin.c
src/libtommath/bn_mp_prime_next_prime.c
src/libtommath/bn_mp_prime_rabin_miller_trials.c
src/libtommath/bn_mp_prime_random_ex.c
src/libtommath/bn_mp_radix_size.c
src/libtommath/bn_mp_radix_smap.c
src/libtommath/bn_mp_rand.c
src/libtommath/bn_mp_read_radix.c
src/libtommath/bn_mp_read_signed_bin.c
src/libtommath/bn_mp_read_unsigned_bin.c
src/libtommath/bn_mp_reduce.c
src/libtommath/bn_mp_reduce_2k.c
src/libtommath/bn_mp_reduce_2k_l.c
src/libtommath/bn_mp_reduce_2k_setup.c
src/libtommath/bn_mp_reduce_2k_setup_l.c
src/libtommath/bn_mp_reduce_is_2k.c
src/libtommath/bn_mp_reduce_is_2k_l.c
src/libtommath/bn_mp_reduce_setup.c
src/libtommath/bn_mp_rshd.c
src/libtommath/bn_mp_set.c
src/libtommath/bn_mp_set_int.c
src/libtommath/bn_mp_shrink.c
src/libtommath/bn_mp_signed_bin_size.c
src/libtommath/bn_mp_sqr.c
src/libtommath/bn_mp_sqrmod.c
src/libtommath/bn_mp_sqrt.c
src/libtommath/bn_mp_sub.c
src/libtommath/bn_mp_submod.c
src/libtommath/bn_mp_sub_d.c
src/libtommath/bn_mp_toom_mul.c
src/libtommath/bn_mp_toom_sqr.c
src/libtommath/bn_mp_toradix.c
src/libtommath/bn_mp_toradix_n.c
src/libtommath/bn_mp_to_signed_bin.c
src/libtommath/bn_mp_to_signed_bin_n.c
src/libtommath/bn_mp_to_unsigned_bin.c
src/libtommath/bn_mp_to_unsigned_bin_n.c
src/libtommath/bn_mp_unsigned_bin_size.c
src/libtommath/bn_mp_xor.c
src/libtommath/bn_mp_zero.c
src/libtommath/bn_prime_tab.c
src/libtommath/bn_reverse.c
src/libtommath/bn_s_mp_add.c
src/libtommath/bn_s_mp_exptmod.c
src/libtommath/bn_s_mp_mul_digs.c
src/libtommath/bn_s_mp_mul_high_digs.c
src/libtommath/bn_s_mp_sqr.c
src/libtommath/bn_s_mp_sub.c
)
set(ZLIB_BZIP2_FILES
src/bzip2/blocksort.c
src/bzip2/bzlib.c
src/bzip2/compress.c
src/bzip2/crctable.c
src/bzip2/decompress.c
src/bzip2/huffman.c
src/bzip2/randtable.c
src/zlib/adler32.c
src/zlib/compress2.c
src/zlib/crc32.c
src/zlib/deflate.c
src/zlib/inffast.c
src/zlib/inflate.c
src/zlib/inftrees.c
src/zlib/trees.c
src/zlib/zutil.c
)
set(TEST_SRC_FILES
test/Test.cpp
)
add_definitions(-D_7ZIP_ST -DBZ_STRICT_ANSI)
if(WIN32)
if(MSVC)
add_definitions(-D_7ZIP_ST -DWIN32)
endif()
set(SRC_ADDITIONAL_FILES ${ZLIB_BZIP2_FILES} ${TOMCRYPT_FILES} ${TOMMATH_FILES})
set(LINK_LIBS wininet)
endif()
if(APPLE)
set(LINK_LIBS z bz2)
set(SRC_ADDITIONAL_FILES ${TOMCRYPT_FILES} ${TOMMATH_FILES})
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL Linux)
option(WITH_LIBTOMCRYPT "Use system LibTomCrypt library" OFF)
if(WITH_LIBTOMCRYPT)
set(LINK_LIBS z bz2 tomcrypt)
else()
set(LINK_LIBS z bz2)
set(SRC_ADDITIONAL_FILES ${TOMCRYPT_FILES} ${TOMMATH_FILES})
endif()
endif()
add_library(storm STATIC ${SRC_FILES} ${SRC_ADDITIONAL_FILES})
target_link_libraries(storm ${LINK_LIBS})
if(UNIX)
set_target_properties(storm PROPERTIES SOVERSION 0)
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)
endif()

View file

@ -0,0 +1,62 @@
StormLib history
================
Version 8.02
- Support for UNICODE encoding for on-disk files
- Optimized file deleting
Version 8.01
- SFileFindFirstFile and SFileFindNextFile no longer find files that have
patch file in the oldest MPQ in the patch chain
- Write support for MPQs version 4
Version 8.00
- Updated support for protected maps from Warcraft III
Version 7.11
- Support for MPQs v 3.0 (WOW-Cataclysm BETA)
- StormLib now deals properly with files that have MPQ_SECTOR_CHECKSUM missing,
but have sector checksum entry present in the sector offset table
Version 7.10
- Support for partial MPQs ("interface.MPQ.part")
- The only operation that is externally allowed to do with internal files
("(listfile)", "(attributes)" and "(signature)") is reading. Attempt to modify any of the file
fails and GetLastError returns ERROR_INTERNAL_FILE
- Fixed memory leak that has occured when writing more than one sector to the file at once
Version 7.01
- Support for adding files from memory
- Fixed improper validation of handles to MPQ file and MPQ archive
- Fixed bug where StormLib didn't save CRC32 of the file when added to archive
Version 7.00
- Properly deals with MPQs protected by w3xMaster
- Major rewrite
- Fixed support for (attributes)
- Added file verification
- Added MPQ signature verification
Version 6.22
- Properly deals with MPQs protected by w3xMaster
Version 6.21
- SFileRenameFile now properly re-crypts the file if necessary.
- SFileFindFirstFile correctly deals with deleted files
Version 6.20
- Fixed lots of bugs when processing files with same names but different locales
- Fixed bugs when repeately extracts the same file with MPQ_FILE_SINGLE_UNIT flag
- Added SFileFlushArchive
- Fixed issue opening AVI files renamed to MPQ using SFileCreateArchiveEx

View file

@ -0,0 +1,25 @@
After sector offset table
=========================
FileSize CmpSize DWORDs
00007588 000075A4 0x01
0000A9EA 000095EC 0x01
0000E51D 0000E20D 0x02
00015C00 00015C40 0x02
0001C578 000186C4 0x02
0002A9D4 0002A9EA 0x04
00037BAC 00037A42 0x06
0003C3C1 00034084 0x06
0003D224 0003B30F 0x06
00045105 0004195A 0x07
00045D9C 0003D87D 0x07
0004AAB8 0004860A 0x08
0004D18E 00048E0C 0x07
00056B4C 00056BDD 0x09
0005DC08 00059426 0x09
00061EC0 00057711 0x0A
0006CEC4 00062561 0x0B
000778EE 00066736 0x0C
000AD0CB 0007F32E 0x11
00327EAC 00303395 0x53

View file

@ -0,0 +1,318 @@
THE MOPAQ ARCHIVE FORMAT
v0.9 (Thursday, June 30, 2005)
by Justin Olbrantz(Quantam)
Distribution and reproduction of this specification are allowed without limitation, as long as it is not altered. Quoting
in other works is freely allowed, as long as the source and author of the quote is stated.
TABLE OF CONTENTS
1. Introduction to the MoPaQ Format
2. The MoPaQ Format
2.1 General Archive Layout
2.2 Archive Header
2.3 Block Table
2.4 Hash Table
2.5 File Data
2.6 Listfile
2.7 Extended Attributes
2.8 Weak (Old) Digital Signature
2.9 Strong (New) Digital Signature
3. Algorithm Source Code
3.1 Encryption/Decryption
3.2 Hashing
3.3 Conversion of FILETIME and time_t
1. INTRODUCTION TO THE MOPAQ FORMAT
The MoPaQ (or MPQ) format is an archive file format designed by Mike O'Brien (hence the name Mike O'brien PaCK) at Blizzard
Entertainment. The format has been used in all Blizzard games since (and including) Diablo. It is heavily optimized to be
a read-only game archive format, and excels at this role.
The Blizzard MoPaQ-reading functions are contained in the Storm module, which my be either statically or dynamically linked.
The Blizzard MoPaQ-writing functions are contained in the MPQAPI module, which is always statically linked.
2. THE MOPAQ FORMAT
All numbers in the MoPaQ format are in little endian. Data types are listed either as int (integer, the number of bits specified),
byte (8 bits), and char (bytes which contain ASCII characters). All sizes and offsets are in bytes, unless specified otherwise.
Structure members are listed in the following general form:
offset from the beginning of the structure: data type(array size) member name : member description
2.1 GENERAL ARCHIVE LAYOUT
- Archive Header
- File Data
- File Data - Special Files
- Hash Table
- Block Table
- Strong Digital signature
This is the usual archive format, and is not absolutely essential. Some archives have been observed placing the hash table
and file table after the archive header, and before the file data.
2.2 ARCHIVE HEADER
00h: char(4) Magic : Indicates that the file is a MoPaQ archive. Must be ASCII "MPQ" 1Ah.
04h: int32 HeaderSize : Size of the archive header. Should be 32.
08h: int32 ArchiveSize : Size of the whole archive, including the header. Does not include the strong digital signature, if present.
This size is used, among other things, for determining the region to hash in computing the digital signature.
0Ch: int16 Unknown : Unknown
0Eh: int8 SectorSizeShift : Power of two exponent specifying the number of 512-byte disk sectors in each logical sector
in the archive. The size of each logical sector the archive is 512 * 2^SectorSizeShift. Bugs in the Storm library dictate
that this should always be 3 (4096 byte sectors).
10h: int32 HashTableOffset : Offset to the beginning of the hash table, relative to the beginning of the archive.
14h: int32 BlockTableOffset : Offset to the beginning of the block table, relative to the beginning of the archive.
18h: int32 HashTableEntries : Number of entries in the hash table. Must be a power of two, and must be less than 2^16.
1Ch: int32 BlockTableEntries : Number of entries in the block table.
The archive header is the first structure in the archive, at archive offset 0, but the archive does not need to be at offset
0 of the containing file. The offset of the archive in the file is referred to here as ArchiveOffset. If the archive is not
at the beginning of the file, it must begin at a disk sector boundary (512 bytes). Early versions of Storm require that the
archive be at the end of the containing file (ArchiveOffset + ArchiveSize = file size), but this is not required in newer
versions (due to the strong digital signature not being considered a part of the archive).
2.3 BLOCK TABLE
The block table contains entries for each region in the archive. Regions may be either files or empty space, which may be
overwritten by new files (typically this space is from deleted file data). The block table is encrypted, using the hash
of "(block table)" as the key. Each entry is structured as follows:
00h: int32 BlockOffset : Offset of the beginning of the block, relative to the beginning of the archive. Meaningless if the block size is 0.
04h: int32 BlockSize : Size of the block in the archive.
08h: int32 FileSize : Size of the file data stored in the block. Only valid if the block is a file, otherwise meaningless, and should be 0. If the file is compressed, this is the size of the uncompressed file data.
0Ch: int32 Flags : Bit mask of the flags for the block. The following values are conclusively identified:
80000000h: Block is a file, and follows the file data format; otherwise, block is free space, and may be overwritten. If the block is not a file, all other flags should be cleared.
01000000h: File is stored as a single unit, rather than split into sectors.
00020000h: The file's encryption key is adjusted by the block offset and file size (explained in detail in the File Data section). File must be encrypted.
00010000h: File is encrypted.
00000200h: File is compressed. Mutually exclusive to file imploded.
00000100h: File is imploded. Mutually exclusive to file compressed.
2.4 HASH TABLE
Instead of storing file names, for quick access MoPaQs use a fixed, power of two-size hash table of files in the archive. A file is uniquely identified by its file path, its language, and its platform. The home entry for a file in the hash table is computed as a hash of the file path. In the event of a collision (the home entry is occupied by another file), progressive overflow is used, and the file is placed in the next available hash table entry. Searches for a desired file in the hash table proceed from the home entry for the file until either the file is found, the entire hash table is searched, or an empty hash table entry (FileBlockIndex of FFFFFFFFh) is encountered. The hash table is encrypted using the hash of "(hash table)" as the key. Each entry is structured as follows:
00h: int32 FilePathHashA : The hash of the file path, using method A.
04h: int32 FilePathHashB : The hash of the file path, using method B.
08h: int16 Language : The language of the file. This is a Windows LANGID data type, and uses the same values. 0 indicates the default language (American English), or that the file is language-neutral.
0Ah: int8 Platform : The platform the file is used for. 0 indicates the default platform. No other values have been observed.
0Ch: int32 FileBlockIndex : 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 (in other words, the file was deleted). Does not terminate searches for a given file.
2.5 FILE DATA
00h: int32(SectorsInFile + 1) SectorOffsetTable : Offsets to the start of each sector's data, relative to the beginning of the file data. Not present if this information is calculatable (see details below).
immediately following SectorOffsetTable: SectorData : Data of each sector in the file, packed end to end (see details below).
Normally, file data is split up into sectors, for simple streaming. All sectors, save for the last, will contain as many bytes of file data as specified in the archive header's SectorSizeShift; the last sector may be smaller than this, depending on the size of the file data. This sector size is the size of the raw file data; if the file is compressed, the compressed sector will be smaller or the same size as the uncompressed sector size. Individual sectors in a compressed file may be stored uncompressed; this occurs if and only if the sector could not be compressed by the algorithm used (if the compressed sector size was greater than or equal to the size of the raw data), and is indicated by the sector's compressed size in SectorOffsetTable being equal to the uncompressed size of the sector (which may be calculated from the FileSize).
If the sector is compressed (but not imploded), a bit mask byte of the compression algorithm(s) used to compress the sector is appended to the beginning of the compressed sector data. This additional byte counts towards the total size of the sector; if the size of the sector (including this byte) exceeds or matches the uncompressed size of the sector data, the sector will be stored uncompressed, and this byte omitted. Multiple compression algorithms may be used on the same sector; in this case, successive compression occurs in the order the algorithms are listed below, and decompression occurs in the opposite order. For implimentations of all of these algorithms, see StormLib.
40h: IMA ADPCM mono
80h: IMA ADPCM stereo
01h: Huffman encoded
02h: Deflated (see ZLib)
08h: Imploded (see PKWare Data Compression Library)
10h: BZip2 compressed (see BZip2)
If the file is stored as a single unit (indicated in the file's Flags), there is effectively only a single sector, which
contains the entire file.
If the file is encrypted, each sector (after compression and appendage of the compression type byte, if applicable)
is encrypted with the file's key. The base key for a file is determined by a hash of the file name stripped of the
directory (i.e. the key for a file named "directory\file" would be computed as the hash of "file"). If this key is
adjusted, as indicated in the file's Flags, the final key is calculated as ((base key + BlockOffset - ArchiveOffset)
XOR FileSize) (StormLib - an open-source implementation of the MoPaQ reading and writing functions,
by Ladislav Zezula - incorrectly uses an AND in place of the XOR). Each sector is encrypted using the key + the
0-based index of the sector in the file. The SectorOffsetTable, if present, is encrypted using the key - 1.
The SectorOffsetTable is omitted when the sizes and offsets of all sectors in the file are calculatable from the FileSize.
This can happen in several circumstances. If the file is not compressed/imploded, then the size and offset of all sectors
is known, based on the archive's SectorSizeShift. If the file is stored as a single unit compressed/imploded, then the
SectorOffsetTable is omitted, as the single file "sector" corresponds to BlockSize and FileSize, as mentioned previously.
Note that the SectorOffsetTable will always be present if the file is compressed/imploded and the file is not stored as
a single unit, even if there is only a single sector in the file (the size of the file is less than or equal to the
archive's sector size).
2.6 LISTFILE
The listfile is a very simple extension to the MoPaQ format that contains the file paths of (most) files in the archive.
The languages and platforms of the files are not stored in the listfile. The listfile is contained in the file "(listfile)",
and is simply a non-Unix-style text file with one file path on each line, lines terminated with the bytes 0Dh 0Ah. The file
"(listfile)" may not be listed in the listfile.
2.7 EXTENDED ATTRIBUTES
The extended attributes are optional file attributes for files in the block table. These attributes were added at times after
the MoPaQ format was already finalized, and it is not necessary for every archive to have all (or any) of the extended attributes.
If an archive contains a given attribute, there will be an instance of that attribute for every block in the block table, although
the attribute will be meaningless if the block is not a file. The order of the attributes for blocks correspond to the order of the
blocks in the block table, and are of the same number. The attributes are stored in parallel arrays in the "(attributes)" file,
in the archive. The attributes corresponding to this file need not be valid (and logically cannot be). Unlike all the other
structures in the MoPaQ format, entries in the extended attributes are NOT guaranteed to be aligned. Also note that in some
archives, malicious zeroing of the attributes has been observed, perhaps with the intent of breaking archive viewers. This
file is structured as follows:
00h: int32 Version : Specifies the extended attributes format version. For now, must be 100.
04h: int32 AttributesPresent : Bit mask of the extended attributes present in the archive:
00000001h: File CRC32s.
00000002h: File timestamps.
00000004h: File MD5s.
08h: int32(BlockTableEntries) CRC32s : CRC32s of the (uncompressed) file data for each block in the archive. Omitted if the
archive does not have CRC32s. immediately after CRC32s: FILETIME(BlockTableEntries) Timestamps : Timestamps for each block
in the archive. The format is that of the Windows FILETIME structure. Omitted if the archive does not have timestamps.
immediately after Timestamps: MD5(BlockTableEntries) MD5s : MD5s of the (uncompressed) file data for each block in the archive.
Omitted if the archive does not have MD5s.
2.8 WEAK DIGITAL SIGNATURE
The weak digital signature is a digital signature using Microsoft CryptoAPI. It is an implimentation of the RSASSA-PKCS1-v1_5
digital signature protocol, using the MD5 hashing algorithm and a 512-bit (weak) RSA key (for more information about this
protocol, see the RSA Labs PKCS1 specification). The public key and exponent are stored in a resource in Storm. The signature
is stored uncompressed, unencrypted in the file "(signature)" in the archive. The archive is hashed from the beginning of the
archive (ArchiveOffset in the containing file) to the end of the archive (the length indicated by ArchiveSize); the signature
file is added to the archive before signing, and the space occupied by the file is considered to be all binary 0s during
signing/verification. This file is structured as follows:
00h: int32 Unknown : Must be 0.
04h: int32 Unknown : must be 0.
08h: int512 Signature : The digital signature. Like all other numbers in the MoPaQ format, this is stored in little-endian order.
2.9 STRONG DIGITAL SIGNATURE
The strong digital signature uses a simple proprietary implementation of RSA signing, using the SHA-1 hashing algorithm and
a 2048-bit (strong) RSA key. The default public key and exponent are stored in Storm, but other keys may be used as well.
The strong digital signature is stored immediately after the archive, in the containing file; the entire archive (ArchiveSize
bytes, starting at ArchiveOffset in the containing file) is hashed as a single block. The signature has the following format:
00h: char(4) Magic : Indicates the presence of a digital signature. Must be "NGIS" ("SIGN" backwards).
04h: int2048 Signature : The digital signature, stored in little-endian format.
When the Signature field is decrypted with the public key and exponent, and the result stored in little-endian order, it is structured as follows:
00h: byte Padding : Must be 0Bh.
01h: byte(235) Padding : Must be BBh.
ECh: byte(20) SHA-1 : SHA-1 hash of the archive, in standard SHA-1 format.
3. ALGORITHM SOURCE CODE
3.1 ENCRYPTION/DECRYPTION
I believe this was derived at some point from code in StormLib. Assumes the long type to be 32 bits, and the machine to be little endian order.
unsigned long dwCryptTable[0x500];
void InitializeCryptTable()
{
unsigned long seed = 0x00100001;
unsigned long index1 = 0;
unsigned long index2 = 0;
int i;
for (index1 = 0; index1 < 0x100; index1++)
{
for (index2 = index1, i = 0; i < 5; i++, index2 += 0x100)
{
unsigned long temp1, temp2;
seed = (seed * 125 + 3) % 0x2AAAAB;
temp1 = (seed & 0xFFFF) << 0x10;
seed = (seed * 125 + 3) % 0x2AAAAB;
temp2 = (seed & 0xFFFF);
dwCryptTable[index2] = (temp1 | temp2);
}
}
}
void EncryptData(void *lpbyBuffer, unsigned long dwLength, unsigned long dwKey)
{
unsigned long *lpdwBuffer = (unsigned long *)lpbyBuffer;
unsigned long seed = 0xEEEEEEEE;
unsigned long ch;
assert(lpbyBuffer);
dwLength /= sizeof(unsigned long);
while(dwLength-- > 0)
{
seed += dwCryptTable[0x400 + (dwKey & 0xFF)];
ch = *lpdwBuffer ^ (dwKey + seed);
dwKey = ((~dwKey << 0x15) + 0x11111111) | (dwKey >> 0x0B);
seed = *lpdwBuffer + seed + (seed << 5) + 3;
*lpdwBuffer++ = ch;
}
}
void DecryptData(void *lpbyBuffer, unsigned long dwLength, unsigned long dwKey)
{
unsigned long *lpdwBuffer = (unsigned long *)lpbyBuffer;
unsigned long seed = 0xEEEEEEEE;
unsigned long ch;
assert(lpbyBuffer);
dwLength /= sizeof(unsigned long);
while(dwLength-- > 0)
{
seed += dwCryptTable[0x400 + (dwKey & 0xFF)];
ch = *lpdwBuffer ^ (dwKey + seed);
dwKey = ((~dwKey << 0x15) + 0x11111111) | (dwKey >> 0x0B);
seed = ch + seed + (seed << 5) + 3;
*lpdwBuffer++ = ch;
}
}
3.2 HASHING
Based on code from StormLib.
// Different types of hashes to make with HashString
#define MPQ_HASH_TABLE_OFFSET 0
#define MPQ_HASH_NAME_A 1
#define MPQ_HASH_NAME_B 2
#define MPQ_HASH_FILE_KEY 3
unsigned long HashString(const char *lpszString, unsigned long dwHashType)
{
unsigned long seed1 = 0x7FED7FED;
unsigned long seed2 = 0xEEEEEEEE;
int ch;
while (*lpszString != 0)
{
ch = toupper(*lpszString++);
seed1 = dwCryptTable[(dwHashType * 0xFF) + ch] ^ (seed1 + seed2);
seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
}
return seed1;
}
3.3 CONVERSION OF FILETIME AND time_t
#define EPOCH_OFFSET 116444736000000000ULL // Number of 100 ns units between 01/01/1601 and 01/01/1970
bool GetTimeFromFileTime(FILETIME &fileTime, time_t &time)
{
// The FILETIME represents a 64-bit integer: the number of 100 ns units since January 1, 1601
unsigned long long nTime = ((unsigned long long)fileTime.dwHighDateTime << 32) + fileTime.dwLowDateTime;
if (nTime < EPOCH_OFFSET)
return false;
nTime -= EPOCH_OFFSET; // Convert the time base from 01/01/1601 to 01/01/1970
nTime /= 10000000ULL; // Convert 100 ns to sec
time = (time_t)nTime;
// Test for overflow (FILETIME is 64 bits, time_t is 32 bits)
if ((nTime - (unsigned long long)time) > 0)
return false;
return true;
}
void GetFileTimeFromTime(time_t &time, FILETIME &fileTime)
{
unsigned long long nTime = (unsigned long long)time;
nTime *= 10000000ULL;
nTime += EPOCH_OFFSET;
fileTime.dwLowDateTime = (DWORD)nTime;
fileTime.dwHighDateTime = (DWORD)(nTime >> 32);
}

View file

@ -0,0 +1,433 @@
THE MOPAQ ARCHIVE FORMAT
v1.0 (Friday, September 1, 2006)
by Justin Olbrantz(Quantam)
Distribution and reproduction of this specification are allowed without limitation, as long as it is not altered. Quotation in other works is freely allowed, as long as the source and author of the quote are stated.
TABLE OF CONTENTS
1. Introduction to the MoPaQ Format
2. The MoPaQ Format
2.1 General Archive Layout
2.2 Archive Header
2.3 Block Table
2.4 Extended Block Table
2.5 Hash Table
2.6 File Data
2.7 Listfile
2.8 Extended Attributes
2.9 Weak (Old) Digital Signature
2.10 Strong (New) Digital Signature
3. Algorithm Source Code
3.1 Encryption/Decryption
3.2 Hashing and File Key Computation
3.3 Finding Files
3.4 Deleting Files
3.5 Conversion of FILETIME and time_t
3.6 Forming a 64-bit Large Archive Offset from 32-bit and 16-bit Components
4. Revision History
1. INTRODUCTION TO THE MOPAQ FORMAT
The MoPaQ (or MPQ) format is an archive file format designed by Mike O'Brien (hence the name Mike O'brien PaCK) at Blizzard Entertainment. The format has been used in all Blizzard games since (and including) Diablo. It is heavily optimized to be a read-only game archive format, and excels at this role.
The Blizzard MoPaQ-reading functions are contained in the Storm module, which my be either statically or dynamically linked. The Blizzard MoPaQ-writing functions are contained in the MPQAPI module, which is always statically linked.
StormLib - mentioned several times in this specification - is an open-source MoPaQ reading and writing library written by Ladislav Zezula (no affiliation with Blizzard Entertainment). While it's a bit dated, and does not support all of the newer MoPaQ features, it contains source code to the more exotic compression methods used by MoPaQ, such as the PKWare implode algorithm, MoPaQ's huffman compression algorithm, and the IMA ADPCM compression used by MoPaQ.
2. THE MOPAQ FORMAT
All numbers in the MoPaQ format are in little endian byte order; signed numbers use the two's complement system. Data types are listed either as int (integer, the number of bits specified), byte (8 bits), or char (bytes which contain ASCII characters). All sizes and offsets are in bytes, unless specified otherwise. Structure members are listed in the following general form:
offset from the beginning of the structure: data type(array size) member name : member description
2.1 GENERAL ARCHIVE LAYOUT
- Archive Header
- File Data
- File Data - Special Files
- Hash Table
- Block Table
- Extended Block Table
- Strong Digital signature
This is the usual archive format, but it is not mandatory. Some archives have been observed placing the hash table and file table after the archive header, and before the file data.
2.2 ARCHIVE HEADER
00h: char(4) Magic : Indicates that the file is a MoPaQ archive. Must be ASCII "MPQ" 1Ah.
04h: int32 HeaderSize : Size of the archive header.
08h: int32 ArchiveSize : Size of the whole archive, including the header. Does not include the strong digital signature, if present. This size is used, among other things, for determining the region to hash in computing the digital signature. This field is deprecated in the Burning Crusade MoPaQ format, and the size of the archive is calculated as the size from the beginning of the archive to the end of the hash table, block table, or extended block table (whichever is largest).
0Ch: int16 FormatVersion : MoPaQ format version. MPQAPI will not open archives where this is negative. Known versions:
0000h: Original format. HeaderSize should be 20h, and large archives are not supported.
0001h: Burning Crusade format. Header size should be 2Ch, and large archives are supported.
0Eh: int8 SectorSizeShift : Power of two exponent specifying the number of 512-byte disk sectors in each logical sector in the archive. The size of each logical sector in the archive is 512 * 2^SectorSizeShift. Bugs in the Storm library dictate that this should always be 3 (4096 byte sectors).
10h: int32 HashTableOffset : Offset to the beginning of the hash table, relative to the beginning of the archive.
14h: int32 BlockTableOffset : Offset to the beginning of the block table, relative to the beginning of the archive.
18h: int32 HashTableEntries : 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.
1Ch: int32 BlockTableEntries : Number of entries in the block table.
Fields only present in the Burning Crusade format and later:
20h: int64 ExtendedBlockTableOffset : Offset to the beginning of the extended block table, relative to the beginning of the archive.
28h: int16 HashTableOffsetHigh : High 16 bits of the hash table offset for large archives.
2Ah: int16 BlockTableOffsetHigh : High 16 bits of the block table offset for large archives.
The archive header is the first structure in the archive, at archive offset 0; however, the archive does not need to be at offset 0 of the containing file. The offset of the archive in the file is referred to here as ArchiveOffset. If the archive is not at the beginning of the file, it must begin at a disk sector boundary (512 bytes). Early versions of Storm require that the archive be at the end of the containing file (ArchiveOffset + ArchiveSize = file size), but this is not required in newer versions (due to the strong digital signature not being considered a part of the archive).
2.3 BLOCK TABLE
The block table contains entries for each region in the archive. Regions may be either files, empty space, which may be overwritten by new files (typically this space is from deleted file data), or unused block table entries. Empty space entries should have BlockOffset and BlockSize nonzero, and FileSize and Flags zero; unused block table entries should have BlockSize, FileSize, and Flags zero. The block table is encrypted, using the hash of "(block table)" as the key. Each entry is structured as follows:
00h: int32 BlockOffset : Offset of the beginning of the block, relative to the beginning of the archive.
04h: int32 BlockSize : Size of the block in the archive.
08h: int32 FileSize : Size of the file data stored in the block. Only valid if the block is a file; otherwise meaningless, and should be 0. If the file is compressed, this is the size of the uncompressed file data.
0Ch: int32 Flags : Bit mask of the flags for the block. The following values are conclusively identified:
80000000h: Block is a file, and follows the file data format; otherwise, block is free space or unused. If the block is not a file, all other flags should be cleared, and FileSize should be 0.
01000000h: File is stored as a single unit, rather than split into sectors.
00020000h: The file's encryption key is adjusted by the block offset and file size (explained in detail in the File Data section). File must be encrypted.
00010000h: File is encrypted.
00000200h: File is compressed. File cannot be imploded.
00000100h: File is imploded. File cannot be compressed.
2.4 EXTENDED BLOCK TABLE
The extended block table was added to support archives larger than 4 gigabytes (2^32 bytes). The table contains the upper bits of the archive offsets for each block in the block table. It is simply an array of int16s, which become bits 32-47 of the archive offsets for each block, with bits 48-63 being zero. Individual blocks in the archive are still limited to 4 gigabytes in size. This table is only present in Burning Crusade format archives that exceed 4 gigabytes size.
As of the Burning Crusade Friends and Family beta, this table is not encrypted.
2.5 HASH TABLE
Instead of storing file names, for quick access MoPaQs use a fixed, power of two-size hash table of files in the archive. A file is uniquely identified by its file path, its language, and its platform. The home entry for a file in the hash table is computed as a hash of the file path. In the event of a collision (the home entry is occupied by another file), progressive overflow is used, and the file is placed in the next available hash table entry. Searches for a desired file in the hash table proceed from the home entry for the file until either the file is found, the entire hash table is searched, or an empty hash table entry (FileBlockIndex of FFFFFFFFh) is encountered. The hash table is encrypted using the hash of "(hash table)" as the key. Each entry is structured as follows:
00h: int32 FilePathHashA : The hash of the file path, using method A.
04h: int32 FilePathHashB : The hash of the file path, using method B.
08h: int16 Language : The language of the file. This is a Windows LANGID data type, and uses the same values. 0 indicates the default language (American English), or that the file is language-neutral.
0Ah: int8 Platform : The platform the file is used for. 0 indicates the default platform. No other values have been observed.
0Ch: int32 FileBlockIndex : 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 (in other words, the file was deleted). Does not terminate searches for a given file.
2.6 FILE DATA
The data for each file is composed of the following structure:
00h: int32(SectorsInFile + 1) SectorOffsetTable : Offsets to the start of each sector, relative to the beginning of the file data. The last entry contains the file size, making it possible to easily calculate the size of any given sector. This table is not present if this information can be calculated (see details below).
immediately following SectorOffsetTable: SECTOR Sectors(SectorsInFile) : Data of each sector in the file, packed end to end (see details below).
Normally, file data is split up into sectors, for simple streaming. All sectors, save for the last, will contain as many bytes of file data as specified in the archive header's SectorSizeShift; the last sector may contain less than this, depending on the size of the entire file's data. If the file is compressed or imploded, the sector will be smaller or the same size as the file data it contains. Individual sectors in a compressed or imploded file may be stored uncompressed; this occurs if and only if the file data the sector contains could not be compressed by the algorithm(s) used (if the compressed sector size was greater than or equal to the size of the file data), and is indicated by the sector's size in SectorOffsetTable being equal to the size of the file data in the sector (which may be calculated from the FileSize).
The format of each sector depends on the kind of sector it is. Uncompressed sectors are simply the the raw file data contained in the sector. Imploded sectors are the raw compressed data following compression with the implode algorithm (these sectors can only be in imploded files). Compressed sectors (only found in compressed - not imploded - files) are compressed with one or more compression algorithms, and have the following structure:
00h: byte CompressionMask : Mask of the compression types applied to this sector. If multiple compression types are used, they are applied in the order listed below, and decompression is performed in the opposite order. This byte counts towards the total sector size, meaning that the sector will be stored uncompressed if the data cannot be compressed by at least two bytes; as well, this byte is encrypted with the sector data, if applicable. The following compression types are defined (for implementations of these algorithms, see StormLib):
40h: IMA ADPCM mono
80h: IMA ADPCM stereo
01h: Huffman encoded
02h: Deflated (see ZLib)
08h: Imploded (see PKWare Data Compression Library)
10h: BZip2 compressed (see BZip2)
01h: byte(SectorSize - 1) SectorData : The compressed data for the sector.
If the file is stored as a single unit (indicated in the file's Flags), there is effectively only a single sector, which contains the entire file data.
If the file is encrypted, each sector (after compression/implosion, if applicable) is encrypted with the file's key. The base key for a file is determined by a hash of the file name stripped of the directory (i.e. the key for a file named "directory\file" would be computed as the hash of "file"). If this key is adjusted, as indicated in the file's Flags, the final key is calculated as ((base key + BlockOffset - ArchiveOffset) XOR FileSize) (StormLib incorrectly uses an AND in place of the XOR). Each sector is encrypted using the key + the 0-based index of the sector in the file. The SectorOffsetTable, if present, is encrypted using the key - 1.
The SectorOffsetTable is omitted when the sizes and offsets of all sectors in the file are calculatable from the FileSize. This can happen in several circumstances. If the file is not compressed/imploded, then the size and offset of all sectors is known, based on the archive's SectorSizeShift. If the file is stored as a single unit compressed/imploded, then the SectorOffsetTable is omitted, as the single file "sector" corresponds to BlockSize and FileSize, as mentioned previously. However, the SectorOffsetTable will be present if the file is compressed/imploded and the file is not stored as a single unit, even if there is only a single sector in the file (the size of the file is less than or equal to the archive's sector size).
2.7 LISTFILE
The listfile is a very simple extension to the MoPaQ format that contains the file paths of (most) files in the archive. The languages and platforms of the files are not stored in the listfile. The listfile is contained in the file "(listfile)" (default language and platform), and is simply a text file with file paths separated by ';', 0Dh, 0Ah, or some combination of these. The file "(listfile)" may not be listed in the listfile.
2.8 EXTENDED ATTRIBUTES
The extended attributes are optional file attributes for files in the block table. These attributes were added at times after the MoPaQ format was already finalized, and it is not necessary for every archive to have all (or any) of the extended attributes. If an archive contains a given attribute, there will be an instance of that attribute for every block in the block table, although the attribute will be meaningless if the block is not a file. The order of the attributes for blocks correspond to the order of the blocks in the block table, and are of the same number. The attributes are stored in parallel arrays in the "(attributes)" file (default language and platform), in the archive. The attributes corresponding to this file need not be valid (and logically cannot be). Unlike all the other structures in the MoPaQ format, entries in the extended attributes are NOT guaranteed to be aligned. Also note that in some archives, malicious zeroing of the attributes has been observed, perhaps with the intent of breaking archive viewers. This file is structured as follows:
00h: int32 Version : Specifies the extended attributes format version. For now, must be 100.
04h: int32 AttributesPresent : Bit mask of the extended attributes present in the archive:
00000001h: File CRC32s.
00000002h: File timestamps.
00000004h: File MD5s.
08h: int32(BlockTableEntries) CRC32s : CRC32s of the (uncompressed) file data for each block in the archive. Omitted if the archive does not have CRC32s.
immediately after CRC32s: FILETIME(BlockTableEntries) Timestamps : Timestamps for each block in the archive. The format is that of the Windows FILETIME structure. Omitted if the archive does not have timestamps.
immediately after Timestamps: MD5(BlockTableEntries) MD5s : MD5s of the (uncompressed) file data for each block in the archive. Omitted if the archive does not have MD5s.
2.9 WEAK DIGITAL SIGNATURE
The weak digital signature is a digital signature using Microsoft CryptoAPI. It is an implimentation
of the RSASSA-PKCS1-v1_5 digital signature protocol, using the MD5 hashing algorithm and a 512-bit (weak)
RSA key (for more information about this protocol, see the RSA Labs PKCS1 specification). The public key
and exponent are stored in a resource in Storm, the private key is stored in a separate file, whose filename
is passed to MPQAPI (the private key is not stored in MPQAPI). The signature is stored uncompressed,
unencrypted in the file "(signature)" (default language and platform) in the archive. The archive
is hashed from the beginning of the archive (ArchiveOffset in the containing file) to the end of
the archive (the length indicated by ArchiveSize, or calculated in the Burning Crusade MoPaQ format);
the signature file is added to the archive before signing, and the space occupied by the file is considered
to be all binary 0s during signing/verification. This file is structured as follows:
00h: int32 Unknown : Must be 0.
04h: int32 Unknown : Must be 0.
08h: int512 Signature : The digital signature. Like all other numbers in the MoPaQ format, this is stored
in little-endian order. The structure of this, when decrypted, follows the RSASSA-PKCS1-v1_5 specification;
this format is rather icky to work with (I wrote a program to verify this signature using nothing but an MD5
function and huge integer functions; it wasn't pleasant), and best left to an encryption library such as Cryto++.
2.10 STRONG DIGITAL SIGNATURE
The strong digital signature uses a simple proprietary implementation of RSA signing, using the SHA-1 hashing algorithm and a 2048-bit (strong) RSA key. The default public key and exponent are stored in Storm, but other keys may be used as well. The strong digital signature is stored immediately after the archive, in the containing file; the entire archive (ArchiveSize bytes, starting at ArchiveOffset in the containing file) is hashed as a single block. The signature has the following format:
00h: char(4) Magic : Indicates the presence of a digital signature. Must be "NGIS" ("SIGN" backwards).
04h: int2048 Signature : The digital signature, stored in little-endian format.
When the Signature field is decrypted with the public key and exponent, and the resulting large integer is stored in little-endian order, it is structured as follows:
00h: byte Padding : Must be 0Bh.
01h: byte(235) Padding : Must be BBh.
ECh: byte(20) SHA-1 : SHA-1 hash of the archive, in standard SHA-1 byte order.
3. ALGORITHM SOURCE CODE
All of the sample code here assumes little endian machine byte order, that the short type is 16 bits, that the long type is 32 bits, and that the long long type is 64 bits. Adjustments must be made if these assumptions are not correct on a given platform. All code not credited otherwise was written by myself in the writing of this specification.
3.1 ENCRYPTION/DECRYPTION
Based on code from StormLib.
unsigned long dwCryptTable[0x500];
// The encryption and hashing functions use a number table in their procedures. This table must be initialized before the functions are called the first time.
void InitializeCryptTable()
{
unsigned long seed = 0x00100001;
unsigned long index1 = 0;
unsigned long index2 = 0;
int i;
for (index1 = 0; index1 < 0x100; index1++)
{
for (index2 = index1, i = 0; i < 5; i++, index2 += 0x100)
{
unsigned long temp1, temp2;
seed = (seed * 125 + 3) % 0x2AAAAB;
temp1 = (seed & 0xFFFF) << 0x10;
seed = (seed * 125 + 3) % 0x2AAAAB;
temp2 = (seed & 0xFFFF);
dwCryptTable[index2] = (temp1 | temp2);
}
}
}
void EncryptData(void *lpbyBuffer, unsigned long dwLength, unsigned long dwKey)
{
assert(lpbyBuffer);
unsigned long *lpdwBuffer = (unsigned long *)lpbyBuffer;
unsigned long seed = 0xEEEEEEEE;
unsigned long ch;
dwLength /= sizeof(unsigned long);
while(dwLength-- > 0)
{
seed += dwCryptTable[0x400 + (dwKey & 0xFF)];
ch = *lpdwBuffer ^ (dwKey + seed);
dwKey = ((~dwKey << 0x15) + 0x11111111) | (dwKey >> 0x0B);
seed = *lpdwBuffer + seed + (seed << 5) + 3;
*lpdwBuffer++ = ch;
}
}
void DecryptData(void *lpbyBuffer, unsigned long dwLength, unsigned long dwKey)
{
assert(lpbyBuffer);
unsigned long *lpdwBuffer = (unsigned long *)lpbyBuffer;
unsigned long seed = 0xEEEEEEEEL;
unsigned long ch;
dwLength /= sizeof(unsigned long);
while(dwLength-- > 0)
{
seed += dwCryptTable[0x400 + (dwKey & 0xFF)];
ch = *lpdwBuffer ^ (dwKey + seed);
dwKey = ((~dwKey << 0x15) + 0x11111111L) | (dwKey >> 0x0B);
seed = ch + seed + (seed << 5) + 3;
*lpdwBuffer++ = ch;
}
}
3.2 HASHING AND FILE KEY COMPUTATION
These functions may have been derived from StormLib code at some point in the very distant past. It was so long ago that I don't remember for certain.
// Different types of hashes to make with HashString
#define MPQ_HASH_TABLE_OFFSET 0
#define MPQ_HASH_NAME_A 1
#define MPQ_HASH_NAME_B 2
#define MPQ_HASH_FILE_KEY 3
// Based on code from StormLib.
unsigned long HashString(const char *lpszString, unsigned long dwHashType)
{
assert(lpszString);
assert(dwHashType <= MPQ_HASH_FILE_KEY);
unsigned long seed1 = 0x7FED7FEDL;
unsigned long seed2 = 0xEEEEEEEEL;
int ch;
while (*lpszString != 0)
{
ch = toupper(*lpszString++);
seed1 = dwCryptTable[(dwHashType * 0x100) + ch] ^ (seed1 + seed2);
seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;
}
return seed1;
}
#define BLOCK_OFFSET_ADJUSTED_KEY 0x00020000L
unsigned long ComputeFileKey(const char *lpszFilePath, const BlockTableEntry &blockEntry, unsigned long nArchiveOffset)
{
assert(lpszFilePath);
// Find the file name part of the path
const char *lpszFileName = strrchr(lpszFilePath, '\\');
if (lpszFileName)
lpszFileName++; // Skip the \
else
lpszFileName = lpszFilePath;
// Hash the name to get the base key
unsigned long nFileKey = HashString(lpszFileName, MPQ_HASH_FILE_KEY);
// Offset-adjust the key if necessary
if (blockEntry.Flags & BLOCK_OFFSET_ADJUSTED_KEY)
nFileKey = (nFileKey + blockEntry.BlockOffset) ^ blockEntry.FileSize;
return nFileKey;
}
3.3 FINDING FILES
#define MPQ_HASH_ENTRY_EMPTY 0xFFFFFFFFL
#define MPQ_HASH_ENTRY_DELETED 0xFFFFFFFEL
bool FindFileInHashTable(const HashTableEntry *lpHashTable, unsigned long nHashTableSize, const char *lpszFilePath, unsigned short nLang, unsigned char nPlatform, unsigned long &iFileHashEntry)
{
assert(lpHashTable);
assert(nHashTableSize);
assert(lpszFilePath);
// Find the home entry in the hash table for the file
unsigned long iInitEntry = HashString(lpszFilePath, MPQ_HASH_TABLE_OFFSET) & (nHashTableSize - 1);
// Is there anything there at all?
if (lpHashTable[iInitEntry].FileBlockIndex == MPQ_HASH_ENTRY_EMPTY)
return false;
// Compute the hashes to compare the hash table entry against
unsigned long nNameHashA = HashString(lpszFilePath, MPQ_HASH_NAME_A),
nNameHashB = HashString(lpszFilePath, MPQ_HASH_NAME_B),
iCurEntry = iInitEntry;
// Check each entry in the hash table till a termination point is reached
do
{
if (lpHashTable[iCurEntry].FileBlockIndex != MPQ_HASH_ENTRY_DELETED)
{
if (lpHashTable[iCurEntry].FilePathHashA == nNameHashA
&& lpHashTable[iCurEntry].FilePathHashB == nNameHashB
&& lpHashTable[iCurEntry].Language == nLang
&& lpHashTable[iCurEntry].Platform == nPlatform)
{
iFileHashEntry = iCurEntry;
return true;
}
}
iCurEntry = (iCurEntry + 1) & (nHashTableSize - 1);
} while (iCurEntry != iInitEntry && lpHashTable[iCurEntry].FileBlockIndex != MPQ_HASH_ENTRY_EMPTY);
return false;
}
3.4 DELETING FILES
bool DeleteFile(HashTableEntry *lpHashTable, unsigned long nHashTableSize, BlockTableEntry *lpBlockTable, const char *lpszFilePath, unsigned short nLang, unsigned char nPlatform)
{
assert(lpHashTable);
assert(nHashTableSize);
assert(lpBlockTable);
// Find the file in the hash table
unsigned long iFileHashEntry;
if (!FindFileInHashTable(lpHashTable, nHashTableSize, lpszFilePath, nLang, nPlatform, iFileHashEntry))
return false;
// Get the block table index before we nuke the hash table entry
unsigned long iFileBlockEntry = lpHashTable[iFileHashEntry].FileBlockIndex;
// Delete the file's entry in the hash table
memset(&lpHashTable[iFileHashEntry], 0xFF, sizeof(HashTableEntry));
// If the next entry is empty, mark this one as empty; otherwise, mark this as deleted.
if (lpHashTable[(iFileHashEntry + 1) & (nHashTableSize - 1)].FileBlockIndex == MPQ_HASH_ENTRY_EMPTY)
lpHashTable[iFileHashEntry].FileBlockIndex = MPQ_HASH_ENTRY_EMPTY;
else
lpHashTable[iFileHashEntry].FileBlockIndex = MPQ_HASH_ENTRY_DELETED;
// If the block occupies space, mark the block as free space; otherwise, clear the block table entry.
if (lpBlockTable[iFileBlockEntry].BlockSize > 0)
{
lpBlockTable[iFileBlockEntry].FileSize = 0;
lpBlockTable[iFileBlockEntry].Flags = 0;
}
else
memset(&lpBlockTable[iFileBlockEntry], 0, sizeof(BlockTableEntry);
return true;
}
3.5 CONVERSION OF FILETIME AND time_t
This code assumes that the base ("zero") date for time_t is 01/01/1970. This is true on Windows, Unix System V systems, and Mac OS X. It is unknown whether this is true on all other platforms. You'll need to research this yourself, if you plan on porting it somewhere else.
#define EPOCH_OFFSET 116444736000000000ULL // Number of 100 ns units between 01/01/1601 and 01/01/1970
bool GetTimeFromFileTime(const FILETIME &fileTime, time_t &time)
{
// The FILETIME represents a 64-bit integer: the number of 100 ns units since January 1, 1601
unsigned long long nTime = ((unsigned long long)fileTime.dwHighDateTime << 32) + fileTime.dwLowDateTime;
if (nTime < EPOCH_OFFSET)
return false;
nTime -= EPOCH_OFFSET; // Convert the time base from 01/01/1601 to 01/01/1970
nTime /= 10000000ULL; // Convert 100 ns to sec
time = (time_t)nTime;
// Test for overflow (FILETIME is 64 bits, time_t is 32 bits)
if ((nTime - (unsigned long long)time) > 0)
return false;
return true;
}
void GetFileTimeFromTime(const time_t &time, FILETIME &fileTime)
{
unsigned long long nTime = (unsigned long long)time;
nTime *= 10000000ULL;
nTime += EPOCH_OFFSET;
fileTime.dwLowDateTime = (DWORD)nTime;
fileTime.dwHighDateTime = (DWORD)(nTime >> 32);
}
3.6 FORMING A 64-BIT LARGE ARCHIVE OFFSET FROM 32-BIT AND 16-BIT COMPONENTS
unsigned long long MakeLargeArchiveOffset(unsigned long nOffsetLow, unsigned short nOffsetHigh)
{
return ((unsigned long long)nOffsetHigh << 32) + (unsigned long long)nOffsetLow;
}
4. REVISION HISTORY
1.0
- Updated to include most of the changes found in the Burning Crusade Friends and Family beta
0.91.
- Updated several structure member descriptions
- Listed the full set of characters that can separate list file entries
- Noted that (attributes), (listfile), and (signature) use the default language and platform codes
- Redid part of the file data specs to clarify the format of sectors
- Enhanced descriptions of the different kinds of block table entries
- Added ComputeFileKey, FindFileInHashTable, and DeleteFile source

View file

@ -0,0 +1 @@
UCMXF6EJY352EFH4XFRXCFH2XC9MQRZK

View file

@ -0,0 +1 @@
MMKVHY48RP7WXP4GHYBQ7SL9J9UNPHBP

View file

@ -0,0 +1 @@
8MXLWHQ7VGGLTZ9MQZQSFDCLJYET3CPP

View file

@ -0,0 +1 @@
EJ2R5TM6XFE2GUNG5QDGHKQ9UAKPWZSZ

View file

@ -0,0 +1 @@
PBGFBE42Z6LNK65UGJQ3WZVMCLP4HQQT

View file

@ -0,0 +1 @@
X7SEJJS9TSGCW5P28EBSC47AJPEY8VU2

View file

@ -0,0 +1 @@
5KVBQA8VYE6XRY3DLGC5ZDE4XS4P7YA2

View file

@ -0,0 +1 @@
478JD2K56EVNVVY4XX8TDWYT5B8KB254

View file

@ -0,0 +1 @@
8TS4VNFQRZTN6YWHE9CHVDH9NVWD474A

View file

@ -0,0 +1 @@
LJ52Z32DF4LZ4ZJJXVKK3AZQA6GABLJB

View file

@ -0,0 +1 @@
K6BDHY2ECUE2545YKNLBJPVYWHE7XYAG

View file

@ -0,0 +1 @@
6VWCQTN8V3ZZMRUCZXV8A8CGUX2TAA8H

View file

@ -0,0 +1,49 @@
{
"config":{
"product": "D3",
"install_progress_percent": 70.0,
"install_progress_info": [8000000000.0, 0.0, 0.0],
"download_progress_info": [10000000.0, 500000000.0, 500000000.0],
"install_progress_speed": 5000000.0,
"tome_download_progress_percent": 0.0,
"updater_product": "d3_patch",
"expansion_level": 0,
"ptr": false,
"beta": false,
"update_method": "patch on demand",
"supports_multibox": false,
"data_dir": "Data_D3/PC/MPQs/",
"patch_url": "http://ruRU.patch.battle.net:1119/patch",
"update_regex": "(?P<prefix>d3-update-(?P<dataset>\\w+))-(?P<build>\\d+)\\.mpq$",
"update_identifier": "d3-update-",
"torrent_file_path": "Diablo III.tfil",
"manifest_file_path": "Diablo III.mfil",
"priority_file_path": "Diablo III.pfil",
"priority_file_layout": "Retail",
"binary_version_path": "Diablo III.exe",
"binary_launch_path": "Diablo III.exe",
"display_locales":["ruRU"],
"supported_locales" : ["enUS", "esMX", "ptBR", "enGB", "deDE", "esES", "frFR", "itIT", "plPL", "enSG", "ptPT", "ruRU", "koKR", "zhTW", "zhCN"],
"launch_arguments":["-launch","-uid","diablo3_ruru"],
"form": {
"eula": {
"eula":false
},
"game_dir": {
"default": "Program Files",
"dirname": "Diablo III",
"required_space": 15032385536
},
"language": {
"default":["ruRU"],
"list":["ruRU"]
},
"authentication_key": {
"url": [
"http://ruru.nydus.battle.net/D3/ruRU/setup/mediakey",
"http://dist.blizzard.com/mediakey/d3-authenticationcode-ruRU.txt"
]
}
}
}
}

View file

@ -0,0 +1,14 @@
http://dist.blizzard.com/mediakey/d3-authenticationcode-deDE.txt
http://dist.blizzard.com/mediakey/d3-authenticationcode-enGB.txt
http://dist.blizzard.com/mediakey/d3-authenticationcode-enSG.txt
http://dist.blizzard.com/mediakey/d3-authenticationcode-enUS.txt
http://dist.blizzard.com/mediakey/d3-authenticationcode-esES.txt
http://dist.blizzard.com/mediakey/d3-authenticationcode-esMX.txt
http://dist.blizzard.com/mediakey/d3-authenticationcode-frFR.txt
http://dist.blizzard.com/mediakey/d3-authenticationcode-itIT.txt
http://dist.blizzard.com/mediakey/d3-authenticationcode-koKR.txt
http://dist.blizzard.com/mediakey/d3-authenticationcode-plPL.txt
http://dist.blizzard.com/mediakey/d3-authenticationcode-ptBR.txt
http://dist.blizzard.com/mediakey/d3-authenticationcode-ruRU.txt <====
http://dist.blizzard.com/mediakey/d3-authenticationcode-zhTW.txt
http://dist.blizzard.com/mediakey/d3-authenticationcode-zhCN.txt <====

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,189 @@
/*****************************************************************************/
/* FileStream.h Copyright (c) Ladislav Zezula 2012 */
/*---------------------------------------------------------------------------*/
/* Description: Definitions for FileStream object */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 14.04.12 1.00 Lad The first version of FileStream.h */
/*****************************************************************************/
#ifndef __FILESTREAM_H__
#define __FILESTREAM_H__
//-----------------------------------------------------------------------------
// Function prototypes
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
);
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
);
typedef bool (*STREAM_GETPOS)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG & ByteOffset // Pointer to store current file position
);
typedef bool (*STREAM_GETSIZE)(
struct TFileStream * pStream, // Pointer to an open stream
ULONGLONG & FileSize // 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 void (*STREAM_CLOSE)(
struct TFileStream * pStream
);
//-----------------------------------------------------------------------------
// Local structures - part file structure
typedef struct _PART_FILE_HEADER
{
DWORD PartialVersion; // Always set to 2
char GameBuildNumber[0x20]; // Minimum build number of the game that can use this MPQ
DWORD Flags; // Flags (details unknown)
DWORD FileSizeLo; // Low 32 bits of the contained file size
DWORD FileSizeHi; // High 32 bits of the contained file size
DWORD BlockSize; // Size of one file block, in bytes
} PART_FILE_HEADER, *PPART_FILE_HEADER;
// Structure describing the block-to-file map entry
typedef struct _PART_FILE_MAP_ENTRY
{
DWORD Flags; // 3 = the block is present in the file
DWORD BlockOffsLo; // Low 32 bits of the block position in the file
DWORD BlockOffsHi; // High 32 bits of the block position in the file
DWORD LargeValueLo; // 64-bit value, meaning is unknown
DWORD LargeValueHi;
} PART_FILE_MAP_ENTRY, *PPART_FILE_MAP_ENTRY;
//-----------------------------------------------------------------------------
// Local structures
union TBaseData
{
struct
{
ULONGLONG FileSize; // Size of the file
ULONGLONG FilePos; // Current file position
ULONGLONG FileTime; // Date/time of last modification of the file
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
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
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_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_CLOSE StreamClose; // Pointer to function closing the stream
// Stream provider data members
TCHAR szFileName[MAX_PATH]; // File name
DWORD dwFlags; // Stream flags
// Base provider functions
STREAM_READ BaseRead;
STREAM_WRITE BaseWrite;
STREAM_GETPOS BaseGetPos; // Pointer to function that returns current file position
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_CLOSE BaseClose; // Pointer to function closing the stream
// Base provider data members
TBaseData Base; // Base provider data
// Followed by stream provider data, with variable length
};
//-----------------------------------------------------------------------------
// Structure for linear stream
struct TLinearStream : 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
};
//-----------------------------------------------------------------------------
// Structure for encrypted stream
#define MPQE_CHUNK_SIZE 0x40 // Size of one chunk to be decrypted
struct TEncryptedStream : public TFileStream
{
BYTE Key[MPQE_CHUNK_SIZE]; // File key
};
#endif // __FILESTREAM_H__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,144 @@
/*****************************************************************************/
/* SBaseDumpData.cpp Copyright (c) Ladislav Zezula 2011 */
/*---------------------------------------------------------------------------*/
/* Description : */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 26.01.11 1.00 Lad The first version of SBaseDumpData.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
#ifdef __STORMLIB_DUMP_DATA__
void DumpMpqHeader(TMPQHeader * pHeader)
{
printf("== MPQ Header =================================\n");
printf("DWORD dwID = %08X\n", pHeader->dwID);
printf("DWORD dwHeaderSize = %08X\n", pHeader->dwHeaderSize);
printf("DWORD dwArchiveSize = %08X\n", pHeader->dwArchiveSize);
printf("USHORT wFormatVersion = %04X\n", pHeader->wFormatVersion);
printf("USHORT wSectorSize = %04X\n", pHeader->wSectorSize);
printf("DWORD dwHashTablePos = %08X\n", pHeader->dwHashTablePos);
printf("DWORD dwBlockTablePos = %08X\n", pHeader->dwBlockTablePos);
printf("DWORD dwHashTableSize = %08X\n", pHeader->dwHashTableSize);
printf("DWORD dwBlockTableSize = %08X\n", pHeader->dwBlockTableSize);
printf("ULONGLONG HiBlockTablePos64 = %016llX\n", pHeader->HiBlockTablePos64);
printf("USHORT wHashTablePosHi = %04X\n", pHeader->wHashTablePosHi);
printf("USHORT wBlockTablePosHi = %04X\n", pHeader->wBlockTablePosHi);
printf("ULONGLONG ArchiveSize64 = %016llX\n", pHeader->ArchiveSize64);
printf("ULONGLONG BetTablePos64 = %016llX\n", pHeader->BetTablePos64);
printf("ULONGLONG HetTablePos64 = %016llX\n", pHeader->HetTablePos64);
printf("ULONGLONG HashTableSize64 = %016llX\n", pHeader->HashTableSize64);
printf("ULONGLONG BlockTableSize64 = %016llX\n", pHeader->BlockTableSize64);
printf("ULONGLONG HiBlockTableSize64 = %016llX\n", pHeader->HiBlockTableSize64);
printf("ULONGLONG HetTableSize64 = %016llX\n", pHeader->HetTableSize64);
printf("ULONGLONG BetTableSize64 = %016llX\n", pHeader->BetTableSize64);
printf("DWORD dwRawChunkSize = %08X\n", pHeader->dwRawChunkSize);
printf("-----------------------------------------------\n\n");
}
void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable)
{
DWORD i;
if(pHetTable == NULL || pBetTable == NULL)
return;
printf("== HET Header =================================\n");
printf("ULONGLONG AndMask64 = %016llX\n", pHetTable->AndMask64);
printf("ULONGLONG OrMask64 = %016llX\n", pHetTable->OrMask64);
printf("DWORD dwIndexSizeTotal = %08X\n", pHetTable->dwIndexSizeTotal);
printf("DWORD dwIndexSizeExtra = %08X\n", pHetTable->dwIndexSizeExtra);
printf("DWORD dwIndexSize = %08X\n", pHetTable->dwIndexSize);
printf("DWORD dwMaxFileCount = %08X\n", pHetTable->dwMaxFileCount);
printf("DWORD dwHashTableSize = %08X\n", pHetTable->dwHashTableSize);
printf("DWORD dwHashBitSize = %08X\n", pHetTable->dwHashBitSize);
printf("-----------------------------------------------\n\n");
printf("== BET Header =================================\n");
printf("DWORD dwTableEntrySize = %08X\n", pBetTable->dwTableEntrySize);
printf("DWORD dwBitIndex_FilePos = %08X\n", pBetTable->dwBitIndex_FilePos);
printf("DWORD dwBitIndex_FileSize = %08X\n", pBetTable->dwBitIndex_FileSize);
printf("DWORD dwBitIndex_CmpSize = %08X\n", pBetTable->dwBitIndex_CmpSize);
printf("DWORD dwBitIndex_FlagIndex = %08X\n", pBetTable->dwBitIndex_FlagIndex);
printf("DWORD dwBitIndex_Unknown = %08X\n", pBetTable->dwBitIndex_Unknown);
printf("DWORD dwBitCount_FilePos = %08X\n", pBetTable->dwBitCount_FilePos);
printf("DWORD dwBitCount_FileSize = %08X\n", pBetTable->dwBitCount_FileSize);
printf("DWORD dwBitCount_CmpSize = %08X\n", pBetTable->dwBitCount_CmpSize);
printf("DWORD dwBitCount_FlagIndex = %08X\n", pBetTable->dwBitCount_FlagIndex);
printf("DWORD dwBitCount_Unknown = %08X\n", pBetTable->dwBitCount_Unknown);
printf("DWORD dwBetHashSizeTotal = %08X\n", pBetTable->dwBetHashSizeTotal);
printf("DWORD dwBetHashSizeExtra = %08X\n", pBetTable->dwBetHashSizeExtra);
printf("DWORD dwBetHashSize = %08X\n", pBetTable->dwBetHashSize);
printf("DWORD dwMaxFileCount = %08X\n", pBetTable->dwMaxFileCount);
printf("DWORD dwFlagCount = %08X\n", pBetTable->dwFlagCount);
printf("-----------------------------------------------\n\n");
printf("== HET & Bet Table ======================================================================\n\n");
printf("HetIdx HetHash BetIdx BetHash ByteOffset FileSize CmpSize FlgIdx Flags \n");
printf("------ ------- ------ ---------------- ---------------- -------- -------- ------ --------\n");
for(i = 0; i < pHetTable->dwHashTableSize; i++)
{
ULONGLONG ByteOffset = 0;
ULONGLONG BetHash = 0;
DWORD dwFileSize = 0;
DWORD dwCmpSize = 0;
DWORD dwFlagIndex = 0;
DWORD dwFlags = 0;
DWORD dwBetIndex = 0;
pHetTable->pBetIndexes->GetBits(i * pHetTable->dwIndexSizeTotal,
pHetTable->dwIndexSize,
&dwBetIndex,
4);
if(dwBetIndex < pHetTable->dwMaxFileCount)
{
DWORD dwEntryIndex = pBetTable->dwTableEntrySize * dwBetIndex;
pBetTable->pBetHashes->GetBits(dwBetIndex * pBetTable->dwBetHashSizeTotal,
pBetTable->dwBetHashSize,
&BetHash,
8);
pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_FilePos,
pBetTable->dwBitCount_FilePos,
&ByteOffset,
8);
pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_FileSize,
pBetTable->dwBitCount_FileSize,
&dwFileSize,
4);
pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_CmpSize,
pBetTable->dwBitCount_CmpSize,
&dwCmpSize,
4);
pBetTable->pFileTable->GetBits(dwEntryIndex + pBetTable->dwBitIndex_FlagIndex,
pBetTable->dwBitCount_FlagIndex,
&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);
}
printf("-----------------------------------------------------------------------------------------\n");
}
#endif // __STORMLIB_DUMP_DATA__

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,477 @@
/*****************************************************************************/
/* SAttrFile.cpp Copyright (c) Ladislav Zezula 2007 */
/*---------------------------------------------------------------------------*/
/* Description: */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 12.06.04 1.00 Lad The first version of SAttrFile.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
//-----------------------------------------------------------------------------
// Local structures
typedef struct _MPQ_ATTRIBUTES_HEADER
{
DWORD dwVersion; // Version of the (attributes) file. Must be 100 (0x64)
DWORD dwFlags; // See MPQ_ATTRIBUTE_XXXX
// Followed by an array of CRC32
// Followed by an array of file times
// Followed by an array of MD5
// Followed by an array of patch bits
} MPQ_ATTRIBUTES_HEADER, *PMPQ_ATTRIBUTES_HEADER;
//-----------------------------------------------------------------------------
// Public functions (internal use by StormLib)
int SAttrLoadAttributes(TMPQArchive * ha)
{
MPQ_ATTRIBUTES_HEADER AttrHeader;
TMPQFile * hf;
HANDLE hFile = NULL;
DWORD dwBlockTableSize = ha->pHeader->dwBlockTableSize;
DWORD dwArraySize;
DWORD dwBytesRead;
DWORD i;
int nError = ERROR_SUCCESS;
// File table must be initialized
assert(ha->pFileTable != NULL);
// 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))
{
// Remember the flags for (attributes)
hf = (TMPQFile *)hFile;
ha->dwFileFlags2 = hf->pFileEntry->dwFlags;
// 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;
// Verify the header of the (attributes) file
if(nError == ERROR_SUCCESS)
{
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;
}
// 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
SFileCloseFile(hFile);
}
return nError;
}
int SAttrFileSaveToMpq(TMPQArchive * ha)
{
MPQ_ATTRIBUTES_HEADER AttrHeader;
TFileEntry * pFileEntry;
TMPQFile * hf = NULL;
DWORD dwFinalBlockTableSize = ha->dwFileTableSize;
DWORD dwFileSize = 0;
DWORD dwToWrite;
DWORD i;
int nError = ERROR_SUCCESS;
// Now we have to check if we need patch bits in the (attributes)
if(nError == ERROR_SUCCESS)
{
for(i = 0; i < ha->dwFileTableSize; i++)
{
if(ha->pFileTable[i].dwFlags & MPQ_FILE_PATCH_FILE)
{
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;
}
nError = SFileAddFile_Write(hf, pbBitArray, dwByteSize, MPQ_COMPRESSION_ZLIB);
STORM_FREE(pbBitArray);
}
}
// Finalize the file in the archive
if(hf != NULL)
{
SFileAddFile_Finish(hf);
}
if(nError == ERROR_SUCCESS)
ha->dwFlags &= ~MPQ_FLAG_INV_ATTRIBUTES;
return nError;
}
//-----------------------------------------------------------------------------
// Public functions
DWORD WINAPI SFileGetAttributes(HANDLE hMpq)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
// Verify the parameters
if(!IsValidMpqHandle(ha))
{
SetLastError(ERROR_INVALID_PARAMETER);
return SFILE_INVALID_ATTRIBUTES;
}
return ha->dwAttrFlags;
}
bool WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
// Verify the parameters
if(!IsValidMpqHandle(ha))
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
// Not allowed when the archive is read-only
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
{
SetLastError(ERROR_ACCESS_DENIED);
return false;
}
// Set the attributes
InvalidateInternalFiles(ha);
ha->dwAttrFlags = (dwFlags & MPQ_ATTRIBUTE_ALL);
return true;
}
bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName)
{
hash_state md5_state;
TMPQArchive * ha = (TMPQArchive *)hMpq;
TMPQFile * hf;
BYTE Buffer[0x1000];
HANDLE hFile = NULL;
DWORD dwTotalBytes = 0;
DWORD dwBytesRead;
DWORD dwCrc32;
// Verify the parameters
if(!IsValidMpqHandle(ha))
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
// Not allowed when the archive is read-only
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
{
SetLastError(ERROR_ACCESS_DENIED);
return false;
}
// Attempt to open the file
if(!SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_FROM_MPQ, &hFile))
return false;
// Get the file size
hf = (TMPQFile *)hFile;
SFileGetFileInfo(hFile, SFILE_INFO_FILE_SIZE, &dwTotalBytes, sizeof(DWORD));
// Initialize the CRC32 and MD5 contexts
md5_init(&md5_state);
dwCrc32 = crc32(0, Z_NULL, 0);
// Go through entire file and calculate both CRC32 and MD5
while(dwTotalBytes != 0)
{
// Read data from file
SFileReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead, NULL);
if(dwBytesRead == 0)
break;
// Update CRC32 and MD5
dwCrc32 = crc32(dwCrc32, Buffer, dwBytesRead);
md5_process(&md5_state, Buffer, dwBytesRead);
// Decrement the total size
dwTotalBytes -= dwBytesRead;
}
// Update both CRC32 and MD5
hf->pFileEntry->dwCrc32 = dwCrc32;
md5_done(&md5_state, hf->pFileEntry->md5);
// Remember that we need to save the MPQ tables
InvalidateInternalFiles(ha);
SFileCloseFile(hFile);
return true;
}

View file

@ -0,0 +1,765 @@
/*****************************************************************************/
/* SFileCompactArchive.cpp Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* Archive compacting function */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 14.04.03 1.00 Lad Splitted from SFileCreateArchiveEx.cpp */
/* 19.11.03 1.01 Dan Big endian handling */
/*****************************************************************************/
#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)
{
TFileEntry * pFileTableEnd;
TFileEntry * pFileEntry;
DWORD dwBlockIndex = 0;
int nError = ERROR_SUCCESS;
// Add the listfile to the MPQ
if(nError == ERROR_SUCCESS && szListFile != NULL)
{
// Notify the user
if(CompactCB != NULL)
CompactCB(pvUserData, CCB_CHECKING_FILES, CompactBytesProcessed, CompactTotalBytes);
nError = SFileAddListFile((HANDLE)ha, szListFile);
}
// Verify the file table
if(nError == ERROR_SUCCESS)
{
pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++, dwBlockIndex++)
{
if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
{
if(pFileEntry->szFileName != NULL && !IsPseudoFileName(pFileEntry->szFileName, NULL))
{
DWORD dwFileKey = 0;
// Resolve the file key. Use plain file name for it
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
{
dwFileKey = DecryptFileKey(pFileEntry->szFileName,
pFileEntry->ByteOffset,
pFileEntry->dwFileSize,
pFileEntry->dwFlags);
}
// Give the key to the caller
if(pFileKeys != NULL)
pFileKeys[dwBlockIndex] = dwFileKey;
}
else
{
nError = ERROR_CAN_NOT_COMPLETE;
break;
}
}
}
}
return nError;
}
static int CopyNonMpqData(
TFileStream * pSrcStream,
TFileStream * pTrgStream,
ULONGLONG & ByteOffset,
ULONGLONG & ByteCount)
{
ULONGLONG DataSize = ByteCount;
DWORD dwToRead;
char DataBuffer[0x1000];
int nError = ERROR_SUCCESS;
// Copy the data
while(DataSize > 0)
{
// Get the proper size of data
dwToRead = sizeof(DataBuffer);
if(DataSize < dwToRead)
dwToRead = (DWORD)DataSize;
// Read from the source stream
if(!FileStream_Read(pSrcStream, &ByteOffset, DataBuffer, dwToRead))
{
nError = GetLastError();
break;
}
// Write to the target stream
if(!FileStream_Write(pTrgStream, NULL, DataBuffer, dwToRead))
{
nError = GetLastError();
break;
}
// Update the progress
if(CompactCB != NULL)
{
CompactBytesProcessed += dwToRead;
CompactCB(pvUserData, CCB_COPYING_NON_MPQ_DATA, CompactBytesProcessed, CompactTotalBytes);
}
// Decrement the number of data to be copied
ByteOffset += dwToRead;
DataSize -= dwToRead;
}
return ERROR_SUCCESS;
}
// Copies all file sectors into another archive.
static int CopyMpqFileSectors(
TMPQArchive * ha,
TMPQFile * hf,
TFileStream * pNewStream)
{
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
DWORD dwFileKey2 = 0; // File key used for encryption
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))
{
dwFileKey2 = dwFileKey1 = hf->dwFileKey;
if(pFileEntry->dwFlags & MPQ_FILE_FIX_KEY)
{
dwFileKey2 = (dwFileKey1 ^ pFileEntry->dwFileSize) - (DWORD)pFileEntry->ByteOffset;
dwFileKey2 = (dwFileKey2 + (DWORD)MpqFilePos) ^ pFileEntry->dwFileSize;
}
}
// If we have to save patch header, do it
if(nError == ERROR_SUCCESS && hf->pPatchInfo != NULL)
{
BSWAP_ARRAY32_UNSIGNED(hf->pPatchInfo, sizeof(DWORD) * 3);
if(!FileStream_Write(pNewStream, NULL, hf->pPatchInfo, hf->pPatchInfo->dwLength))
nError = GetLastError();
// Save the size of the patch info
dwPatchSize = hf->pPatchInfo->dwLength;
}
// 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 dwSectorOffsLen = hf->SectorOffsets[0];
assert((pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) == 0);
assert(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED);
if(SectorOffsetsCopy == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
// Encrypt the secondary sector offset table and write it to the target file
if(nError == ERROR_SUCCESS)
{
memcpy(SectorOffsetsCopy, hf->SectorOffsets, dwSectorOffsLen);
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
EncryptMpqBlock(SectorOffsetsCopy, dwSectorOffsLen, dwFileKey2 - 1);
BSWAP_ARRAY32_UNSIGNED(SectorOffsetsCopy, dwSectorOffsLen);
if(!FileStream_Write(pNewStream, NULL, SectorOffsetsCopy, dwSectorOffsLen))
nError = GetLastError();
dwBytesToCopy -= dwSectorOffsLen;
dwCmpSize += dwSectorOffsLen;
}
// Update compact progress
if(CompactCB != NULL)
{
CompactBytesProcessed += dwSectorOffsLen;
CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes);
}
STORM_FREE(SectorOffsetsCopy);
}
// Now we have to copy all file sectors. We do it without
// recompression, because recompression is not necessary in this case
if(nError == ERROR_SUCCESS)
{
for(DWORD dwSector = 0; dwSector < hf->dwSectorCount; dwSector++)
{
DWORD dwRawDataInSector = hf->dwSectorSize;
DWORD dwRawByteOffset = dwSector * hf->dwSectorSize;
// Fix the raw data length if the file is compressed
if(hf->SectorOffsets != NULL)
{
dwRawDataInSector = hf->SectorOffsets[dwSector+1] - hf->SectorOffsets[dwSector];
dwRawByteOffset = hf->SectorOffsets[dwSector];
}
// Last sector: If there is not enough bytes remaining in the file, cut the raw size
if(dwRawDataInSector > dwBytesToCopy)
dwRawDataInSector = dwBytesToCopy;
// Calculate the raw file offset of the file sector
CalculateRawSectorOffset(RawFilePos, hf, dwRawByteOffset);
// Read the file sector
if(!FileStream_Read(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector))
{
nError = GetLastError();
break;
}
// If necessary, re-encrypt the sector
// Note: Recompression is not necessary here. Unlike encryption,
// the compression does not depend on the position of the file in MPQ.
if((pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) && dwFileKey1 != dwFileKey2)
{
BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector);
DecryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwFileKey1 + dwSector);
EncryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwFileKey2 + dwSector);
BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector);
}
// Now write the sector back to the file
if(!FileStream_Write(pNewStream, NULL, hf->pbFileSector, dwRawDataInSector))
{
nError = GetLastError();
break;
}
// Update compact progress
if(CompactCB != NULL)
{
CompactBytesProcessed += dwRawDataInSector;
CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes);
}
// Adjust byte counts
dwBytesToCopy -= dwRawDataInSector;
dwCmpSize += dwRawDataInSector;
}
}
// Copy the sector CRCs, if any
// Sector CRCs are always compressed (not imploded) and unencrypted
if(nError == ERROR_SUCCESS && hf->SectorOffsets != NULL && hf->SectorChksums != NULL)
{
DWORD dwCrcLength;
dwCrcLength = hf->SectorOffsets[hf->dwSectorCount + 1] - hf->SectorOffsets[hf->dwSectorCount];
if(dwCrcLength != 0)
{
if(!FileStream_Read(ha->pStream, NULL, hf->SectorChksums, dwCrcLength))
nError = GetLastError();
if(!FileStream_Write(pNewStream, NULL, hf->SectorChksums, dwCrcLength))
nError = GetLastError();
// Update compact progress
if(CompactCB != NULL)
{
CompactBytesProcessed += dwCrcLength;
CompactCB(pvUserData, CCB_COMPACTING_FILES, CompactBytesProcessed, CompactTotalBytes);
}
// Size of the CRC block is also included in the compressed file size
dwBytesToCopy -= dwCrcLength;
dwCmpSize += dwCrcLength;
}
}
// There might be extra data beyond sector checksum table
// Sometimes, these data are even part of sector offset table
// Examples:
// 2012 - WoW\15354\locale-enGB.MPQ:DBFilesClient\SpellLevels.dbc
// 2012 - WoW\15354\locale-enGB.MPQ:Interface\AddOns\Blizzard_AuctionUI\Blizzard_AuctionUI.xml
if(nError == ERROR_SUCCESS && dwBytesToCopy != 0)
{
LPBYTE pbExtraData;
// Allocate space for the extra data
pbExtraData = STORM_ALLOC(BYTE, dwBytesToCopy);
if(pbExtraData != NULL)
{
if(!FileStream_Read(ha->pStream, NULL, pbExtraData, dwBytesToCopy))
nError = GetLastError();
if(!FileStream_Write(pNewStream, NULL, pbExtraData, dwBytesToCopy))
nError = GetLastError();
// Include these extra data in the compressed size
dwCmpSize += dwBytesToCopy;
dwBytesToCopy = 0;
STORM_FREE(pbExtraData);
}
else
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Write the MD5's of the raw file data, if needed
if(nError == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0)
{
nError = WriteMpqDataMD5(pNewStream,
ha->MpqPos + MpqFilePos,
pFileEntry->dwCmpSize,
ha->pHeader->dwRawChunkSize);
}
// Update file position in the block table
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, ...)
//
// 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
{
nError = ERROR_FILE_CORRUPT;
assert(false);
}
}
return nError;
}
static int CopyMpqFiles(TMPQArchive * ha, LPDWORD pFileKeys, TFileStream * pNewStream)
{
TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
TFileEntry * pFileEntry;
TMPQFile * hf = NULL;
int nError = ERROR_SUCCESS;
// Walk through all files and write them to the destination MPQ archive
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
{
// Copy all the file sectors
// Only do that when the file has nonzero size
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->dwFileSize != 0)
{
// Allocate structure for the MPQ file
hf = CreateMpqFile(ha);
if(hf == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// 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)
{
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;
// 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);
if(nError != ERROR_SUCCESS)
break;
// Free buffers. This also sets "hf" to NULL.
FreeMPQFile(hf);
}
}
// Cleanup and exit
if(hf != NULL)
FreeMPQFile(hf);
return nError;
}
/*****************************************************************************/
/* Public functions */
/*****************************************************************************/
bool WINAPI SFileSetCompactCallback(HANDLE /* hMpq */, SFILE_COMPACT_CALLBACK aCompactCB, void * pvData)
{
CompactCB = aCompactCB;
pvUserData = pvData;
return true;
}
//-----------------------------------------------------------------------------
// Archive compacting
bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bReserved */)
{
TFileStream * pTempStream = NULL;
TMPQArchive * ha = (TMPQArchive *)hMpq;
ULONGLONG ByteOffset;
ULONGLONG ByteCount;
LPDWORD pFileKeys = NULL;
TCHAR szTempFile[MAX_PATH] = _T("");
TCHAR * szTemp = NULL;
int nError = ERROR_SUCCESS;
// Test the valid parameters
if(!IsValidMpqHandle(ha))
nError = ERROR_INVALID_HANDLE;
if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
nError = ERROR_ACCESS_DENIED;
// If the MPQ is changed at this moment, we have to flush the archive
if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_CHANGED))
{
SFileFlushArchive(hMpq);
}
// Create the table with file keys
if(nError == ERROR_SUCCESS)
{
if((pFileKeys = STORM_ALLOC(DWORD, ha->dwFileTableSize)) != NULL)
memset(pFileKeys, 0, sizeof(DWORD) * ha->dwFileTableSize);
else
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// First of all, we have to check of we are able to decrypt all files.
// If not, sorry, but the archive cannot be compacted.
if(nError == ERROR_SUCCESS)
{
// Initialize the progress variables for compact callback
FileStream_GetSize(ha->pStream, CompactTotalBytes);
CompactBytesProcessed = 0;
nError = CheckIfAllFilesKnown(ha, szListFile, pFileKeys);
}
// Get the temporary file name and create it
if(nError == ERROR_SUCCESS)
{
_tcscpy(szTempFile, FileStream_GetFileName(ha->pStream));
if((szTemp = _tcsrchr(szTempFile, '.')) != NULL)
_tcscpy(szTemp + 1, _T("mp_"));
else
_tcscat(szTempFile, _T("_"));
pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
if(pTempStream == NULL)
nError = GetLastError();
}
// Write the data before MPQ user data (if any)
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);
ByteOffset = 0;
ByteCount = ha->UserDataPos;
nError = CopyNonMpqData(ha->pStream, pTempStream, ByteOffset, ByteCount);
}
// Write the MPQ user data (if any)
if(nError == ERROR_SUCCESS && ha->MpqPos > ha->UserDataPos)
{
// At this point, we assume that the user data size is equal
// to pUserData->dwHeaderOffs.
// If this assumption doesn't work, then we have an unknown version of MPQ
ByteOffset = ha->UserDataPos;
ByteCount = ha->MpqPos - ha->UserDataPos;
assert(ha->pUserData != NULL);
assert(ha->pUserData->dwHeaderOffs == ByteCount);
nError = CopyNonMpqData(ha->pStream, pTempStream, ByteOffset, ByteCount);
}
// Write the MPQ header
if(nError == ERROR_SUCCESS)
{
// Remember the header size before swapping
DWORD dwBytesToWrite = ha->pHeader->dwHeaderSize;
BSWAP_TMPQHEADER(ha->pHeader);
if(!FileStream_Write(pTempStream, NULL, ha->pHeader, dwBytesToWrite))
nError = GetLastError();
BSWAP_TMPQHEADER(ha->pHeader);
// Update the progress
CompactBytesProcessed += ha->pHeader->dwHeaderSize;
}
// Now copy all files
if(nError == ERROR_SUCCESS)
{
nError = CopyMpqFiles(ha, pFileKeys, pTempStream);
ha->dwFlags |= MPQ_FLAG_CHANGED;
}
// If succeeded, switch the streams
if(nError == ERROR_SUCCESS)
{
if(FileStream_Switch(ha->pStream, pTempStream))
pTempStream = NULL;
else
nError = ERROR_CAN_NOT_COMPLETE;
}
// If all succeeded, save the MPQ tables
if(nError == ERROR_SUCCESS)
{
//
// Note: We don't recalculate position of the MPQ tables at this point.
// SaveMPQTables does it automatically.
//
nError = SaveMPQTables(ha);
if(nError == ERROR_SUCCESS && CompactCB != NULL)
{
CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
CompactBytesProcessed += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock));
CompactCB(pvUserData, CCB_CLOSING_ARCHIVE, CompactBytesProcessed, CompactTotalBytes);
}
}
// Invalidate the compact callback
pvUserData = NULL;
CompactCB = NULL;
// Cleanup and return
if(pTempStream != NULL)
FileStream_Close(pTempStream);
if(pFileKeys != NULL)
STORM_FREE(pFileKeys);
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return (nError == ERROR_SUCCESS);
}
//-----------------------------------------------------------------------------
// Changing hash table size
DWORD WINAPI SFileGetMaxFileCount(HANDLE hMpq)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
return ha->dwMaxFileCount;
}
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;
int nError = ERROR_SUCCESS;
// Test the valid parameters
if(!IsValidMpqHandle(ha))
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)
nError = ERROR_DISK_FULL;
// ALL file names must be known in order to be able
// to rebuild hash table size
if(nError == ERROR_SUCCESS)
{
nError = CheckIfAllFilesKnown(ha, NULL, NULL);
}
// 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;
// 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;
}
// Now we have to build both classic hash table and HET table.
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;
InvalidateInternalFiles(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;
}
SetLastError(nError);
}
// Return the result
return (nError == ERROR_SUCCESS);
}

View file

@ -0,0 +1,255 @@
/*****************************************************************************/
/* SFileCreateArchive.cpp Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* MPQ Editing functions */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 24.03.03 1.00 Lad Splitted from SFileOpenArchive.cpp */
/* 08.06.10 1.00 Lad Renamed to SFileCreateArchive.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
//-----------------------------------------------------------------------------
// Local variables
static const DWORD MpqHeaderSizes[] =
{
MPQ_HEADER_SIZE_V1,
MPQ_HEADER_SIZE_V2,
MPQ_HEADER_SIZE_V3,
MPQ_HEADER_SIZE_V4
};
//-----------------------------------------------------------------------------
// Local functions
static USHORT GetSectorSizeShift(DWORD dwSectorSize)
{
USHORT wSectorSizeShift = 0;
while(dwSectorSize > 0x200)
{
dwSectorSize >>= 1;
wSectorSizeShift++;
}
return wSectorSizeShift;
}
static int WriteNakedMPQHeader(TMPQArchive * ha)
{
TMPQHeader * pHeader = ha->pHeader;
TMPQHeader Header;
DWORD dwBytesToWrite = pHeader->dwHeaderSize;
int nError = ERROR_SUCCESS;
// Prepare the naked MPQ header
memset(&Header, 0, sizeof(TMPQHeader));
Header.dwID = pHeader->dwID;
Header.dwHeaderSize = pHeader->dwHeaderSize;
Header.dwArchiveSize = pHeader->dwHeaderSize;
Header.wFormatVersion = pHeader->wFormatVersion;
Header.wSectorSize = pHeader->wSectorSize;
// Write it to the file
BSWAP_TMPQHEADER(&Header);
if(!FileStream_Write(ha->pStream, &ha->MpqPos, &Header, dwBytesToWrite))
nError = GetLastError();
return nError;
}
//-----------------------------------------------------------------------------
// Creates a new MPQ archive.
bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, 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.dwSectorSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_3) ? 0x4000 : 0x1000;
CreateInfo.dwRawChunkSize = (CreateInfo.dwMpqVersion >= MPQ_FORMAT_VERSION_4) ? 0x4000 : 0;
CreateInfo.dwMaxFileCount = dwMaxFileCount;
return SFileCreateArchive2(szMpqName, &CreateInfo, phMpq);
}
bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq)
{
TFileStream * pStream = NULL; // File stream
TMPQArchive * ha = NULL; // MPQ archive handle
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;
int nError = ERROR_SUCCESS;
// Check the parameters, if they are valid
if(szMpqName == NULL || *szMpqName == 0 || pCreateInfo == NULL || phMpq == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
// 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))
{
SetLastError(ERROR_INVALID_PARAMETER);
return false;
}
// One time initialization of MPQ cryptography
InitializeMpqCryptography();
// 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))
{
SFileCloseArchive(hMpq);
SetLastError(ERROR_ALREADY_EXISTS);
return false;
}
//
// At this point, we have to create the archive.
// - If the file exists, convert it to MPQ archive.
// - If the file doesn't exist, create new empty file
//
pStream = FileStream_OpenFile(szMpqName, pCreateInfo->dwStreamFlags);
if(pStream == NULL)
{
pStream = FileStream_CreateFile(szMpqName, pCreateInfo->dwStreamFlags);
if(pStream == NULL)
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++;
// If file count is not zero, initialize the hash table size
dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount);
// Retrieve the file size and round it up to 0x200 bytes
FileStream_GetSize(pStream, MpqPos);
MpqPos = (MpqPos + 0x1FF) & (ULONGLONG)0xFFFFFFFFFFFFFE00ULL;
if(!FileStream_SetSize(pStream, MpqPos))
nError = GetLastError();
#ifdef _DEBUG
// Debug code, used for testing StormLib
// dwBlockTableSize = dwHashTableSize * 2;
#endif
// Create the archive handle
if(nError == ERROR_SUCCESS)
{
if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Fill the MPQ archive handle structure
if(nError == ERROR_SUCCESS)
{
memset(ha, 0, sizeof(TMPQArchive));
ha->pStream = pStream;
ha->dwSectorSize = pCreateInfo->dwSectorSize;
ha->UserDataPos = MpqPos;
ha->MpqPos = MpqPos;
ha->pHeader = (TMPQHeader *)ha->HeaderData;
ha->dwMaxFileCount = dwMaxFileCount;
ha->dwFileTableSize = 0;
ha->dwFileFlags1 = pCreateInfo->dwFileFlags1;
ha->dwFileFlags2 = pCreateInfo->dwFileFlags2;
ha->dwFlags = 0;
// Setup the attributes
ha->dwAttrFlags = pCreateInfo->dwAttrFlags;
pStream = NULL;
}
// Fill the MPQ header
if(nError == ERROR_SUCCESS)
{
TMPQHeader * pHeader = ha->pHeader;
// Fill the MPQ header
memset(pHeader, 0, sizeof(ha->HeaderData));
pHeader->dwID = ID_MPQ;
pHeader->dwHeaderSize = MpqHeaderSizes[pCreateInfo->dwMpqVersion];
pHeader->dwArchiveSize = pHeader->dwHeaderSize + dwHashTableSize * sizeof(TMPQHash);
pHeader->wFormatVersion = (USHORT)pCreateInfo->dwMpqVersion;
pHeader->wSectorSize = GetSectorSizeShift(ha->dwSectorSize);
pHeader->dwHashTablePos = pHeader->dwHeaderSize;
pHeader->dwHashTableSize = dwHashTableSize;
pHeader->dwBlockTablePos = pHeader->dwHashTablePos + dwHashTableSize * sizeof(TMPQHash);
pHeader->dwBlockTableSize = dwBlockTableSize;
// For MPQs version 4 and higher, we set the size of raw data block
// for calculating MD5
if(pCreateInfo->dwMpqVersion >= MPQ_FORMAT_VERSION_4)
pHeader->dwRawChunkSize = pCreateInfo->dwRawChunkSize;
// 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)
{
ha->pHetTable = CreateHetTable(ha->dwMaxFileCount, 0x40, true);
if(ha->pHetTable == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Create initial hash table
if(nError == ERROR_SUCCESS)
{
nError = CreateHashTable(ha, dwHashTableSize);
}
// Create initial file table
if(nError == ERROR_SUCCESS)
{
ha->pFileTable = STORM_ALLOC(TFileEntry, ha->dwMaxFileCount);
if(ha->pFileTable != NULL)
memset(ha->pFileTable, 0x00, sizeof(TFileEntry) * ha->dwMaxFileCount);
else
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Cleanup : If an error, delete all buffers and return
if(nError != ERROR_SUCCESS)
{
FileStream_Close(pStream);
FreeMPQArchive(ha);
SetLastError(nError);
ha = NULL;
}
// Return the values
*phMpq = (HANDLE)ha;
return (nError == ERROR_SUCCESS);
}

View file

@ -0,0 +1,67 @@
/*****************************************************************************/
/* SFileExtractFile.cpp Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* Simple extracting utility */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 20.06.03 1.00 Lad The first version of SFileExtractFile.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope)
{
TFileStream * pLocalFile = NULL;
HANDLE hMpqFile = NULL;
int nError = ERROR_SUCCESS;
// Open the MPQ file
if(nError == ERROR_SUCCESS)
{
if(!SFileOpenFileEx(hMpq, szToExtract, dwSearchScope, &hMpqFile))
nError = GetLastError();
}
// Create the local file
if(nError == ERROR_SUCCESS)
{
pLocalFile = FileStream_CreateFile(szExtracted, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE);
if(pLocalFile == NULL)
nError = GetLastError();
}
// Copy the file's content
if(nError == ERROR_SUCCESS)
{
char szBuffer[0x1000];
DWORD dwTransferred;
for(;;)
{
// dwTransferred is only set to nonzero if something has been read.
// nError can be ERROR_SUCCESS or ERROR_HANDLE_EOF
if(!SFileReadFile(hMpqFile, szBuffer, sizeof(szBuffer), &dwTransferred, NULL))
nError = GetLastError();
if(nError == ERROR_HANDLE_EOF)
nError = ERROR_SUCCESS;
if(dwTransferred == 0)
break;
// If something has been actually read, write it
if(!FileStream_Write(pLocalFile, NULL, szBuffer, dwTransferred))
nError = GetLastError();
}
}
// Close the files
if(hMpqFile != NULL)
SFileCloseFile(hMpqFile);
if(pLocalFile != NULL)
FileStream_Close(pLocalFile);
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return (nError == ERROR_SUCCESS);
}

View file

@ -0,0 +1,446 @@
/*****************************************************************************/
/* SFileFindFile.cpp Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* A module for file searching within MPQs */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 25.03.03 1.00 Lad The first version of SFileFindFile.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
//-----------------------------------------------------------------------------
// Defines
#define LISTFILE_CACHE_SIZE 0x1000
//-----------------------------------------------------------------------------
// Private structure used for file search (search handle)
struct TMPQSearch;
typedef int (*MPQSEARCH)(TMPQSearch *, SFILE_FIND_DATA *);
// Used by searching in MPQ archives
struct TMPQSearch
{
TMPQArchive * ha; // Handle to MPQ, where the search runs
TFileEntry ** pSearchTable; // Table for files that have been already found
DWORD dwSearchTableItems; // Number of items in the search table
DWORD dwNextIndex; // Next file index to be checked
DWORD dwFlagMask; // For checking flag mask
char szSearchMask[1]; // Search mask (variable length)
};
//-----------------------------------------------------------------------------
// Local functions
static bool IsValidSearchHandle(TMPQSearch * hs)
{
if(hs == NULL)
return false;
return IsValidMpqHandle(hs->ha);
}
bool CheckWildCard(const char * szString, const char * szWildCard)
{
const char * szSubString;
int nSubStringLength;
int nMatchCount = 0;
// When the mask is empty, it never matches
if(szWildCard == NULL || *szWildCard == 0)
return false;
// If the wildcard contains just "*", then it always matches
if(szWildCard[0] == '*' && szWildCard[1] == 0)
return true;
// Do normal test
for(;;)
{
// If there is '?' in the wildcard, we skip one char
while(*szWildCard == '?')
{
szWildCard++;
szString++;
}
// If there is '*', means zero or more chars. We have to
// find the sequence after '*'
if(*szWildCard == '*')
{
// More stars is equal to one star
while(*szWildCard == '*' || *szWildCard == '?')
szWildCard++;
// If we found end of the wildcard, it's a match
if(*szWildCard == 0)
return true;
// Determine the length of the substring in szWildCard
szSubString = szWildCard;
while(*szSubString != 0 && *szSubString != '?' && *szSubString != '*')
szSubString++;
nSubStringLength = (int)(szSubString - szWildCard);
nMatchCount = 0;
// Now we have to find a substring in szString,
// that matches the substring in szWildCard
while(*szString != 0)
{
// Calculate match count
while(nMatchCount < nSubStringLength)
{
if(toupper(szString[nMatchCount]) != toupper(szWildCard[nMatchCount]))
break;
if(szString[nMatchCount] == 0)
break;
nMatchCount++;
}
// If the match count has reached substring length, we found a match
if(nMatchCount == nSubStringLength)
{
szWildCard += nMatchCount;
szString += nMatchCount;
break;
}
// No match, move to the next char in szString
nMatchCount = 0;
szString++;
}
}
else
{
// If we came to the end of the string, compare it to the wildcard
if(toupper(*szString) != toupper(*szWildCard))
return false;
// If we arrived to the end of the string, it's a match
if(*szString == 0)
return true;
// Otherwise, continue in comparing
szWildCard++;
szString++;
}
}
}
static DWORD GetSearchTableItems(TMPQArchive * ha)
{
DWORD dwMergeItems = 0;
// Loop over all patches
while(ha != NULL)
{
// Append the number of files
dwMergeItems += (ha->pHetTable != NULL) ? ha->pHetTable->dwMaxFileCount
: ha->pHeader->dwBlockTableSize;
// Move to the patched archive
ha = ha->haPatch;
}
// Return the double size of number of items
return (dwMergeItems | 1);
}
static bool FileWasFoundBefore(
TMPQArchive * ha,
TMPQSearch * hs,
TFileEntry * pFileEntry)
{
TFileEntry * pEntry;
char * szRealFileName = pFileEntry->szFileName;
DWORD dwStartIndex;
DWORD dwNameHash;
DWORD dwIndex;
if(hs->pSearchTable != NULL && szRealFileName != NULL)
{
// If we are in patch MPQ, we check if patch prefix matches
// and then trim the patch prefix
if(ha->cchPatchPrefix != 0)
{
// 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))
return true;
szRealFileName += ha->cchPatchPrefix;
}
// Calculate the hash to the table
dwNameHash = HashString(szRealFileName, MPQ_HASH_NAME_A);
dwStartIndex = dwIndex = (dwNameHash % hs->dwSearchTableItems);
// The file might have been found before
// only if this is not the first MPQ being searched
if(ha->haBase != NULL)
{
// Enumerate all entries in the search table
for(;;)
{
// Get the file entry at that position
pEntry = hs->pSearchTable[dwIndex];
if(pEntry == NULL)
break;
if(pEntry->szFileName != NULL)
{
// Does the name match?
if(!_stricmp(pEntry->szFileName, szRealFileName))
return true;
}
// Move to the next entry
dwIndex = (dwIndex + 1) % hs->dwSearchTableItems;
if(dwIndex == dwStartIndex)
break;
}
}
// Put the entry to the table for later use
hs->pSearchTable[dwIndex] = pFileEntry;
}
return false;
}
static TFileEntry * FindPatchEntry(TMPQArchive * ha, TFileEntry * pFileEntry)
{
TFileEntry * pPatchEntry = NULL;
TFileEntry * pTempEntry;
char szFileName[MAX_PATH];
LCID lcLocale = pFileEntry->lcLocale;
// Go while there are patches
while(ha->haPatch != NULL)
{
// Move to the patch archive
ha = ha->haPatch;
// Prepare the prefix for the file name
strcpy(szFileName, ha->szPatchPrefix);
strcat(szFileName, pFileEntry->szFileName);
// Try to find the file there
pTempEntry = GetFileEntryExact(ha, szFileName, lcLocale);
if(pTempEntry != NULL)
pPatchEntry = pTempEntry;
}
// Return the found patch entry
return pPatchEntry;
}
// Performs one MPQ search
static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
{
TMPQArchive * ha = hs->ha;
TFileEntry * pFileTableEnd;
TFileEntry * pPatchEntry;
TFileEntry * pFileEntry;
const char * szFileName;
HANDLE hFile;
char szPseudoName[20];
DWORD dwBlockIndex;
size_t nPrefixLength;
// Start searching with base MPQ
while(ha != NULL)
{
// Now parse the file entry table in order to get all files.
pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
pFileEntry = ha->pFileTable + hs->dwNextIndex;
// Get the length of the patch prefix (0 if none)
nPrefixLength = strlen(ha->szPatchPrefix);
// Parse the file table
while(pFileEntry < pFileTableEnd)
{
// Increment the next index for subsequent search
hs->dwNextIndex++;
// Is it a file and 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))
{
// 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)
{
// 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))
{
szFileName = (pFileEntry->szFileName != NULL) ? pFileEntry->szFileName : szPseudoName;
SFileCloseFile(hFile);
}
}
// 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++;
}
// Move to the next patch in the patch chain
hs->ha = ha = ha->haPatch;
hs->dwNextIndex = 0;
}
// No more files found, return error
return ERROR_NO_MORE_FILES;
}
static void FreeMPQSearch(TMPQSearch *& hs)
{
if(hs != NULL)
{
if(hs->pSearchTable != NULL)
STORM_FREE(hs->pSearchTable);
STORM_FREE(hs);
hs = NULL;
}
}
//-----------------------------------------------------------------------------
// Public functions
HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const char * szListFile)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
TMPQSearch * hs = NULL;
size_t nSize = 0;
int nError = ERROR_SUCCESS;
// Check for the valid parameters
if(!IsValidMpqHandle(ha))
nError = ERROR_INVALID_HANDLE;
if(szMask == NULL || lpFindFileData == NULL)
nError = ERROR_INVALID_PARAMETER;
// Include the listfile into the MPQ's internal listfile
// Note that if the listfile name is NULL, do nothing because the
// internal listfile is always included.
if(nError == ERROR_SUCCESS && szListFile != NULL && *szListFile != 0)
nError = SFileAddListFile((HANDLE)ha, szListFile);
// Allocate the structure for MPQ search
if(nError == ERROR_SUCCESS)
{
nSize = sizeof(TMPQSearch) + strlen(szMask) + 1;
if((hs = (TMPQSearch *)STORM_ALLOC(char, nSize)) == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Perform the first search
if(nError == ERROR_SUCCESS)
{
memset(hs, 0, sizeof(TMPQSearch));
strcpy(&hs->szSearchMask[0], szMask);
hs->dwFlagMask = MPQ_FILE_EXISTS;
hs->ha = ha;
// If the archive is patched archive, we have to create a merge table
// to prevent files being repeated
if(ha->haPatch != NULL)
{
hs->dwSearchTableItems = GetSearchTableItems(ha);
hs->pSearchTable = STORM_ALLOC(TFileEntry *, hs->dwSearchTableItems);
hs->dwFlagMask = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE;
if(hs->pSearchTable != NULL)
memset(hs->pSearchTable, 0, hs->dwSearchTableItems * sizeof(TFileEntry *));
else
nError = ERROR_NOT_ENOUGH_MEMORY;
}
}
// Perform first item searching
if(nError == ERROR_SUCCESS)
{
nError = DoMPQSearch(hs, lpFindFileData);
}
// Cleanup
if(nError != ERROR_SUCCESS)
{
FreeMPQSearch(hs);
SetLastError(nError);
}
// Return the result value
return (HANDLE)hs;
}
bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
{
TMPQSearch * hs = (TMPQSearch *)hFind;
int nError = ERROR_SUCCESS;
// Check the parameters
if(!IsValidSearchHandle(hs))
nError = ERROR_INVALID_HANDLE;
if(lpFindFileData == NULL)
nError = ERROR_INVALID_PARAMETER;
if(nError == ERROR_SUCCESS)
nError = DoMPQSearch(hs, lpFindFileData);
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return (nError == ERROR_SUCCESS);
}
bool WINAPI SFileFindClose(HANDLE hFind)
{
TMPQSearch * hs = (TMPQSearch *)hFind;
// Check the parameters
if(!IsValidSearchHandle(hs))
{
SetLastError(ERROR_INVALID_HANDLE);
return false;
}
FreeMPQSearch(hs);
return true;
}

View file

@ -0,0 +1,557 @@
/*****************************************************************************/
/* SListFile.cpp Copyright (c) Ladislav Zezula 2004 */
/*---------------------------------------------------------------------------*/
/* Description: */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 12.06.04 1.00 Lad The first version of SListFile.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
#include <assert.h>
//-----------------------------------------------------------------------------
// Listfile entry structure
#define CACHE_BUFFER_SIZE 0x1000 // Size of the cache buffer
struct TListFileCache
{
HANDLE hFile; // Stormlib file handle
char * szMask; // 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
};
//-----------------------------------------------------------------------------
// Local functions (cache)
static TListFileCache * CreateListFileCache(HANDLE hMpq, const char * szListFile)
{
TListFileCache * pCache = NULL;
HANDLE hListFile = NULL;
DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE;
DWORD dwBytesRead = 0;
int nError = ERROR_SUCCESS;
// If the szListFile is NULL, it means we have to open internal listfile
if(szListFile == NULL)
{
// Use SFILE_OPEN_ANY_LOCALE for listfile. This will allow us to load
// the listfile even if there is only non-neutral version of the listfile in the MPQ
dwSearchScope = SFILE_OPEN_ANY_LOCALE;
szListFile = LISTFILE_NAME;
}
// Open the local/internal listfile
if(SFileOpenFileEx(hMpq, szListFile, dwSearchScope, &hListFile))
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
TMPQFile * hf = (TMPQFile *)hListFile;
// Remember flags for (listfile)
if(hf->pFileEntry != NULL)
ha->dwFileFlags1 = hf->pFileEntry->dwFlags;
}
else
nError = GetLastError();
// Allocate cache for one file block
if(nError == ERROR_SUCCESS)
{
pCache = (TListFileCache *)STORM_ALLOC(TListFileCache, 1);
if(pCache == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
if(nError == ERROR_SUCCESS)
{
// Initialize the file cache
memset(pCache, 0, sizeof(TListFileCache));
pCache->dwFileSize = SFileGetFileSize(hListFile, NULL);
pCache->hFile = hListFile;
// Fill the cache
SFileReadFile(hListFile, pCache->Buffer, CACHE_BUFFER_SIZE, &dwBytesRead, NULL);
if(dwBytesRead == 0)
nError = GetLastError();
}
// Initialize the pointers
if(nError == ERROR_SUCCESS)
{
pCache->pBegin =
pCache->pPos = &pCache->Buffer[0];
pCache->pEnd = pCache->pBegin + dwBytesRead;
}
else
{
SListFileFindClose((HANDLE)pCache);
SetLastError(nError);
pCache = NULL;
}
// Return the cache
return pCache;
}
// Reloads the cache. Returns number of characters
// that has been loaded into the cache.
static DWORD ReloadListFileCache(TListFileCache * pCache)
{
DWORD dwBytesToRead;
DWORD dwBytesRead = 0;
// 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)
return 0;
// Get the number of bytes remaining
dwBytesToRead = pCache->dwFileSize - pCache->dwFilePos;
if(dwBytesToRead > CACHE_BUFFER_SIZE)
dwBytesToRead = CACHE_BUFFER_SIZE;
// 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);
// If we didn't read anything, it might mean that the block
// of the file is not available (in case of partial MPQs).
// We stop reading the file at this point, because the rest
// of the listfile is unreliable
if(dwBytesRead == 0)
return 0;
// Set the buffer pointers
pCache->pBegin =
pCache->pPos = &pCache->Buffer[0];
pCache->pEnd = pCache->pBegin + dwBytesRead;
}
return dwBytesRead;
}
static size_t ReadListFileLine(TListFileCache * pCache, char * szLine, int nMaxChars)
{
char * szLineBegin = szLine;
char * szLineEnd = szLine + nMaxChars - 1;
char * szExtraString = NULL;
// Skip newlines, spaces, tabs and another non-printable stuff
for(;;)
{
// If we need to reload the cache, do it
if(pCache->pPos == pCache->pEnd)
{
if(ReloadListFileCache(pCache) == 0)
break;
}
// If we found a non-whitespace character, stop
if(*pCache->pPos > 0x20)
break;
// Skip the character
pCache->pPos++;
}
// Copy the remaining characters
while(szLine < szLineEnd)
{
// If we need to reload the cache, do it now and resume copying
if(pCache->pPos == pCache->pEnd)
{
if(ReloadListFileCache(pCache) == 0)
break;
}
// If we have found a newline, stop loading
if(*pCache->pPos == 0x0D || *pCache->pPos == 0x0A)
break;
// Blizzard listfiles can also contain information about patch:
// Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326)
if(*pCache->pPos == '~')
szExtraString = szLine;
// Copy the character
*szLine++ = *pCache->pPos++;
}
// Terminate line with zero
*szLine = 0;
// If there was extra string after the file name, clear it
if(szExtraString != NULL)
{
if(szExtraString[0] == '~' && szExtraString[1] == 'P')
{
szLine = szExtraString;
*szExtraString = 0;
}
}
// Return the length of the line
return (szLine - szLineBegin);
}
static int CompareFileNodes(const void * p1, const void * p2)
{
char * szFileName1 = *(char **)p1;
char * szFileName2 = *(char **)p2;
return _stricmp(szFileName1, szFileName2);
}
static int WriteListFileLine(
TMPQFile * hf,
const char * szLine)
{
char szNewLine[2] = {0x0D, 0x0A};
size_t nLength = strlen(szLine);
int nError;
nError = SFileAddFile_Write(hf, szLine, (DWORD)nLength, MPQ_COMPRESSION_ZLIB);
if(nError != ERROR_SUCCESS)
return nError;
return SFileAddFile_Write(hf, szNewLine, sizeof(szNewLine), MPQ_COMPRESSION_ZLIB);
}
//-----------------------------------------------------------------------------
// Local functions (listfile nodes)
// Adds a name into the list of all names. For each locale in the MPQ,
// one entry will be created
// If the file name is already there, does nothing.
int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName)
{
TMPQHeader * pHeader = ha->pHeader;
TFileEntry * pFileEntry;
TMPQHash * pFirstHash;
TMPQHash * pHash;
bool bNameEntryCreated = false;
// If we have HET table, use that one
if(ha->pHetTable != NULL)
{
pFileEntry = GetFileEntryAny(ha, szFileName);
if(pFileEntry != NULL)
{
// Allocate file name for the file entry
AllocateFileName(pFileEntry, szFileName);
bNameEntryCreated = true;
}
return ERROR_SUCCESS;
}
// If we have hash table, we use it
if(bNameEntryCreated == false && ha->pHashTable != NULL)
{
// Look for the first hash table entry for the file
pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
// Go while we found something
while(pHash != NULL)
{
// Is it a valid file table index ?
if(pHash->dwBlockIndex < pHeader->dwBlockTableSize)
{
// Allocate file name for the file entry
AllocateFileName(ha->pFileTable + pHash->dwBlockIndex, szFileName);
bNameEntryCreated = true;
}
// Now find the next language version of the file
pHash = GetNextHashEntry(ha, pFirstHash, pHash);
}
}
return ERROR_CAN_NOT_COMPLETE;
}
// Saves the whole listfile into 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;
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 take existing items
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL)
{
// Ignore pseudo-names
if(!IsPseudoFileName(pFileEntry->szFileName, NULL) && !IsInternalMpqFileName(pFileEntry->szFileName))
{
SortTable[nFileNodes++] = pFileEntry->szFileName;
}
}
}
// Sort the table
qsort(SortTable, nFileNodes, sizeof(char *), CompareFileNodes);
// Now parse the table of file names again - remove duplicates
// and count file size.
if(nFileNodes != 0)
{
// Count the 0-th item
dwFileSize += (DWORD)strlen(SortTable[0]) + 2;
szPrevItem = SortTable[0];
// Count all next items
for(i = 1; i < nFileNodes; i++)
{
// If the item is the same like the last one, skip it
if(_stricmp(SortTable[i], szPrevItem))
{
dwFileSize += (DWORD)strlen(SortTable[i]) + 2;
szPrevItem = SortTable[i];
}
}
// 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(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);
}
}
// 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;
}
//-----------------------------------------------------------------------------
// File functions
// Adds a listfile into the MPQ archive.
// Note that the function does not remove the
int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile)
{
TListFileCache * pCache = NULL;
TMPQArchive * ha = (TMPQArchive *)hMpq;
char szFileName[MAX_PATH];
size_t nLength = 0;
int nError = ERROR_SUCCESS;
// Add the listfile for each MPQ in the patch chain
while(ha != NULL)
{
// Load the listfile to cache
pCache = CreateListFileCache(hMpq, szListFile);
if(pCache == NULL)
{
nError = GetLastError();
break;
}
// Load the node list. Add the node for every locale in the archive
while((nLength = ReadListFileLine(pCache, szFileName, sizeof(szFileName))) > 0)
SListFileCreateNodeForAllLocales(ha, szFileName);
// Also, add three special files to the listfile:
// (listfile) itself, (attributes) and (signature)
SListFileCreateNodeForAllLocales(ha, LISTFILE_NAME);
SListFileCreateNodeForAllLocales(ha, SIGNATURE_NAME);
SListFileCreateNodeForAllLocales(ha, ATTRIBUTES_NAME);
// Delete the cache
SListFileFindClose((HANDLE)pCache);
// Move to the next archive in the chain
ha = ha->haPatch;
}
return nError;
}
//-----------------------------------------------------------------------------
// Passing through the listfile
HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData)
{
TListFileCache * pCache = NULL;
size_t nLength = 0;
int nError = ERROR_SUCCESS;
// Initialize the structure with zeros
memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
// Load the listfile to cache
pCache = CreateListFileCache(hMpq, szListFile);
if(pCache == NULL)
nError = GetLastError();
// Allocate file mask
if(nError == ERROR_SUCCESS && szMask != NULL)
{
pCache->szMask = STORM_ALLOC(char, strlen(szMask) + 1);
if(pCache->szMask != NULL)
strcpy(pCache->szMask, szMask);
else
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Perform file search
if(nError == ERROR_SUCCESS)
{
for(;;)
{
// Read the (next) line
nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
if(nLength == 0)
{
nError = ERROR_NO_MORE_FILES;
break;
}
// If some mask entered, check it
if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
break;
}
}
// Cleanup & exit
if(nError != ERROR_SUCCESS)
{
memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
SListFileFindClose((HANDLE)pCache);
pCache = NULL;
SetLastError(nError);
}
return (HANDLE)pCache;
}
bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
{
TListFileCache * pCache = (TListFileCache *)hFind;
size_t nLength;
bool bResult = false;
int nError = ERROR_SUCCESS;
for(;;)
{
// Read the (next) line
nLength = ReadListFileLine(pCache, lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName));
if(nLength == 0)
{
nError = ERROR_NO_MORE_FILES;
break;
}
// If some mask entered, check it
if(CheckWildCard(lpFindFileData->cFileName, pCache->szMask))
{
bResult = true;
break;
}
}
if(nError != ERROR_SUCCESS)
SetLastError(nError);
return bResult;
}
bool WINAPI SListFileFindClose(HANDLE hFind)
{
TListFileCache * pCache = (TListFileCache *)hFind;
if(pCache != NULL)
{
if(pCache->hFile != NULL)
SFileCloseFile(pCache->hFile);
if(pCache->szMask != NULL)
STORM_FREE(pCache->szMask);
STORM_FREE(pCache);
return true;
}
return false;
}

View file

@ -0,0 +1,470 @@
/*****************************************************************************/
/* SFileOpenArchive.cpp Copyright Ladislav Zezula 1999 */
/* */
/* Author : Ladislav Zezula */
/* E-mail : ladik@zezula.net */
/* WWW : www.zezula.net */
/*---------------------------------------------------------------------------*/
/* Archive functions of Storm.dll */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* xx.xx.xx 1.00 Lad The first version of SFileOpenArchive.cpp */
/* 19.11.03 1.01 Dan Big endian handling */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
/*****************************************************************************/
/* Local functions */
/*****************************************************************************/
static bool IsAviFile(void * pvFileBegin)
{
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]);
// Test for 'RIFF', 'AVI ' or 'LIST'
return (DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C);
}
static TFileBitmap * CreateFileBitmap(TMPQArchive * ha, TMPQBitmap * pMpqBitmap, bool bFileIsComplete)
{
TFileBitmap * pBitmap;
size_t nLength;
// 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);
// Allocate the file bitmap
pBitmap = (TFileBitmap *)STORM_ALLOC(BYTE, sizeof(TFileBitmap) + nLength);
if(pBitmap != NULL)
{
// 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;
// Copy the file bitmap
memcpy((pBitmap + 1), (pMpqBitmap + 1), nLength);
}
return pBitmap;
}
// This function gets the right positions of the hash table and the block table.
static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize)
{
TMPQHeader * pHeader = ha->pHeader;
ULONGLONG ByteOffset;
// Check the begin of HET table
if(pHeader->HetTablePos64)
{
ByteOffset = ha->MpqPos + pHeader->HetTablePos64;
if(ByteOffset > FileSize)
return ERROR_BAD_FORMAT;
}
// Check the begin of BET table
if(pHeader->BetTablePos64)
{
ByteOffset = ha->MpqPos + pHeader->BetTablePos64;
if(ByteOffset > FileSize)
return ERROR_BAD_FORMAT;
}
// Check the begin of hash table
if(pHeader->wHashTablePosHi || pHeader->dwHashTablePos)
{
ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos);
if(ByteOffset > FileSize)
return ERROR_BAD_FORMAT;
}
// Check the begin of block table
if(pHeader->wBlockTablePosHi || pHeader->dwBlockTablePos)
{
ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos);
if(ByteOffset > FileSize)
return ERROR_BAD_FORMAT;
}
// Check the begin of hi-block table
if(pHeader->HiBlockTablePos64 != 0)
{
ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64;
if(ByteOffset > FileSize)
return ERROR_BAD_FORMAT;
}
// All OK.
return ERROR_SUCCESS;
}
/*****************************************************************************/
/* Public functions */
/*****************************************************************************/
//-----------------------------------------------------------------------------
// SFileGetLocale and SFileSetLocale
// Set the locale for all newly opened files
LCID WINAPI SFileGetLocale()
{
return lcFileLocale;
}
LCID WINAPI SFileSetLocale(LCID lcNewLocale)
{
lcFileLocale = lcNewLocale;
return lcFileLocale;
}
//-----------------------------------------------------------------------------
// SFileOpenArchive
//
// szFileName - MPQ archive file name to open
// dwPriority - When SFileOpenFileEx called, this contains the search priority for searched archives
// dwFlags - See MPQ_OPEN_XXX in StormLib.h
// phMpq - Pointer to store open archive handle
bool WINAPI SFileOpenArchive(
const TCHAR * szMpqName,
DWORD dwPriority,
DWORD dwFlags,
HANDLE * phMpq)
{
TFileStream * pStream = NULL; // Open file stream
TMPQArchive * ha = NULL; // Archive handle
ULONGLONG FileSize = 0; // Size of the file
int nError = ERROR_SUCCESS;
// Verify the parameters
if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL)
nError = ERROR_INVALID_PARAMETER;
// One time initialization of MPQ cryptography
InitializeMpqCryptography();
dwPriority = dwPriority;
// Open the MPQ archive file
if(nError == ERROR_SUCCESS)
{
// Initialize the stream
pStream = FileStream_OpenFile(szMpqName, (dwFlags & STREAM_OPTIONS_MASK));
if(pStream == NULL)
nError = GetLastError();
}
// Allocate the MPQhandle
if(nError == ERROR_SUCCESS)
{
FileStream_GetSize(pStream, FileSize);
if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Initialize handle structure and allocate structure for MPQ header
if(nError == ERROR_SUCCESS)
{
memset(ha, 0, sizeof(TMPQArchive));
ha->pStream = pStream;
pStream = NULL;
// Remember if the archive is open for write
if(FileStream_IsReadOnly(ha->pStream))
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
// 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;
}
// Find the offset of MPQ header within the file
if(nError == ERROR_SUCCESS)
{
ULONGLONG SearchPos = 0;
DWORD dwHeaderID;
while(SearchPos < FileSize)
{
DWORD dwBytesAvailable = MPQ_HEADER_SIZE_V4;
// Cut the bytes available, if needed
if((FileSize - SearchPos) < MPQ_HEADER_SIZE_V4)
dwBytesAvailable = (DWORD)(FileSize - SearchPos);
// Read the eventual MPQ header
if(!FileStream_Read(ha->pStream, &SearchPos, ha->HeaderData, dwBytesAvailable))
{
nError = GetLastError();
break;
}
// There are AVI files from Warcraft III with 'MPQ' extension.
if(SearchPos == 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)
{
// 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)
{
// Fill the user data header
ha->pUserData = &ha->UserData;
memcpy(ha->pUserData, ha->HeaderData, sizeof(TMPQUserData));
BSWAP_TMPQUSERDATA(ha->pUserData);
// Remember the position of the user data and continue search
ha->UserDataPos = SearchPos;
SearchPos += ha->pUserData->dwHeaderOffs;
continue;
}
}
// There must be MPQ header signature
if(dwHeaderID == ID_MPQ)
{
// 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);
break;
}
// Move to the next possible offset
SearchPos += 0x200;
}
// If we haven't found MPQ header in the file, it's an error
if(ha->pHeader == NULL)
nError = ERROR_BAD_FORMAT;
}
// Fix table positions according to format
if(nError == ERROR_SUCCESS)
{
// Dump the header
// 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
// 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)
{
ha->pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1;
ha->pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1;
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
ha->pUserData = NULL;
}
// Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode
if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES))
ha->dwFlags |= MPQ_FLAG_READ_ONLY;
// Set the size of file sector
ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize);
// Verify if any of the tables doesn't start beyond the end of the file
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)
{
nError = LoadAnyHashTable(ha);
}
// Now, build the file table. It will be built by combining
// the block table, BET table, hi-block table, (attributes) and (listfile).
if(nError == ERROR_SUCCESS)
{
nError = BuildFileTable(ha, FileSize);
}
// Verify the file table, if no kind of protection was detected
if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_PROTECTED) == 0)
{
TFileEntry * pFileTableEnd = ha->pFileTable + ha->pHeader->dwBlockTableSize;
TFileEntry * pFileEntry = ha->pFileTable;
// ULONGLONG ArchiveSize = 0;
ULONGLONG RawFilePos;
// Parse all file entries
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
{
// If that file entry is valid, check the file position
if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
{
// Get the 64-bit file position,
// relative to the begin of the file
RawFilePos = ha->MpqPos + pFileEntry->ByteOffset;
// Begin of the file must be within range
if(RawFilePos > FileSize)
{
nError = ERROR_FILE_CORRUPT;
break;
}
// End of the file must be within range
RawFilePos += pFileEntry->dwCmpSize;
if(RawFilePos > FileSize)
{
nError = ERROR_FILE_CORRUPT;
break;
}
// Also, we remember end of the file
// if(RawFilePos > ArchiveSize)
// ArchiveSize = RawFilePos;
}
}
}
// Load the internal listfile and include it to the file table
if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0)
{
// 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)
{
// Ignore result of the operation. (attributes) is optional.
SAttrLoadAttributes(ha);
}
// Cleanup and exit
if(nError != ERROR_SUCCESS)
{
FileStream_Close(pStream);
FreeMPQArchive(ha);
SetLastError(nError);
ha = NULL;
}
*phMpq = ha;
return (nError == ERROR_SUCCESS);
}
//-----------------------------------------------------------------------------
// SFileGetArchiveBitmap
bool WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
return FileStream_GetBitmap(ha->pStream, pBitmap, Length, LengthNeeded);
}
//-----------------------------------------------------------------------------
// bool SFileFlushArchive(HANDLE hMpq)
//
// Saves all dirty data into MPQ archive.
// Has similar effect like SFileCloseArchive, but the archive is not closed.
// Use on clients who keep MPQ archive open even for write operations,
// and terminating without calling SFileCloseArchive might corrupt the archive.
//
bool WINAPI SFileFlushArchive(HANDLE hMpq)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
int nResultError = ERROR_SUCCESS;
int nError;
// Do nothing if 'hMpq' is bad parameter
if(!IsValidMpqHandle(ha))
{
SetLastError(ERROR_INVALID_HANDLE);
return false;
}
// If the (listfile) has been invalidated, save it
if(ha->dwFlags & MPQ_FLAG_INV_LISTFILE)
{
nError = SListFileSaveToMpq(ha);
if(nError != ERROR_SUCCESS)
nResultError = nError;
}
// If the (attributes) has been invalidated, save it
if(ha->dwFlags & MPQ_FLAG_INV_ATTRIBUTES)
{
nError = SAttrFileSaveToMpq(ha);
if(nError != ERROR_SUCCESS)
nResultError = nError;
}
// Save HET table, BET table, hash table, block table, hi-block table
if(ha->dwFlags & MPQ_FLAG_CHANGED)
{
nError = SaveMPQTables(ha);
if(nError != ERROR_SUCCESS)
nResultError = nError;
}
// Return the error
if(nResultError != ERROR_SUCCESS)
SetLastError(nResultError);
return (nResultError == ERROR_SUCCESS);
}
//-----------------------------------------------------------------------------
// bool SFileCloseArchive(HANDLE hMpq);
//
bool WINAPI SFileCloseArchive(HANDLE hMpq)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
bool bResult;
// Flush all unsaved data to the storage
bResult = SFileFlushArchive(hMpq);
// Free all memory used by MPQ archive
FreeMPQArchive(ha);
return bResult;
}

View file

@ -0,0 +1,469 @@
/*****************************************************************************/
/* SFileOpenFileEx.cpp Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* Description : */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* xx.xx.99 1.00 Lad The first version of SFileOpenFileEx.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
/*****************************************************************************/
/* Local functions */
/*****************************************************************************/
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
if(pStream != NULL)
{
// Allocate and initialize file handle
hf = CreateMpqFile(NULL);
if(hf != NULL)
{
hf->pStream = pStream;
*phFile = hf;
return true;
}
else
{
FileStream_Close(pStream);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
}
*phFile = NULL;
return false;
}
bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, DWORD dwReserved, HANDLE * phFile)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
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];
// 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
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;
}
SFileCloseFile((HANDLE)hfBase);
}
// Move to the next file in the patch chain
ha = ha->haPatch;
}
// If we couldn't find the file in any of the patches, it doesn't exist
if(hf == 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)
{
// Construct patch file name
strcpy(szPatchFileName, ha->szPatchPrefix);
strcpy(&szPatchFileName[ha->cchPatchPrefix], szFileName);
if(SFileOpenFileEx((HANDLE)ha, szPatchFileName, SFILE_OPEN_FROM_MPQ, &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;
// Set current patch to base file and move on
hf->hfPatchFile = 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;
}
/*****************************************************************************/
/* Public functions */
/*****************************************************************************/
//-----------------------------------------------------------------------------
// SFileEnumLocales enums all locale versions within MPQ.
// Functions fills all available language identifiers on a file into the buffer
// pointed by plcLocales. There must be enough entries to copy the localed,
// otherwise the function returns ERROR_INSUFFICIENT_BUFFER.
int WINAPI SFileEnumLocales(
HANDLE hMpq,
const char * szFileName,
LCID * plcLocales,
LPDWORD pdwMaxLocales,
DWORD dwSearchScope)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
TFileEntry * pFileEntry;
TMPQHash * pFirstHash;
TMPQHash * pHash;
DWORD dwFileIndex = 0;
DWORD dwLocales = 0;
// Test the parameters
if(!IsValidMpqHandle(ha))
return ERROR_INVALID_HANDLE;
if(szFileName == NULL || *szFileName == 0)
return ERROR_INVALID_PARAMETER;
if(pdwMaxLocales == NULL)
return ERROR_INVALID_PARAMETER;
// Keep compiler happy
dwSearchScope = dwSearchScope;
// Parse hash table entries for all locales
if(!IsPseudoFileName(szFileName, &dwFileIndex))
{
// Calculate the number of locales
pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
while(pHash != NULL)
{
dwLocales++;
pHash = GetNextHashEntry(ha, pFirstHash, pHash);
}
// Test if there is enough space to copy the locales
if(*pdwMaxLocales < dwLocales)
{
*pdwMaxLocales = dwLocales;
return ERROR_INSUFFICIENT_BUFFER;
}
// Enum the locales
pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
while(pHash != NULL)
{
*plcLocales++ = pHash->lcLocale;
pHash = GetNextHashEntry(ha, pFirstHash, pHash);
}
}
else
{
// There must be space for 1 locale
if(*pdwMaxLocales < 1)
{
*pdwMaxLocales = 1;
return ERROR_INSUFFICIENT_BUFFER;
}
// For nameless access, always return 1 locale
pFileEntry = GetFileEntryByIndex(ha, dwFileIndex);
pHash = ha->pHashTable + pFileEntry->dwHashIndex;
*plcLocales = pHash->lcLocale;
dwLocales = 1;
}
// Give the caller the total number of found locales
*pdwMaxLocales = dwLocales;
return ERROR_SUCCESS;
}
//-----------------------------------------------------------------------------
// SFileHasFile
//
// hMpq - Handle of opened MPQ archive
// szFileName - Name of file to look for
bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
TFileEntry * pFileEntry;
DWORD dwFlagsToCheck = MPQ_FILE_EXISTS;
DWORD dwFileIndex = 0;
char szPatchFileName[MAX_PATH];
bool bIsPseudoName;
int nError = ERROR_SUCCESS;
if(!IsValidMpqHandle(ha))
nError = ERROR_INVALID_HANDLE;
if(szFileName == NULL || *szFileName == 0)
nError = ERROR_INVALID_PARAMETER;
// Prepare the file opening
if(nError == ERROR_SUCCESS)
{
// Different processing for pseudo-names
bIsPseudoName = IsPseudoFileName(szFileName, &dwFileIndex);
// Walk through the MPQ and all patches
while(ha != NULL)
{
// Verify presence of the file
pFileEntry = (bIsPseudoName == false) ? GetFileEntryLocale(ha, szFileName, lcFileLocale)
: GetFileEntryByIndex(ha, dwFileIndex);
// Verify the file flags
if(pFileEntry != NULL && (pFileEntry->dwFlags & dwFlagsToCheck) == MPQ_FILE_EXISTS)
return true;
// 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
nError = ERROR_FILE_NOT_FOUND;
}
// Cleanup
SetLastError(nError);
return false;
}
//-----------------------------------------------------------------------------
// SFileOpenFileEx
//
// hMpq - Handle of opened MPQ archive
// szFileName - Name of file to open
// dwSearchScope - Where to search
// phFile - Pointer to store opened file handle
bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
TFileEntry * pFileEntry = NULL;
TMPQFile * hf = NULL;
DWORD dwFileIndex = 0;
bool bOpenByIndex = false;
int nError = ERROR_SUCCESS;
// Don't accept NULL pointer to file handle
if(phFile == NULL)
nError = ERROR_INVALID_PARAMETER;
// Prepare the file opening
if(nError == ERROR_SUCCESS)
{
switch(dwSearchScope)
{
case SFILE_OPEN_PATCHED_FILE:
// We want to open the updated version of the file
return OpenPatchedFile(hMpq, szFileName, 0, phFile);
case SFILE_OPEN_FROM_MPQ:
if(!IsValidMpqHandle(ha))
{
nError = ERROR_INVALID_HANDLE;
break;
}
if(szFileName == NULL || *szFileName == 0)
{
nError = ERROR_INVALID_PARAMETER;
break;
}
// First of all, check the name as-is
if(!IsPseudoFileName(szFileName, &dwFileIndex))
{
pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale);
if(pFileEntry == NULL)
nError = ERROR_FILE_NOT_FOUND;
}
else
{
bOpenByIndex = true;
pFileEntry = GetFileEntryByIndex(ha, dwFileIndex);
if(pFileEntry == NULL)
nError = ERROR_FILE_NOT_FOUND;
}
break;
case SFILE_OPEN_ANY_LOCALE:
// 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;
case SFILE_OPEN_LOCAL_FILE:
if(szFileName == NULL || *szFileName == 0)
{
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);
return false;
}
}
// Test if the file was not already deleted.
if(nError == ERROR_SUCCESS)
{
if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
nError = ERROR_FILE_NOT_FOUND;
if(pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS)
nError = ERROR_NOT_SUPPORTED;
}
// Allocate file handle
if(nError == ERROR_SUCCESS)
{
if((hf = STORM_ALLOC(TMPQFile, 1)) == 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;
// If we know the real file name, copy it to the file entry
if(bOpenByIndex == false)
{
// If there is no file name yet, allocate it
AllocateFileName(pFileEntry, szFileName);
// If the file is encrypted, we should detect the file key
if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
{
hf->dwFileKey = DecryptFileKey(szFileName,
pFileEntry->ByteOffset,
pFileEntry->dwFileSize,
pFileEntry->dwFlags);
}
}
else
{
// Try to auto-detect the file name
if(!SFileGetFileName(hf, NULL))
nError = GetLastError();
}
}
// 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
if(nError != ERROR_SUCCESS)
{
SetLastError(nError);
FreeMPQFile(hf);
}
*phFile = hf;
return (nError == ERROR_SUCCESS);
}
//-----------------------------------------------------------------------------
// bool WINAPI SFileCloseFile(HANDLE hFile);
bool WINAPI SFileCloseFile(HANDLE hFile)
{
TMPQFile * hf = (TMPQFile *)hFile;
if(!IsValidFileHandle(hf))
{
SetLastError(ERROR_INVALID_HANDLE);
return false;
}
// Free the structure
FreeMPQFile(hf);
return true;
}

View file

@ -0,0 +1,587 @@
/*****************************************************************************/
/* SFilePatchArchives.cpp Copyright (c) Ladislav Zezula 2010 */
/*---------------------------------------------------------------------------*/
/* Description: */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 18.08.10 1.00 Lad The first version of SFilePatchArchives.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
//-----------------------------------------------------------------------------
// Local structures
typedef struct _BLIZZARD_BSDIFF40_FILE
{
ULONGLONG Signature;
ULONGLONG CtrlBlockSize;
ULONGLONG DataBlockSize;
ULONGLONG NewFileSize;
} BLIZZARD_BSDIFF40_FILE, *PBLIZZARD_BSDIFF40_FILE;
//-----------------------------------------------------------------------------
// Local functions
static bool GetDefaultPatchPrefix(
const TCHAR * szBaseMpqName,
char * szBuffer)
{
const TCHAR * szExtension;
const TCHAR * szDash;
// 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;
}
static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE pbCompressed, DWORD cbCompressed)
{
LPBYTE pbDecompressedEnd = pbDecompressed + cbDecompressed;
LPBYTE pbCompressedEnd = pbCompressed + cbCompressed;
BYTE RepeatCount;
BYTE OneByte;
// Cut the initial DWORD from the compressed chunk
pbCompressed += sizeof(DWORD);
cbCompressed -= sizeof(DWORD);
// Pre-fill decompressed buffer with zeros
memset(pbDecompressed, 0, cbDecompressed);
// Unpack
while(pbCompressed < pbCompressedEnd && pbDecompressed < pbDecompressedEnd)
{
OneByte = *pbCompressed++;
// Is it a repetition byte ?
if(OneByte & 0x80)
{
RepeatCount = (OneByte & 0x7F) + 1;
for(BYTE i = 0; i < RepeatCount; i++)
{
if(pbDecompressed == pbDecompressedEnd || pbCompressed == pbCompressedEnd)
break;
*pbDecompressed++ = *pbCompressed++;
}
}
else
{
pbDecompressed += (OneByte + 1);
}
}
}
static int LoadMpqPatch_COPY(TMPQFile * hf, TPatchHeader * pPatchHeader)
{
int nError = ERROR_SUCCESS;
// Allocate space for patch header and compressed data
hf->pPatchHeader = (TPatchHeader *)STORM_ALLOC(BYTE, pPatchHeader->dwSizeOfPatchData);
if(hf->pPatchHeader == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
// Load the patch data and decide if they are compressed or not
if(nError == ERROR_SUCCESS)
{
LPBYTE pbPatchFile = (LPBYTE)hf->pPatchHeader;
// Copy the patch header itself
memcpy(pbPatchFile, pPatchHeader, sizeof(TPatchHeader));
pbPatchFile += sizeof(TPatchHeader);
// Load the rest of the patch
if(!SFileReadFile((HANDLE)hf, pbPatchFile, pPatchHeader->dwSizeOfPatchData - sizeof(TPatchHeader)))
nError = GetLastError();
}
return nError;
}
static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader)
{
LPBYTE pbDecompressed = NULL;
LPBYTE pbCompressed = NULL;
DWORD cbDecompressed = 0;
DWORD cbCompressed = 0;
DWORD dwBytesRead = 0;
int nError = ERROR_SUCCESS;
// Allocate space for compressed data
cbCompressed = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER;
pbCompressed = STORM_ALLOC(BYTE, cbCompressed);
if(pbCompressed == NULL)
nError = ERROR_SUCCESS;
// Read the compressed patch data
if(nError == ERROR_SUCCESS)
{
// Load the rest of the header
SFileReadFile((HANDLE)hf, pbCompressed, cbCompressed, &dwBytesRead);
if(dwBytesRead != cbCompressed)
nError = ERROR_FILE_CORRUPT;
}
// Get the uncompressed size of the patch
if(nError == ERROR_SUCCESS)
{
cbDecompressed = pPatchHeader->dwSizeOfPatchData - sizeof(TPatchHeader);
hf->pPatchHeader = (TPatchHeader *)STORM_ALLOC(BYTE, pPatchHeader->dwSizeOfPatchData);
if(hf->pPatchHeader == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Now decompress the patch data
if(nError == ERROR_SUCCESS)
{
// Copy the patch header
memcpy(hf->pPatchHeader, pPatchHeader, sizeof(TPatchHeader));
pbDecompressed = (LPBYTE)hf->pPatchHeader + sizeof(TPatchHeader);
// Uncompress or copy the patch data
if(cbCompressed < cbDecompressed)
{
Decompress_RLE(pbDecompressed, cbDecompressed, pbCompressed, cbCompressed);
}
else
{
assert(cbCompressed == cbDecompressed);
memcpy(pbDecompressed, pbCompressed, cbCompressed);
}
}
// Free buffers and exit
if(pbCompressed != NULL)
STORM_FREE(pbCompressed);
return nError;
}
static int ApplyMpqPatch_COPY(
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;
// 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;
return ERROR_SUCCESS;
}
static int ApplyMpqPatch_BSD0(
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;
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
// Get pointer to the patch header
// Format of BSDIFF header corresponds to original BSDIFF, which is:
// 0000 8 bytes signature "BSDIFF40"
// 0008 8 bytes size of the control block
// 0010 8 bytes size of the data block
// 0018 8 bytes new size of the patched file
pBsdiff = (PBLIZZARD_BSDIFF40_FILE)pbPatchData;
pbPatchData += sizeof(BLIZZARD_BSDIFF40_FILE);
// Get pointer to the 32-bit BSDIFF control block
// The control block follows immediately after the BSDIFF header
// and consists of three 32-bit integers
// 0000 4 bytes Length to copy from the BSDIFF data block the new file
// 0004 4 bytes Length to copy from the BSDIFF extra block
// 0008 4 bytes Size to increment source file offset
pCtrlBlock = (LPDWORD)pbPatchData;
pbPatchData += (size_t)BSWAP_INT64_UNSIGNED(pBsdiff->CtrlBlockSize);
// Get the pointer to the data block
pDataBlock = (LPBYTE)pbPatchData;
pbPatchData += (size_t)BSWAP_INT64_UNSIGNED(pBsdiff->DataBlockSize);
// Get the pointer to the extra block
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)
{
DWORD dwAddDataLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[0]);
DWORD dwMovDataLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[1]);
DWORD dwOldMoveLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[2]);
DWORD i;
// 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];
dwNewOffset++;
dwOldOffset++;
}
// 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);
pExtraBlock += dwMovDataLength;
dwNewOffset += dwMovDataLength;
// Move the old offset
if(dwOldMoveLength & 0x80000000)
dwOldMoveLength = 0x80000000 - dwOldMoveLength;
dwOldOffset += dwOldMoveLength;
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;
return ERROR_SUCCESS;
}
static int LoadMpqPatch(TMPQFile * hf)
{
TPatchHeader PatchHeader;
DWORD dwBytesRead;
int nError = ERROR_SUCCESS;
// Read the patch header
SFileReadFile((HANDLE)hf, &PatchHeader, sizeof(TPatchHeader), &dwBytesRead);
if(dwBytesRead != sizeof(TPatchHeader))
nError = ERROR_FILE_CORRUPT;
// Verify the signatures in the patch header
if(nError == ERROR_SUCCESS)
{
// BSWAP the entire header, if needed
BSWAP_ARRAY32_UNSIGNED(&PatchHeader, sizeof(DWORD) * 6);
PatchHeader.dwXFRM = BSWAP_INT32_UNSIGNED(PatchHeader.dwXFRM);
PatchHeader.dwXfrmBlockSize = BSWAP_INT32_UNSIGNED(PatchHeader.dwXfrmBlockSize);
PatchHeader.dwPatchType = BSWAP_INT32_UNSIGNED(PatchHeader.dwPatchType);
if(PatchHeader.dwSignature != 0x48435450 || PatchHeader.dwMD5 != 0x5f35444d || PatchHeader.dwXFRM != 0x4d524658)
nError = ERROR_FILE_CORRUPT;
}
// Read the patch, depending on patch type
if(nError == ERROR_SUCCESS)
{
switch(PatchHeader.dwPatchType)
{
case 0x59504f43: // 'COPY'
nError = LoadMpqPatch_COPY(hf, &PatchHeader);
break;
case 0x30445342: // 'BSD0'
nError = LoadMpqPatch_BSD0(hf, &PatchHeader);
break;
default:
nError = ERROR_FILE_CORRUPT;
break;
}
}
return nError;
}
static int ApplyMpqPatch(
TMPQFile * hf,
TPatchHeader * pPatchHeader)
{
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;
}
// Apply the patch
if(nError == ERROR_SUCCESS)
{
switch(pPatchHeader->dwPatchType)
{
case 0x59504f43: // 'COPY'
nError = ApplyMpqPatch_COPY(hf, pPatchHeader);
break;
case 0x30445342: // 'BSD0'
nError = ApplyMpqPatch_BSD0(hf, pPatchHeader);
break;
default:
nError = ERROR_FILE_CORRUPT;
break;
}
}
// Verify MD5 after patch
if(nError == ERROR_SUCCESS && pPatchHeader->dwSizeAfterPatch != 0)
{
// Verify the patched file
if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_after_patch))
nError = ERROR_FILE_CORRUPT;
}
return nError;
}
//-----------------------------------------------------------------------------
// Public functions (StormLib internals)
bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize)
{
TPatchHeader * pPatchHeader = (TPatchHeader *)pvData;
BLIZZARD_BSDIFF40_FILE DiffFile;
DWORD dwPatchType;
if(cbData >= sizeof(TPatchHeader) + sizeof(BLIZZARD_BSDIFF40_FILE))
{
dwPatchType = BSWAP_INT32_UNSIGNED(pPatchHeader->dwPatchType);
if(dwPatchType == 0x30445342)
{
// Give the caller the patch file size
if(pdwPatchedFileSize != NULL)
{
Decompress_RLE((LPBYTE)&DiffFile, sizeof(BLIZZARD_BSDIFF40_FILE), (LPBYTE)(pPatchHeader + 1), sizeof(BLIZZARD_BSDIFF40_FILE));
DiffFile.NewFileSize = BSWAP_INT64_UNSIGNED(DiffFile.NewFileSize);
*pdwPatchedFileSize = (DWORD)DiffFile.NewFileSize;
return true;
}
}
}
return false;
}
int PatchFileData(TMPQFile * hf)
{
TMPQFile * hfBase = hf;
int nError = ERROR_SUCCESS;
// Move to the first patch
hf = hf->hfPatchFile;
// Now go through all patches and patch the original data
while(hf != NULL)
{
// This must be true
assert(hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE);
// Make sure that the patch data is loaded
nError = LoadMpqPatch(hf);
if(nError != ERROR_SUCCESS)
break;
// Apply the patch
nError = ApplyMpqPatch(hfBase, hf->pPatchHeader);
if(nError != ERROR_SUCCESS)
break;
// Move to the next patch
hf = hf->hfPatchFile;
}
return nError;
}
//-----------------------------------------------------------------------------
// Public functions
//
// Patch prefix is the path subdirectory where the patched files are within MPQ.
//
// Example 1:
// Main MPQ: locale-enGB.MPQ
// Patch MPQ: wow-update-12694.MPQ
// File in main MPQ: DBFilesClient\Achievement.dbc
// File in patch MPQ: enGB\DBFilesClient\Achievement.dbc
// Path prefix: enGB
//
// Example 2:
// Main MPQ: expansion1.MPQ
// Patch MPQ: wow-update-12694.MPQ
// File in main MPQ: DBFilesClient\Achievement.dbc
// File in patch MPQ: Base\DBFilesClient\Achievement.dbc
// Path prefix: Base
//
bool WINAPI SFileOpenPatchArchive(
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))
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
//
// Error scenario:
//
// 1) Open archive for writing
// 2) Modify or replace a file
// 3) Add patch archive to the opened MPQ
// 4) Read patched file
// 5) Now what ?
//
if(nError == ERROR_SUCCESS)
{
if(!FileStream_IsReadOnly(ha->pStream))
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))
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);
}
// Now add the patch archive to the list of patches to the original MPQ
while(ha != NULL)
{
if(ha->haPatch == NULL)
{
haPatch->haBase = ha;
ha->haPatch = haPatch;
return true;
}
// Move to the next archive
ha = ha->haPatch;
}
// Should never happen
nError = ERROR_CAN_NOT_COMPLETE;
}
SetLastError(nError);
return false;
}
bool WINAPI SFileIsPatchedArchive(HANDLE hMpq)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
// Verify input parameters
if(!IsValidMpqHandle(ha))
return false;
return (ha->haPatch != NULL);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,921 @@
/*****************************************************************************/
/* SFileVerify.cpp Copyright (c) Ladislav Zezula 2010 */
/*---------------------------------------------------------------------------*/
/* MPQ files and MPQ archives verification. */
/* */
/* The MPQ signature verification has been written by Jean-Francois Roy */
/* <bahamut@macstorm.org> and Justin Olbrantz (Quantam). */
/* The MPQ public keys have been created by MPQKit, using OpenSSL library. */
/* */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 04.05.10 1.00 Lad The first version of SFileVerify.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "StormCommon.h"
//-----------------------------------------------------------------------------
// 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 * szBlizzardWeakPublicKey =
"-----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-----";
static const char * szWarcraft3MapPublicKey =
"-----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-----";
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-----";
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-----";
//-----------------------------------------------------------------------------
// Local functions
static void memrev(unsigned char *buf, size_t count)
{
unsigned char *r;
for (r = buf + count - 1; buf < r; buf++, r--)
{
*buf ^= *r;
*r ^= *buf;
*buf ^= *r;
}
}
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];
const char * szBase64Begin;
const char * szBase64End;
unsigned long decoded_length = sizeof(decoded_key);
unsigned long length;
// Find out the begin of the BASE64 data
szBase64Begin = szKeyBase64 + strlen("-----BEGIN PUBLIC KEY-----");
szBase64End = szBase64Begin + strlen(szBase64Begin) - strlen("-----END PUBLIC KEY-----");
if(szBase64End[0] != '-')
return false;
// decode the base64 string
length = (unsigned long)(szBase64End - szBase64Begin);
if(base64_decode((unsigned char *)szBase64Begin, length, decoded_key, &decoded_length) != CRYPT_OK)
return false;
// Create RSA key
if(rsa_import(decoded_key, decoded_length, key) != CRYPT_OK)
return false;
return true;
}
static void GetPlainAnsiFileName(
const TCHAR * szFileName,
char * szPlainName)
{
const TCHAR * szPlainNameT = GetPlainFileNameT(szFileName);
// Convert the plain name to ANSI
while(*szPlainNameT != 0)
*szPlainName++ = (char)*szPlainNameT++;
*szPlainName = 0;
}
// Calculate begin and end of the MPQ archive
static void CalculateArchiveRange(
TMPQArchive * ha,
PMPQ_SIGNATURE_INFO pSI)
{
ULONGLONG TempPos = 0;
char szMapHeader[0x200];
// Get the MPQ begin
pSI->BeginMpqData = ha->MpqPos;
// Warcraft III maps are signed from the map header to the end
if(FileStream_Read(ha->pStream, &TempPos, szMapHeader, sizeof(szMapHeader)))
{
// Is it a map header ?
if(szMapHeader[0] == 'H' && szMapHeader[1] == 'M' && szMapHeader[2] == '3' && szMapHeader[3] == 'W')
{
// We will have to hash since the map header
pSI->BeginMpqData = 0;
}
}
// Get the MPQ data end. This is stored in our MPQ header,
// and it's been already prepared by SFileOpenArchive,
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);
// 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)
{
hash_state md5_state;
ULONGLONG BeginBuffer;
ULONGLONG EndBuffer;
LPBYTE pbDigestBuffer = NULL;
// Allocate buffer for creating the MPQ digest.
pbDigestBuffer = STORM_ALLOC(BYTE, MPQ_DIGEST_UNIT_SIZE);
if(pbDigestBuffer == NULL)
return false;
// Initialize the MD5 hash state
md5_init(&md5_state);
// Set the byte offset of begin of the data
BeginBuffer = pSI->BeginMpqData;
// Create the digest
for(;;)
{
ULONGLONG BytesRemaining;
LPBYTE pbSigBegin = NULL;
LPBYTE pbSigEnd = NULL;
DWORD dwToRead = MPQ_DIGEST_UNIT_SIZE;
// Check the number of bytes remaining
BytesRemaining = pSI->EndMpqData - BeginBuffer;
if(BytesRemaining < MPQ_DIGEST_UNIT_SIZE)
dwToRead = (DWORD)BytesRemaining;
if(dwToRead == 0)
break;
// Read the next chunk
if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead))
{
STORM_FREE(pbDigestBuffer);
return false;
}
// Move the current byte offset
EndBuffer = BeginBuffer + dwToRead;
// Check if the signature is within the loaded digest
if(BeginBuffer <= pSI->BeginExclude && pSI->BeginExclude < EndBuffer)
pbSigBegin = pbDigestBuffer + (size_t)(pSI->BeginExclude - BeginBuffer);
if(BeginBuffer <= pSI->EndExclude && pSI->EndExclude < EndBuffer)
pbSigEnd = pbDigestBuffer + (size_t)(pSI->EndExclude - BeginBuffer);
// Zero the part that belongs to the signature
if(pbSigBegin != NULL || pbSigEnd != NULL)
{
if(pbSigBegin == NULL)
pbSigBegin = pbDigestBuffer;
if(pbSigEnd == NULL)
pbSigEnd = pbDigestBuffer + dwToRead;
memset(pbSigBegin, 0, (pbSigEnd - pbSigBegin));
}
// Pass the buffer to the hashing function
md5_process(&md5_state, pbDigestBuffer, dwToRead);
// Move pointers
BeginBuffer += dwToRead;
}
// Finalize the MD5 hash
md5_done(&md5_state, pMd5Digest);
STORM_FREE(pbDigestBuffer);
return true;
}
static void AddTailToSha1(
hash_state * psha1_state,
const 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)
{
szUpperCase[nLength++] = (unsigned char)toupper(*szTail++);
}
// Append the tail to the SHA1
sha1_process(psha1_state, szUpperCase, nLength);
}
static bool CalculateMpqHashSha1(
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;
hash_state sha1_state;
LPBYTE pbDigestBuffer = NULL;
char szPlainName[MAX_PATH];
// Allocate buffer for creating the MPQ digest.
pbDigestBuffer = STORM_ALLOC(BYTE, MPQ_DIGEST_UNIT_SIZE);
if(pbDigestBuffer == NULL)
return false;
// Initialize SHA1 state structure
sha1_init(&sha1_state);
// Calculate begin of data to be hashed
BeginBuffer = pSI->BeginMpqData;
// Create the digest
for(;;)
{
ULONGLONG BytesRemaining;
DWORD dwToRead = MPQ_DIGEST_UNIT_SIZE;
// Check the number of bytes remaining
BytesRemaining = pSI->EndMpqData - BeginBuffer;
if(BytesRemaining < MPQ_DIGEST_UNIT_SIZE)
dwToRead = (DWORD)BytesRemaining;
if(dwToRead == 0)
break;
// Read the next chunk
if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead))
{
STORM_FREE(pbDigestBuffer);
return false;
}
// Pass the buffer to the hashing function
sha1_process(&sha1_state, pbDigestBuffer, dwToRead);
// Move pointers
BeginBuffer += dwToRead;
}
// Add all three known tails and generate three hashes
memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state));
sha1_done(&sha1_state_temp, sha1_tail0);
memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state));
GetPlainAnsiFileName(FileStream_GetFileName(ha->pStream), szPlainName);
AddTailToSha1(&sha1_state_temp, szPlainName);
sha1_done(&sha1_state_temp, sha1_tail1);
memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state));
AddTailToSha1(&sha1_state_temp, "ARCHIVE");
sha1_done(&sha1_state_temp, sha1_tail2);
// Finalize the MD5 hash
STORM_FREE(pbDigestBuffer);
return true;
}
static int VerifyRawMpqData(
TMPQArchive * ha,
ULONGLONG ByteOffset,
DWORD dwDataSize)
{
ULONGLONG DataOffset = ha->MpqPos + ByteOffset;
LPBYTE pbDataChunk;
LPBYTE pbMD5Array1; // Calculated MD5 array
LPBYTE pbMD5Array2; // MD5 array loaded from the MPQ
DWORD dwBytesInChunk;
DWORD dwChunkCount;
DWORD dwChunkSize = ha->pHeader->dwRawChunkSize;
DWORD dwMD5Size;
int nError = ERROR_SUCCESS;
// Don't verify zero-sized blocks
if(dwDataSize == 0)
return ERROR_SUCCESS;
// Get the number of data chunks to calculate MD5
assert(dwChunkSize != 0);
dwChunkCount = ((dwDataSize - 1) / dwChunkSize) + 1;
dwMD5Size = dwChunkCount * MD5_DIGEST_SIZE;
// Allocate space for data chunk and for the MD5 array
pbDataChunk = STORM_ALLOC(BYTE, dwChunkSize);
if(pbDataChunk == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
// Allocate space for MD5 array
pbMD5Array1 = STORM_ALLOC(BYTE, dwMD5Size);
pbMD5Array2 = STORM_ALLOC(BYTE, dwMD5Size);
if(pbMD5Array1 == NULL || pbMD5Array2 == NULL)
nError = ERROR_NOT_ENOUGH_MEMORY;
// Calculate MD5 of each data chunk
if(nError == ERROR_SUCCESS)
{
LPBYTE pbMD5 = pbMD5Array1;
for(DWORD i = 0; i < dwChunkCount; i++)
{
// Get the number of bytes in the chunk
dwBytesInChunk = STORMLIB_MIN(dwChunkSize, dwDataSize);
// Read the data chunk
if(!FileStream_Read(ha->pStream, &DataOffset, pbDataChunk, dwBytesInChunk))
{
nError = ERROR_FILE_CORRUPT;
break;
}
// Calculate MD5
CalculateDataBlockHash(pbDataChunk, dwBytesInChunk, pbMD5);
// Move pointers and offsets
DataOffset += dwBytesInChunk;
dwDataSize -= dwBytesInChunk;
pbMD5 += MD5_DIGEST_SIZE;
}
}
// Read the MD5 array
if(nError == ERROR_SUCCESS)
{
// Read the array of MD5
if(!FileStream_Read(ha->pStream, &DataOffset, pbMD5Array2, dwMD5Size))
nError = GetLastError();
}
// Compare the array of MD5
if(nError == ERROR_SUCCESS)
{
// Compare the MD5
if(memcmp(pbMD5Array1, pbMD5Array2, dwMD5Size))
nError = ERROR_FILE_CORRUPT;
}
// Free memory and return result
if(pbMD5Array2 != NULL)
STORM_FREE(pbMD5Array2);
if(pbMD5Array1 != NULL)
STORM_FREE(pbMD5Array1);
if(pbDataChunk != NULL)
STORM_FREE(pbDataChunk);
return nError;
}
static DWORD VerifyWeakSignature(
TMPQArchive * ha,
PMPQ_SIGNATURE_INFO pSI)
{
BYTE RevSignature[MPQ_WEAK_SIGNATURE_SIZE];
BYTE Md5Digest[MD5_DIGEST_SIZE];
rsa_key key;
int hash_idx = find_hash("md5");
int result = 0;
// Calculate hash of the entire archive, skipping the (signature) file
if(!CalculateMpqHashMd5(ha, pSI, Md5Digest))
return ERROR_VERIFY_FAILED;
// Import the Blizzard key in OpenSSL format
if(!decode_base64_key(szBlizzardWeakPublicKey, &key))
return ERROR_VERIFY_FAILED;
// Verify the signature
memcpy(RevSignature, &pSI->Signature[8], MPQ_WEAK_SIGNATURE_SIZE);
memrev(RevSignature, MPQ_WEAK_SIGNATURE_SIZE);
rsa_verify_hash_ex(RevSignature, MPQ_WEAK_SIGNATURE_SIZE, Md5Digest, sizeof(Md5Digest), LTC_LTC_PKCS_1_V1_5, hash_idx, 0, &result, &key);
rsa_free(&key);
// Return the result
return result ? ERROR_WEAK_SIGNATURE_OK : ERROR_WEAK_SIGNATURE_ERROR;
}
static DWORD VerifyStrongSignatureWithKey(
unsigned char * reversed_signature,
unsigned char * padded_digest,
const char * szPublicKey)
{
rsa_key key;
int result = 0;
// Import the Blizzard key in OpenSSL format
if(!decode_base64_key(szPublicKey, &key))
{
assert(false);
return ERROR_VERIFY_FAILED;
}
// Verify the signature
if(rsa_verify_simple(reversed_signature, MPQ_STRONG_SIGNATURE_SIZE, padded_digest, MPQ_STRONG_SIGNATURE_SIZE, &result, &key) != CRYPT_OK)
return ERROR_VERIFY_FAILED;
// Free the key and return result
rsa_free(&key);
return result ? ERROR_STRONG_SIGNATURE_OK : ERROR_STRONG_SIGNATURE_ERROR;
}
static DWORD VerifyStrongSignature(
TMPQArchive * ha,
PMPQ_SIGNATURE_INFO pSI)
{
unsigned char reversed_signature[MPQ_STRONG_SIGNATURE_SIZE];
unsigned char Sha1Digest_tail0[SHA1_DIGEST_SIZE];
unsigned char Sha1Digest_tail1[SHA1_DIGEST_SIZE];
unsigned char Sha1Digest_tail2[SHA1_DIGEST_SIZE];
unsigned char padded_digest[MPQ_STRONG_SIGNATURE_SIZE];
DWORD dwResult;
size_t digest_offset;
// Calculate SHA1 hash of the archive
if(!CalculateMpqHashSha1(ha, pSI, Sha1Digest_tail0, Sha1Digest_tail1, Sha1Digest_tail2))
return ERROR_VERIFY_FAILED;
// Prepare the signature for decryption
memcpy(reversed_signature, &pSI->Signature[4], MPQ_STRONG_SIGNATURE_SIZE);
memrev(reversed_signature, MPQ_STRONG_SIGNATURE_SIZE);
// Prepare the padded digest for comparison
digest_offset = sizeof(padded_digest) - SHA1_DIGEST_SIZE;
memset(padded_digest, 0xbb, digest_offset);
padded_digest[0] = 0x0b;
// Try Blizzard Strong public key with no SHA1 tail
memcpy(padded_digest + digest_offset, Sha1Digest_tail0, SHA1_DIGEST_SIZE);
memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE);
dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szBlizzardStrongPublicKey);
if(dwResult == ERROR_STRONG_SIGNATURE_OK)
return dwResult;
// Try War 3 map public key with plain file name as SHA1 tail
memcpy(padded_digest + digest_offset, Sha1Digest_tail1, SHA1_DIGEST_SIZE);
memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE);
dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szWarcraft3MapPublicKey);
if(dwResult == ERROR_STRONG_SIGNATURE_OK)
return dwResult;
// Try WoW-TBC public key with "ARCHIVE" as SHA1 tail
memcpy(padded_digest + digest_offset, Sha1Digest_tail2, SHA1_DIGEST_SIZE);
memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE);
dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szWowPatchPublicKey);
if(dwResult == ERROR_STRONG_SIGNATURE_OK)
return dwResult;
// Try Survey public key with no SHA1 tail
memcpy(padded_digest + digest_offset, Sha1Digest_tail0, SHA1_DIGEST_SIZE);
memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE);
dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szWowSurveyPublicKey);
if(dwResult == ERROR_STRONG_SIGNATURE_OK)
return dwResult;
// Try Starcraft II public key with no SHA1 tail
memcpy(padded_digest + digest_offset, Sha1Digest_tail0, SHA1_DIGEST_SIZE);
memrev(padded_digest + digest_offset, SHA1_DIGEST_SIZE);
dwResult = VerifyStrongSignatureWithKey(reversed_signature, padded_digest, szStarcraft2MapPublicKey);
if(dwResult == ERROR_STRONG_SIGNATURE_OK)
return dwResult;
return ERROR_STRONG_SIGNATURE_ERROR;
}
static DWORD VerifyFile(
HANDLE hMpq,
const char * szFileName,
LPDWORD pdwCrc32,
char * pMD5,
DWORD dwFlags)
{
hash_state md5_state;
unsigned char * pFileMd5;
unsigned char md5[MD5_DIGEST_SIZE];
TFileEntry * pFileEntry;
TMPQFile * hf;
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;
// If we have to verify raw data MD5, do it before file open
if(dwFlags & SFILE_VERIFY_RAW_MD5)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
// Parse the base MPQ and all patches
while(ha != NULL)
{
// Does the archive have support for raw MD5?
if(ha->pHeader->dwRawChunkSize != 0)
{
// The file has raw MD5 if the archive supports it
dwVerifyResult |= VERIFY_FILE_HAS_RAW_MD5;
// Find file entry for the file
pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale);
if(pFileEntry != NULL)
{
// If the file's raw MD5 doesn't match, don't bother with more checks
if(VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize) != ERROR_SUCCESS)
return dwVerifyResult | VERIFY_FILE_RAW_MD5_ERROR;
}
}
// Move to the next patch
ha = ha->haPatch;
}
}
// Attempt to open the file
if(SFileOpenFileEx(hMpq, szFileName, dwSearchScope, &hFile))
{
// Get the file size
hf = (TMPQFile *)hFile;
pFileEntry = hf->pFileEntry;
dwTotalBytes = SFileGetFileSize(hFile, NULL);
// Initialize the CRC32 and MD5 contexts
md5_init(&md5_state);
dwCrc32 = crc32(0, Z_NULL, 0);
// Also turn on sector checksum verification
if(dwFlags & SFILE_VERIFY_SECTOR_CRC)
hf->bCheckSectorCRCs = true;
// Go through entire file and update both CRC32 and MD5
for(;;)
{
// Read data from file
SFileReadFile(hFile, Buffer, sizeof(Buffer), &dwBytesRead, NULL);
if(dwBytesRead == 0)
{
if(GetLastError() == ERROR_CHECKSUM_ERROR)
dwVerifyResult |= VERIFY_FILE_SECTOR_CRC_ERROR;
break;
}
// Update CRC32 value
if(dwFlags & SFILE_VERIFY_FILE_CRC)
dwCrc32 = crc32(dwCrc32, Buffer, dwBytesRead);
// Update MD5 value
if(dwFlags & SFILE_VERIFY_FILE_MD5)
md5_process(&md5_state, Buffer, dwBytesRead);
// Decrement the total size
dwTotalBytes -= dwBytesRead;
}
// If the file has sector checksums, indicate it in the flags
if(dwFlags & SFILE_VERIFY_SECTOR_CRC)
{
if((hf->pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) && hf->SectorChksums != NULL && hf->SectorChksums[0] != 0)
dwVerifyResult |= VERIFY_FILE_HAS_SECTOR_CRC;
}
// Check if the entire file has been read
// No point in checking CRC32 and MD5 if not
// Skip checksum checks if the file has patches
if(dwTotalBytes == 0)
{
// Check CRC32 and MD5 only if there is no patches
if(hf->hfPatchFile == NULL)
{
// Check if the CRC32 matches.
if(dwFlags & SFILE_VERIFY_FILE_CRC)
{
// Only check the CRC32 if it is valid
if(pFileEntry->dwCrc32 != 0)
{
dwVerifyResult |= VERIFY_FILE_HAS_CHECKSUM;
if(dwCrc32 != pFileEntry->dwCrc32)
dwVerifyResult |= VERIFY_FILE_CHECKSUM_ERROR;
}
}
// Check if MD5 matches
if(dwFlags & SFILE_VERIFY_FILE_MD5)
{
// Patch files have their MD5 saved in the patch info
pFileMd5 = (hf->pPatchInfo != NULL) ? hf->pPatchInfo->md5 : pFileEntry->md5;
md5_done(&md5_state, md5);
// Only check the MD5 if it is valid
if(is_valid_md5(pFileMd5))
{
dwVerifyResult |= VERIFY_FILE_HAS_MD5;
if(memcmp(md5, pFileMd5, MD5_DIGEST_SIZE))
dwVerifyResult |= VERIFY_FILE_MD5_ERROR;
}
}
}
else
{
// Patched files are MD5-checked automatically
dwVerifyResult |= VERIFY_FILE_HAS_MD5;
}
}
else
{
dwVerifyResult |= VERIFY_READ_ERROR;
}
SFileCloseFile(hFile);
}
else
{
// Remember that the file couldn't be open
dwVerifyResult |= VERIFY_OPEN_ERROR;
}
// If the caller required CRC32 and/or MD5, give it to him
if(pdwCrc32 != NULL)
*pdwCrc32 = dwCrc32;
if(pMD5 != NULL)
memcpy(pMD5, md5, MD5_DIGEST_SIZE);
return dwVerifyResult;
}
//-----------------------------------------------------------------------------
// Public (exported) functions
bool WINAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWORD pdwCrc32, char * pMD5)
{
DWORD dwVerifyResult;
DWORD dwVerifyFlags = 0;
if(pdwCrc32 != NULL)
dwVerifyFlags |= SFILE_VERIFY_FILE_CRC;
if(pMD5 != NULL)
dwVerifyFlags |= SFILE_VERIFY_FILE_MD5;
dwVerifyResult = VerifyFile(hMpq,
szFileName,
pdwCrc32,
pMD5,
dwVerifyFlags);
// If verification failed, return zero
if(dwVerifyResult & VERIFY_FILE_ERROR_MASK)
{
SetLastError(ERROR_FILE_CORRUPT);
return false;
}
return true;
}
DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags)
{
return VerifyFile(hMpq,
szFileName,
NULL,
NULL,
dwFlags);
}
// Verifies raw data of the archive Only works for MPQs version 4 or newer
int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * szFileName)
{
TMPQArchive * ha = (TMPQArchive *)hMpq;
TFileEntry * pFileEntry;
TMPQHeader * pHeader;
// Verify input parameters
if(!IsValidMpqHandle(ha))
return ERROR_INVALID_PARAMETER;
pHeader = ha->pHeader;
// If the archive doesn't have raw data MD5, report it as OK
if(pHeader->dwRawChunkSize == 0)
return ERROR_SUCCESS;
// If we have to verify MPQ header, do it
switch(dwWhatToVerify)
{
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;
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;
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;
case SFILE_VERIFY_HASH_TABLE:
// Hash table is not protected by MD5
return ERROR_SUCCESS;
case SFILE_VERIFY_BLOCK_TABLE:
// Block table is not protected by MD5
return ERROR_SUCCESS;
case SFILE_VERIFY_HIBLOCK_TABLE:
// It is unknown if the hi-block table is protected my MD5 or not.
return ERROR_SUCCESS;
case SFILE_VERIFY_FILE:
// 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;
return VerifyRawMpqData(ha, pFileEntry->ByteOffset, pFileEntry->dwCmpSize);
}
return ERROR_INVALID_PARAMETER;
}
// Verifies the archive against the signature
DWORD WINAPI SFileVerifyArchive(HANDLE hMpq)
{
MPQ_SIGNATURE_INFO si;
TMPQArchive * ha = (TMPQArchive *)hMpq;
// Verify input parameters
if(!IsValidMpqHandle(ha))
return ERROR_VERIFY_FAILED;
// 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:
return ERROR_NO_SIGNATURE;
case SIGNATURE_TYPE_WEAK:
return VerifyWeakSignature(ha, &si);
case SIGNATURE_TYPE_STRONG:
return VerifyStrongSignature(ha, &si);
}
return ERROR_VERIFY_FAILED;
}

View file

@ -0,0 +1,274 @@
/*****************************************************************************/
/* SCommon.h Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* Common functions for encryption/decryption from Storm.dll. Included by */
/* SFile*** functions, do not include and do not use this file directly */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 24.03.03 1.00 Lad The first version of SFileCommon.h */
/* 12.06.04 1.00 Lad Renamed to SCommon.h */
/* 06.09.10 1.00 Lad Renamed to StormCommon.h */
/*****************************************************************************/
#ifndef __STORMCOMMON_H__
#define __STORMCOMMON_H__
//-----------------------------------------------------------------------------
// Compression support
// Include functions from Pkware Data Compression Library
#include "pklib/pklib.h"
// Include functions from Huffmann compression
#include "huffman/huff.h"
// Include functions from IMA ADPCM compression
#include "adpcm/adpcm.h"
// Include functions from SPARSE compression
#include "sparse/sparse.h"
// Include functions from LZMA compression
#include "lzma/C/LzmaEnc.h"
#include "lzma/C/LzmaDec.h"
// Include functions from zlib
#ifndef __SYS_ZLIB
#include "zlib/zlib.h"
#else
#include <zlib.h>
#endif
// Include functions from bzlib
#ifndef __SYS_BZLIB
#include "bzip2/bzlib.h"
#else
#include <bzlib.h>
#endif
//-----------------------------------------------------------------------------
// Cryptography support
// Headers from LibTomCrypt
#include "libtomcrypt/src/headers/tomcrypt.h"
// For HashStringJenkins
#include "jenkins/lookup.h"
//-----------------------------------------------------------------------------
// StormLib private defines
#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)
// Macro for building 64-bit file offset from two 32-bit
#define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | lo)
//-----------------------------------------------------------------------------
// Memory management
//
// We use our own macros for allocating/freeing memory. If you want
// 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.
//
#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);
}
__inline void DebugFree(void * ptr)
{
// delete [] ptr;
HeapFree(GetProcessHeap(), 0, 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
//-----------------------------------------------------------------------------
// StormLib internal global variables
extern LCID lcFileLocale; // Preferred file locale
//-----------------------------------------------------------------------------
// Encryption and decryption functions
#define MPQ_HASH_TABLE_INDEX 0x000
#define MPQ_HASH_NAME_A 0x100
#define MPQ_HASH_NAME_B 0x200
#define MPQ_HASH_FILE_KEY 0x300
DWORD HashString(const char * szFileName, DWORD dwHashType);
void InitializeMpqCryptography();
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(TMPQArchive * ha, DWORD dwFileSize);
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 DecryptFileKey(const char * szFileName, ULONGLONG MpqPos, DWORD dwFileSize, DWORD dwFlags);
bool IsValidMD5(LPBYTE pbMd5);
bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5);
void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash);
//-----------------------------------------------------------------------------
// Handle validation functions
bool IsValidMpqHandle(TMPQArchive * ha);
bool IsValidFileHandle(TMPQFile * hf);
//-----------------------------------------------------------------------------
// Hash table and block table manipulation
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);
void FindFreeMpqSpace(TMPQArchive * ha, ULONGLONG * pFreeSpacePos);
// Functions that loads and verifies MPQ data bitmap
int LoadMpqDataBitmap(TMPQArchive * ha, ULONGLONG FileSize, bool * pbFileIsComplete);
// 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 SaveMPQTables(TMPQArchive * ha);
TMPQHetTable * CreateHetTable(DWORD dwMaxFileCount, DWORD dwHashBitSize, bool bCreateEmpty);
void FreeHetTable(TMPQHetTable * pHetTable);
TMPQBetTable * CreateBetTable(DWORD dwMaxFileCount);
void FreeBetTable(TMPQBetTable * pBetTable);
// Functions for finding files in the file table
TFileEntry * GetFileEntryAny(TMPQArchive * ha, const char * szFileName);
TFileEntry * GetFileEntryLocale(TMPQArchive * ha, const char * szFileName, LCID lcLocale);
TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID lcLocale);
TFileEntry * GetFileEntryByIndex(TMPQArchive * ha, DWORD dwIndex);
// Allocates file name in the file entry
void AllocateFileName(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);
// Invalidates entries for (listfile) and (attributes)
void InvalidateInternalFiles(TMPQArchive * ha);
//-----------------------------------------------------------------------------
// Common functions - MPQ File
TMPQFile * CreateMpqFile(TMPQArchive * ha);
int LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, void * pvTable, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey);
int AllocateSectorBuffer(TMPQFile * hf);
int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile);
int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile);
int AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile);
void CalculateRawSectorOffset(ULONGLONG & RawFilePos, TMPQFile * hf, DWORD dwSectorOffset);
int WritePatchInfo(TMPQFile * hf);
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);
bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize);
int PatchFileData(TMPQFile * hf);
void FreeMPQArchive(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);
//-----------------------------------------------------------------------------
// 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
);
int SFileAddFile_Write(
TMPQFile * hf,
const void * pvData,
DWORD dwSize,
DWORD dwCompression
);
int SFileAddFile_Finish(
TMPQFile * hf
);
//-----------------------------------------------------------------------------
// Attributes support
int SAttrLoadAttributes(TMPQArchive * ha);
int SAttrFileSaveToMpq(TMPQArchive * ha);
//-----------------------------------------------------------------------------
// Listfile functions
int SListFileSaveToMpq(TMPQArchive * ha);
//-----------------------------------------------------------------------------
// Dump data support
#ifdef __STORMLIB_DUMP_DATA__
void DumpMpqHeader(TMPQHeader * pHeader);
void DumpHetAndBetTable(TMPQHetTable * pHetTable, TMPQBetTable * pBetTable);
#else
#define DumpMpqHeader(h) /* */
#define DumpHetAndBetTable(h, b) /* */
#endif
#endif // __STORMCOMMON_H__

984
dep/StormLib/src/StormLib.h Normal file
View file

@ -0,0 +1,984 @@
/*****************************************************************************/
/* StormLib.h Copyright (c) Ladislav Zezula 1999-2010 */
/*---------------------------------------------------------------------------*/
/* StormLib library v 7.02 */
/* */
/* Author : Ladislav Zezula */
/* E-mail : ladik@zezula.net */
/* WWW : http://www.zezula.net */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* xx.xx.99 1.00 Lad Created */
/* 24.03.03 2.50 Lad Version 2.50 */
/* 02.04.03 3.00 Lad Version 3.00 with compression */
/* 11.04.03 3.01 Lad Renamed to StormLib.h for compatibility with */
/* original headers for Storm.dll */
/* 10.05.03 3.02 Lad Added Pkware DCL compression */
/* 26.05.03 4.00 Lad Completed all compressions */
/* 18.06.03 4.01 Lad Added SFileSetFileLocale */
/* Added SFileExtractFile */
/* 26.07.03 4.02 Lad Implemented nameless rename and delete */
/* 26.07.03 4.03 Lad Added support for protected MPQs */
/* 28.08.03 4.10 Lad Fixed bugs that caused StormLib incorrectly work */
/* with Diablo I savegames and with files having full */
/* hash table */
/* 08.12.03 4.11 DCH Fixed bug in reading file sector larger than 0x1000 */
/* on certain files. */
/* Fixed bug in AddFile with MPQ_FILE_REPLACE_EXISTING */
/* (Thanx Daniel Chiamarello, dchiamarello@madvawes.com)*/
/* 21.12.03 4.50 Lad Completed port for Mac */
/* Fixed bug in compacting (if fsize is mul of 0x1000) */
/* Fixed bug in SCompCompress */
/* 27.05.04 4.51 Lad Changed memory management from new/delete to our */
/* own macros */
/* 22.06.04 4.60 Lad Optimized search. Support for multiple listfiles. */
/* 30.09.04 4.61 Lad Fixed some bugs (Aaargh !!!) */
/* Correctly works if HashTableSize > BlockTableSize */
/* 29.12.04 4.70 Lad Fixed compatibility problem with MPQs from WoW */
/* 14.07.05 5.00 Lad Added the BZLIB compression support */
/* Added suport of files stored as single unit */
/* 17.04.06 5.01 Lad Converted to MS Visual Studio 8.0 */
/* Fixed issue with protected Warcraft 3 protected maps */
/* 15.05.06 5.02 Lad Fixed issue with WoW 1.10+ */
/* 07.09.06 5.10 Lad Fixed processing files longer than 2GB */
/* 22.11.06 6.00 Lad Support for MPQ archives V2 */
/* 12.06.07 6.10 Lad Support for (attributes) file */
/* 10.09.07 6.12 Lad Support for MPQs protected by corrupting hash table */
/* 03.12.07 6.13 Lad Support for MPQs with hash tbl size > block tbl size */
/* 07.04.08 6.20 Lad Added SFileFlushArchive */
/* 09.04.08 Lad Removed FilePointer variable from MPQ handle */
/* structure, as it caused more problems than benefits */
/* 12.05.08 6.22 Lad Support for w3xMaster map protector */
/* 05.10.08 6.23 Lad Support for protectors who set negative values in */
/* the table of file blocks */
/* 26.05.09 6.24 Lad Fixed search for multiple lang files with deleted */
/* entries */
/* 03.09.09 6.25 Lad Fixed decompression bug in huffmann decompression */
/* 22.03.10 6.50 Lad New compressions in Starcraft II (LZMA, sparse) */
/* Fixed compacting MPQs that contain single unit files */
/* 26.04.10 7.00 Lad Major rewrite */
/* 08.06.10 7.10 Lad Support for partial MPQs */
/* 08.07.10 7.11 Lad Support for MPQs v 3.0 */
/* 20.08.10 7.20 Lad Support for opening multiple MPQs in patch mode */
/* 20.09.10 8.00 Lad MPQs v 4, HET and BET tables */
/* 07.01.11 8.01 Lad Write support for MPQs v 3 and 4 */
/* 15.09.11 8.04 Lad Bug fixes, testing for Diablo III MPQs */
/* 26.04.12 8.10 Lad Support for data map, added SFileGetArchiveBitmap */
/*****************************************************************************/
#ifndef __STORMLIB_H__
#define __STORMLIB_H__
#ifdef _MSC_VER
#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
#pragma warning(disable:4820) // 'XXX' : '2' bytes padding added after data member 'XXX::yyy'
#endif
#include "StormPort.h"
#ifdef __cplusplus
extern "C" {
#endif
//-----------------------------------------------------------------------------
// Use the apropriate library
//
// The library type is encoded in the library name as the following
// StormLibXYZ.lib
//
// X - D for Debug version, R for Release version
// Y - A for ANSI version, U for Unicode version
// Z - S for static-linked CRT library, D for multithreaded DLL CRT library
//
#if 0 && defined(_MSC_VER) && !defined(__STORMLIB_SELF__)
#ifdef _DEBUG // DEBUG VERSIONS
#ifndef _UNICODE
#ifdef _DLL
#pragma comment(lib, "StormLibDAD.lib") // Debug Ansi CRT-DLL version
#else
#pragma comment(lib, "StormLibDAS.lib") // Debug Ansi CRT-LIB version
#endif
#else
#ifdef _DLL
#pragma comment(lib, "StormLibDUD.lib") // Debug Unicode CRT-DLL version
#else
#pragma comment(lib, "StormLibDUS.lib") // Debug Unicode CRT-LIB version
#endif
#endif
#else // RELEASE VERSIONS
#ifndef _UNICODE
#ifdef _DLL
#pragma comment(lib, "StormLibRAD.lib") // Release Ansi CRT-DLL version
#else
#pragma comment(lib, "StormLibRAS.lib") // Release Ansi CRT-LIB version
#endif
#else
#ifdef _DLL
#pragma comment(lib, "StormLibRUD.lib") // Release Unicode CRT-DLL version
#else
#pragma comment(lib, "StormLibRUS.lib") // Release Unicode CRT-LIB version
#endif
#endif
#endif
#endif
//-----------------------------------------------------------------------------
// Defines
#define ID_MPQ 0x1A51504D // MPQ archive header ID ('MPQ\x1A')
#define ID_MPQ_USERDATA 0x1B51504D // MPQ userdata entry ('MPQ\x1B')
#define ERROR_AVI_FILE 10000 // No MPQ file, but AVI file.
#define ERROR_UNKNOWN_FILE_KEY 10001 // Returned by SFileReadFile when can't find file key
#define ERROR_CHECKSUM_ERROR 10002 // Returned by SFileReadFile when sector CRC doesn't match
#define ERROR_INTERNAL_FILE 10003 // The given operation is not allowed on internal file
#define ERROR_BASE_FILE_MISSING 10004 // The file is present as incremental patch file, but base file is missing
#define ERROR_MARKED_FOR_DELETE 10005 // The file was marked as "deleted" in the MPQ
// Values for SFileCreateArchive
#define HASH_TABLE_SIZE_MIN 0x00000004 // Minimum acceptable hash table size
#define HASH_TABLE_SIZE_DEFAULT 0x00001000 // Default hash table size for empty MPQs
#define HASH_TABLE_SIZE_MAX 0x00080000 // Maximum acceptable hash table size
#define HASH_ENTRY_DELETED 0xFFFFFFFE // Block index for deleted entry in the hash table
#define HASH_ENTRY_FREE 0xFFFFFFFF // Block index for free entry in the hash table
#define HET_ENTRY_DELETED 0x80 // HET hash value for a deleted entry
#define HET_ENTRY_FREE 0x00 // HET hash value for free entry
#define HASH_STATE_SIZE 0x60 // Size of LibTomCrypt's hash_state structure
#define MPQ_PATCH_PREFIX_LEN 0x20 // Maximum length of the patch prefix
// Values for SFileOpenArchive
#define SFILE_OPEN_HARD_DISK_FILE 2 // Open the archive on HDD
#define SFILE_OPEN_CDROM_FILE 3 // Open the archive only if it is on CDROM
// Values for SFileOpenFile
#define SFILE_OPEN_FROM_MPQ 0x00000000 // Open the file from the MPQ archive
#define SFILE_OPEN_PATCHED_FILE 0x00000001 // Open the file from the MPQ archive
#define SFILE_OPEN_ANY_LOCALE 0xFFFFFFFE // Reserved for StormLib internal use
#define SFILE_OPEN_LOCAL_FILE 0xFFFFFFFF // Open a local file
// Flags for TMPQArchive::dwFlags
#define MPQ_FLAG_READ_ONLY 0x00000001 // If set, the MPQ has been open for read-only access
#define MPQ_FLAG_CHANGED 0x00000002 // If set, the MPQ tables have been changed
#define MPQ_FLAG_PROTECTED 0x00000004 // Set on protected MPQs (like W3M maps)
#define MPQ_FLAG_CHECK_SECTOR_CRC 0x00000008 // Checking sector CRC when reading files
#define MPQ_FLAG_NEED_FIX_SIZE 0x00000010 // Used during opening the archive
#define MPQ_FLAG_INV_LISTFILE 0x00000020 // If set, it means that the (listfile) has been invalidated
#define MPQ_FLAG_INV_ATTRIBUTES 0x00000040 // If set, it means that the (attributes) has been invalidated
// Return value for SFileGetFileSize and SFileSetFilePointer
#define SFILE_INVALID_SIZE 0xFFFFFFFF
#define SFILE_INVALID_POS 0xFFFFFFFF
#define SFILE_INVALID_ATTRIBUTES 0xFFFFFFFF
// Flags for SFileAddFile
#define MPQ_FILE_IMPLODE 0x00000100 // Implode method (By PKWARE Data Compression Library)
#define MPQ_FILE_COMPRESS 0x00000200 // Compress methods (By multiple methods)
#define MPQ_FILE_COMPRESSED 0x0000FF00 // File is compressed
#define MPQ_FILE_ENCRYPTED 0x00010000 // Indicates whether file is encrypted
#define MPQ_FILE_FIX_KEY 0x00020000 // File decryption key has to be fixed
#define MPQ_FILE_PATCH_FILE 0x00100000 // The file is a patch file. Raw file data begin with TPatchInfo structure
#define MPQ_FILE_SINGLE_UNIT 0x01000000 // File is stored as a single unit, rather than split into sectors (Thx, Quantam)
#define MPQ_FILE_DELETE_MARKER 0x02000000 // File is a deletion marker. Used in MPQ patches, indicating that the file no longer exists.
#define MPQ_FILE_SECTOR_CRC 0x04000000 // File has checksums for each sector.
// Ignored if file is not compressed or imploded.
#define MPQ_FILE_EXISTS 0x80000000 // Set if file exists, reset when the file was deleted
#define MPQ_FILE_REPLACEEXISTING 0x80000000 // Replace when the file exist (SFileAddFile)
#define MPQ_FILE_VALID_FLAGS (MPQ_FILE_IMPLODE | \
MPQ_FILE_COMPRESS | \
MPQ_FILE_ENCRYPTED | \
MPQ_FILE_FIX_KEY | \
MPQ_FILE_PATCH_FILE | \
MPQ_FILE_SINGLE_UNIT | \
MPQ_FILE_DELETE_MARKER | \
MPQ_FILE_SECTOR_CRC | \
MPQ_FILE_EXISTS)
// Compression types for multiple compressions
#define MPQ_COMPRESSION_HUFFMANN 0x01 // Huffmann compression (used on WAVE files only)
#define MPQ_COMPRESSION_ZLIB 0x02 // ZLIB compression
#define MPQ_COMPRESSION_PKWARE 0x08 // PKWARE DCL compression
#define MPQ_COMPRESSION_BZIP2 0x10 // BZIP2 compression (added in Warcraft III)
#define MPQ_COMPRESSION_SPARSE 0x20 // Sparse compression (added in Starcraft 2)
#define MPQ_COMPRESSION_ADPCM_MONO 0x40 // IMA ADPCM compression (mono)
#define MPQ_COMPRESSION_ADPCM_STEREO 0x80 // IMA ADPCM compression (stereo)
#define MPQ_COMPRESSION_LZMA 0x12 // LZMA compression. Added in Starcraft 2. This value is NOT a combination of flags.
#define MPQ_COMPRESSION_NEXT_SAME 0xFFFFFFFF // Same compression
// Constants for SFileAddWave
#define MPQ_WAVE_QUALITY_HIGH 0 // Best quality, the worst compression
#define MPQ_WAVE_QUALITY_MEDIUM 1 // Medium quality, medium compression
#define MPQ_WAVE_QUALITY_LOW 2 // Low quality, the best compression
// Signatures for HET and BET table
#define HET_TABLE_SIGNATURE 0x1A544548 // 'HET\x1a'
#define BET_TABLE_SIGNATURE 0x1A544542 // 'BET\x1a'
// Decryption keys for MPQ tables
#define MPQ_KEY_HASH_TABLE 0xC3AF3770 // Obtained by HashString("(hash table)", MPQ_HASH_FILE_KEY)
#define MPQ_KEY_BLOCK_TABLE 0xEC83B3A3 // Obtained by HashString("(block table)", MPQ_HASH_FILE_KEY)
// Block map defines
#define MPQ_DATA_BITMAP_SIGNATURE 0x33767470 // Signature of the MPQ data bitmap ('ptv3')
// Constants for SFileGetFileInfo
#define SFILE_INFO_ARCHIVE_NAME 1 // MPQ size (value from header)
#define SFILE_INFO_ARCHIVE_SIZE 2 // MPQ size (value from header)
#define SFILE_INFO_MAX_FILE_COUNT 3 // Max number of files in the MPQ
#define SFILE_INFO_HASH_TABLE_SIZE 4 // Size of hash table, in entries
#define SFILE_INFO_BLOCK_TABLE_SIZE 5 // Number of entries in the block table
#define SFILE_INFO_SECTOR_SIZE 6 // Size of file sector (in bytes)
#define SFILE_INFO_HASH_TABLE 7 // Pointer to Hash table (TMPQHash *)
#define SFILE_INFO_BLOCK_TABLE 8 // Pointer to Block Table (TMPQBlock *)
#define SFILE_INFO_NUM_FILES 9 // Real number of files within archive
#define SFILE_INFO_STREAM_FLAGS 10 // Stream flags for the MPQ. See STREAM_FLAG_XXX
#define SFILE_INFO_IS_READ_ONLY 11 // TRUE of the MPQ was open as read only
//------
#define SFILE_INFO_HASH_INDEX 100 // Hash index of file in MPQ
#define SFILE_INFO_CODENAME1 101 // The first codename of the file
#define SFILE_INFO_CODENAME2 102 // The second codename of the file
#define SFILE_INFO_LOCALEID 103 // Locale ID of file in MPQ
#define SFILE_INFO_BLOCKINDEX 104 // Index to Block Table
#define SFILE_INFO_FILE_SIZE 105 // Original file size (from the block table)
#define SFILE_INFO_COMPRESSED_SIZE 106 // Compressed file size (from the block table)
#define SFILE_INFO_FLAGS 107 // File flags
#define SFILE_INFO_POSITION 108 // File position within archive
#define SFILE_INFO_KEY 109 // File decryption key
#define SFILE_INFO_KEY_UNFIXED 110 // Decryption key not fixed to file pos and size
#define SFILE_INFO_FILETIME 111 // TMPQFileTime
#define SFILE_INFO_PATCH_CHAIN 112 // Chain of patches
#define LISTFILE_NAME "(listfile)" // Name of internal listfile
#define SIGNATURE_NAME "(signature)" // Name of internal signature
#define ATTRIBUTES_NAME "(attributes)" // Name of internal attributes file
#define PATCH_METADATA_NAME "(patch_metadata)"
#define STORMLIB_VERSION 0x080A // Current version of StormLib (8.10)
#define STORMLIB_VERSION_STRING "8.10"
#define MPQ_FORMAT_VERSION_1 0 // Up to The Burning Crusade
#define MPQ_FORMAT_VERSION_2 1 // The Burning Crusade and newer
#define MPQ_FORMAT_VERSION_3 2 // WoW Cataclysm Beta
#define MPQ_FORMAT_VERSION_4 3 // WoW Cataclysm and newer
// Flags for MPQ attributes
#define MPQ_ATTRIBUTE_CRC32 0x00000001 // The "(attributes)" contains CRC32 for each file
#define MPQ_ATTRIBUTE_FILETIME 0x00000002 // The "(attributes)" contains file time for each file
#define MPQ_ATTRIBUTE_MD5 0x00000004 // The "(attributes)" contains MD5 for each file
#define MPQ_ATTRIBUTE_PATCH_BIT 0x00000008 // The "(attributes)" contains a patch bit for each file
#define MPQ_ATTRIBUTE_ALL 0x0000000F // Summary mask
#define MPQ_ATTRIBUTES_V1 100 // (attributes) format version 1.00
// Flags for SFileOpenArchive
#define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file
#define BASE_PROVIDER_MAP 0x00000001 // Base data source is memory-mapped file
#define BASE_PROVIDER_HTTP 0x00000002 // Base data source is a file on web server
#define BASE_PROVIDER_MASK 0x0000000F // Mask for base provider value
#define STREAM_PROVIDER_LINEAR 0x00000000 // Stream is linear with no offset mapping
#define STREAM_PROVIDER_PARTIAL 0x00000010 // Stream is partial file (.part)
#define STREAM_PROVIDER_ENCRYPTED 0x00000020 // Stream is an encrypted MPQ
#define STREAM_PROVIDER_MASK 0x000000F0 // Mask for stream provider value
#define STREAM_FLAG_READ_ONLY 0x00000100 // Stream is read only
#define STREAM_FLAG_WRITE_SHARE 0x00000200 // Allow write sharing when open for write
#define STREAM_FLAG_MASK 0x0000FF00 // Mask for stream flags
#define STREAM_OPTIONS_MASK 0x0000FFFF // Mask for all stream options
#define MPQ_OPEN_NO_LISTFILE 0x00010000 // Don't load the internal listfile
#define MPQ_OPEN_NO_ATTRIBUTES 0x00020000 // Don't open the attributes
#define MPQ_OPEN_FORCE_MPQ_V1 0x00040000 // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header
#define MPQ_OPEN_CHECK_SECTOR_CRC 0x00080000 // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file
// Deprecated
#define MPQ_OPEN_READ_ONLY STREAM_FLAG_READ_ONLY
#define MPQ_OPEN_ENCRYPTED STREAM_PROVIDER_ENCRYPTED
// Flags for SFileCreateArchive
#define MPQ_CREATE_ATTRIBUTES 0x00100000 // Also add the (attributes) file
#define MPQ_CREATE_ARCHIVE_V1 0x00000000 // Creates archive of version 1 (size up to 4GB)
#define MPQ_CREATE_ARCHIVE_V2 0x01000000 // Creates archive of version 2 (larger than 4 GB)
#define MPQ_CREATE_ARCHIVE_V3 0x02000000 // Creates archive of version 3
#define MPQ_CREATE_ARCHIVE_V4 0x03000000 // Creates archive of version 4
#define MPQ_CREATE_ARCHIVE_VMASK 0x0F000000 // Mask for archive version
#define FLAGS_TO_FORMAT_SHIFT 24 // (MPQ_CREATE_ARCHIVE_V4 >> FLAGS_TO_FORMAT_SHIFT) => MPQ_FORMAT_VERSION_4
// Flags for SFileVerifyFile
#define SFILE_VERIFY_SECTOR_CRC 0x00000001 // Verify sector checksum for the file, if available
#define SFILE_VERIFY_FILE_CRC 0x00000002 // Verify file CRC, if available
#define SFILE_VERIFY_FILE_MD5 0x00000004 // Verify file MD5, if available
#define SFILE_VERIFY_RAW_MD5 0x00000008 // Verify raw file MD5, if available
#define SFILE_VERIFY_ALL 0x0000000F // Verify every checksum possible
// Return values for SFileVerifyFile
#define VERIFY_OPEN_ERROR 0x0001 // Failed to open the file
#define VERIFY_READ_ERROR 0x0002 // Failed to read all data from the file
#define VERIFY_FILE_HAS_SECTOR_CRC 0x0004 // File has sector CRC
#define VERIFY_FILE_SECTOR_CRC_ERROR 0x0008 // Sector CRC check failed
#define VERIFY_FILE_HAS_CHECKSUM 0x0010 // File has CRC32
#define VERIFY_FILE_CHECKSUM_ERROR 0x0020 // CRC32 check failed
#define VERIFY_FILE_HAS_MD5 0x0040 // File has data MD5
#define VERIFY_FILE_MD5_ERROR 0x0080 // MD5 check failed
#define VERIFY_FILE_HAS_RAW_MD5 0x0100 // File has raw data MD5
#define VERIFY_FILE_RAW_MD5_ERROR 0x0200 // Raw MD5 check failed
#define VERIFY_FILE_ERROR_MASK (VERIFY_OPEN_ERROR | VERIFY_READ_ERROR | VERIFY_FILE_SECTOR_CRC_ERROR | VERIFY_FILE_CHECKSUM_ERROR | VERIFY_FILE_MD5_ERROR | VERIFY_FILE_RAW_MD5_ERROR)
// Flags for SFileVerifyRawData (for MPQs version 4.0 or higher)
#define SFILE_VERIFY_MPQ_HEADER 0x0001 // Verify raw MPQ header
#define SFILE_VERIFY_HET_TABLE 0x0002 // Verify raw data of the HET table
#define SFILE_VERIFY_BET_TABLE 0x0003 // Verify raw data of the BET table
#define SFILE_VERIFY_HASH_TABLE 0x0004 // Verify raw data of the hash table
#define SFILE_VERIFY_BLOCK_TABLE 0x0005 // Verify raw data of the block table
#define SFILE_VERIFY_HIBLOCK_TABLE 0x0006 // Verify raw data of the hi-block table
#define SFILE_VERIFY_FILE 0x0007 // Verify raw data of a file
// Return values for SFileVerifyArchive
#define ERROR_NO_SIGNATURE 0 // There is no signature in the MPQ
#define ERROR_VERIFY_FAILED 1 // There was an error during verifying signature (like no memory)
#define ERROR_WEAK_SIGNATURE_OK 2 // There is a weak signature and sign check passed
#define ERROR_WEAK_SIGNATURE_ERROR 3 // There is a weak signature but sign check failed
#define ERROR_STRONG_SIGNATURE_OK 4 // There is a strong signature and sign check passed
#define ERROR_STRONG_SIGNATURE_ERROR 5 // There is a strong signature but sign check failed
#ifndef MD5_DIGEST_SIZE
#define MD5_DIGEST_SIZE 0x10
#endif
#ifndef SHA1_DIGEST_SIZE
#define SHA1_DIGEST_SIZE 0x14 // 160 bits
#endif
#ifndef LANG_NEUTRAL
#define LANG_NEUTRAL 0x00 // Neutral locale
#endif
//-----------------------------------------------------------------------------
// Callback functions
// Values for compact callback
#define CCB_CHECKING_FILES 1 // Checking archive (dwParam1 = current, dwParam2 = total)
#define CCB_CHECKING_HASH_TABLE 2 // Checking hash table (dwParam1 = current, dwParam2 = total)
#define CCB_COPYING_NON_MPQ_DATA 3 // Copying non-MPQ data: No params used
#define CCB_COMPACTING_FILES 4 // Compacting archive (dwParam1 = current, dwParam2 = total)
#define CCB_CLOSING_ARCHIVE 5 // Closing archive: No params used
typedef void (WINAPI * SFILE_ADDFILE_CALLBACK)(void * pvUserData, DWORD dwBytesWritten, DWORD dwTotalBytes, bool bFinalCall);
typedef void (WINAPI * SFILE_COMPACT_CALLBACK)(void * pvUserData, DWORD dwWorkType, ULONGLONG BytesProcessed, ULONGLONG TotalBytes);
struct TFileStream;
//-----------------------------------------------------------------------------
// Structure for bit arrays used for HET and BET tables
struct TBitArray
{
void GetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize);
void SetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize);
DWORD NumberOfBits; // Total number of bits that are available
BYTE Elements[1]; // Array of elements (variable length)
};
// Structure for file bitmap. Used by SFileGetArchiveBitmap
struct TFileBitmap
{
ULONGLONG StartOffset; // Starting offset of the file, covered by bitmap
ULONGLONG EndOffset; // Ending offset of the file, covered by bitmap
DWORD IsComplete; // If nonzero, no blocks are missing
DWORD BitmapSize; // Size of the file bitmap (in bytes)
DWORD BlockSize; // Size of one block, in bytes
DWORD Reserved; // Alignment
// Followed by file bitmap (variable length), array of BYTEs)
};
//-----------------------------------------------------------------------------
// Structures related to MPQ format
//
// Note: All structures in this header file are supposed to remain private
// to StormLib. The structures may (and will) change over time, as the MPQ
// file format evolves. Programmers directly using these structures need to
// be aware of this. And the last, but not least, NEVER do any modifications
// to those structures directly, always use SFile* functions.
//
#define MPQ_HEADER_SIZE_V1 0x20
#define MPQ_HEADER_SIZE_V2 0x2C
#define MPQ_HEADER_SIZE_V3 0x44
#define MPQ_HEADER_SIZE_V4 0xD0
struct TMPQUserData
{
// The ID_MPQ_USERDATA ('MPQ\x1B') signature
DWORD dwID;
// Maximum size of the user data
DWORD cbUserDataSize;
// Offset of the MPQ header, relative to the begin of this header
DWORD dwHeaderOffs;
// Appears to be size of user data header (Starcraft II maps)
DWORD cbUserDataHeader;
};
// MPQ file header
//
// We have to make sure that the header is packed OK.
// Reason: A 64-bit integer at the beginning of 3.0 part,
// which is offset 0x2C
#pragma pack(push, 1)
struct TMPQHeader
{
// The ID_MPQ ('MPQ\x1A') signature
DWORD dwID;
// Size of the archive header
DWORD dwHeaderSize;
// 32-bit size of MPQ archive
// This field is deprecated in the Burning Crusade MoPaQ format, and the size of the archive
// is calculated as the size from the beginning of the archive to the end of the hash table,
// block table, or hi-block table (whichever is largest).
DWORD dwArchiveSize;
// 0 = Format 1 (up to The Burning Crusade)
// 1 = Format 2 (The Burning Crusade and newer)
// 2 = Format 3 (WoW - Cataclysm beta or newer)
// 3 = Format 4 (WoW - Cataclysm beta or newer)
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;
// 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;
//-- MPQ HEADER v 2 -------------------------------------------
// Offset to the beginning of array of 16-bit high parts of file offsets.
ULONGLONG HiBlockTablePos64;
// High 16 bits of the hash table offset for large archives.
USHORT wHashTablePosHi;
// High 16 bits of the block table offset for large archives.
USHORT wBlockTablePosHi;
//-- MPQ HEADER v 3 -------------------------------------------
// 64-bit version of the archive size
ULONGLONG ArchiveSize64;
// 64-bit position of the BET table
ULONGLONG BetTablePos64;
// 64-bit position of the HET table
ULONGLONG HetTablePos64;
//-- MPQ HEADER v 4 -------------------------------------------
// Compressed size of the hash table
ULONGLONG HashTableSize64;
// Compressed size of the block table
ULONGLONG BlockTableSize64;
// Compressed size of the hi-block table
ULONGLONG HiBlockTableSize64;
// Compressed size of the HET block
ULONGLONG HetTableSize64;
// Compressed size of the BET block
ULONGLONG BetTableSize64;
// Size of raw data chunk to calculate MD5.
// MD5 of each data chunk follows the raw file data.
DWORD dwRawChunkSize;
// MD5 of MPQ tables
unsigned char MD5_BlockTable[MD5_DIGEST_SIZE]; // MD5 of the block table before decryption
unsigned char MD5_HashTable[MD5_DIGEST_SIZE]; // MD5 of the hash table before decryption
unsigned char MD5_HiBlockTable[MD5_DIGEST_SIZE]; // MD5 of the hi-block table
unsigned char MD5_BetTable[MD5_DIGEST_SIZE]; // MD5 of the BET table before decryption
unsigned char MD5_HetTable[MD5_DIGEST_SIZE]; // MD5 of the HET table before decryption
unsigned char MD5_MpqHeader[MD5_DIGEST_SIZE]; // MD5 of the MPQ header from signature to (including) MD5_HetTable
};
#pragma pack(pop)
// Hash entry. All files in the archive are searched by their hashes.
struct TMPQHash
{
// The hash of the file path, using method A.
DWORD dwName1;
// The hash of the file path, using method B.
DWORD dwName2;
#ifdef PLATFORM_LITTLE_ENDIAN
// The language of the file. This is a Windows LANGID data type, and uses the same values.
// 0 indicates the default language (American English), or that the file is language-neutral.
USHORT lcLocale;
// The platform the file is used for. 0 indicates the default platform.
// No other values have been observed.
// Note: wPlatform is actually just BYTE, but since it has never been used, we don't care.
USHORT wPlatform;
#else
USHORT wPlatform;
USHORT lcLocale;
#endif
// 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;
};
// File description block contains informations about the file
struct TMPQBlock
{
// Offset of the beginning of the file, relative to the beginning of the archive.
DWORD dwFilePos;
// Compressed file size
DWORD dwCSize;
// Only valid if the block is a file; otherwise meaningless, and should be 0.
// If the file is compressed, this is the size of the uncompressed file data.
DWORD dwFSize;
// Flags for the file. See MPQ_FILE_XXXX constants
DWORD dwFlags;
};
// Patch file information, preceding the sector offset table
struct TPatchInfo
{
DWORD dwLength; // Length of patch info header, in bytes
DWORD dwFlags; // Flags. 0x80000000 = MD5 (?)
DWORD dwDataSize; // Uncompressed size of the patch file
BYTE md5[0x10]; // MD5 of the entire patch file after decompression
// Followed by the sector table (variable length)
};
// Header for PTCH files
struct TPatchHeader
{
//-- PATCH header -----------------------------------
DWORD dwSignature; // 'PTCH'
DWORD dwSizeOfPatchData; // Size of the entire patch (decompressed)
DWORD dwSizeBeforePatch; // Size of the file before patch
DWORD dwSizeAfterPatch; // Size of file after patch
//-- MD5 block --------------------------------------
DWORD dwMD5; // 'MD5_'
DWORD dwMd5BlockSize; // Size of the MD5 block, including the signature and size itself
BYTE md5_before_patch[0x10]; // MD5 of the original (unpached) file
BYTE md5_after_patch[0x10]; // MD5 of the patched file
//-- XFRM block -------------------------------------
DWORD dwXFRM; // 'XFRM'
DWORD dwXfrmBlockSize; // Size of the XFRM block, includes XFRM header and patch data
DWORD dwPatchType; // Type of patch ('BSD0' or 'COPY')
// Followed by the patch data
};
#define SIZE_OF_XFRM_HEADER 0x0C
// This is the combined file entry for maintaining file list in the MPQ.
// This structure is combined from block table, hi-block table,
// (attributes) file and from (listfile).
struct TFileEntry
{
ULONGLONG ByteOffset; // Position of the file content in the MPQ, relative to the MPQ header
ULONGLONG FileTime; // FileTime from the (attributes) file. 0 if not present.
ULONGLONG BetHash; // Lower part of the file name hash. Only used when the MPQ has BET table.
DWORD dwHashIndex; // Index to the hash table. Only used when the MPQ has classic hash table
DWORD dwHetIndex; // Index to the HET table. Only used when the MPQ has HET table
DWORD dwFileSize; // Decompressed size of the file
DWORD dwCmpSize; // Compressed size of the file (i.e., size of the file data in the MPQ)
DWORD dwFlags; // File flags (from block table)
USHORT lcLocale; // Locale ID for the file
USHORT wPlatform; // Platform ID for the file
DWORD dwCrc32; // CRC32 from (attributes) file. 0 if not present.
unsigned char md5[MD5_DIGEST_SIZE]; // File MD5 from the (attributes) file. 0 if not present.
char * szFileName; // File name. NULL if not known.
};
// Common header for HET and BET tables
struct TMPQExtTable
{
DWORD dwSignature; // 'HET\x1A' or 'BET\x1A'
DWORD dwVersion; // Version. Seems to be always 1
DWORD dwDataSize; // Size of the contained table
// Followed by the table header
// Followed by the table data
};
//
// MPQ data bitmap, can be found at (FileSize - sizeof(TMPQBlockMap))
//
// There is bit map of the entire MPQ before TMPQBitmap. Each 0x4000-byte
// block is represented by one bit (including the last, eventually incomplete block).
//
struct TMPQBitmap
{
DWORD dwSignature; // 'ptv3' (MPQ_BLOCK_MAP_SIGNATURE)
DWORD dwAlways3; // Unknown, seems to always have value of 3
DWORD dwBuildNumber; // Game build number for that MPQ
DWORD dwMapOffsetLo; // Low 32-bits of the offset of the bit map
DWORD dwMapOffsetHi; // High 32-bits of the offset of the bit map
DWORD dwBlockSize; // Size of one block (usually 0x4000 bytes)
};
// Structure for parsed HET table
struct TMPQHetTable
{
TBitArray * pBetIndexes; // Bit array of indexes to BET tables
LPBYTE pHetHashes; // Array of HET hashes. Each entry has size of 1 byte
ULONGLONG AndMask64; // AND mask used for calculating file name hash
ULONGLONG OrMask64; // OR mask used for setting the highest bit of the file name hash
DWORD dwIndexSizeTotal; // Total size of one entry in pBetIndexes (in bits)
DWORD dwIndexSizeExtra; // Extra bits in the entry in pBetIndexes
DWORD dwIndexSize; // Effective size of one entry in pBetIndexes (in bits)
DWORD dwMaxFileCount; // Maximum number of files in the MPQ
DWORD dwHashTableSize; // Number of entries in pBetHashes
DWORD dwHashBitSize; // Effective number of bits in the hash
};
// Structure for parsed BET table
struct TMPQBetTable
{
TBitArray * pBetHashes; // Array of BET hashes
TBitArray * pFileTable; // Bit-based file table
LPDWORD pFileFlags; // Array of file flags
DWORD dwTableEntrySize; // Size of one table entry, in bits
DWORD dwBitIndex_FilePos; // Bit index of the file position in the table entry
DWORD dwBitIndex_FileSize; // Bit index of the file size in the table entry
DWORD dwBitIndex_CmpSize; // Bit index of the compressed size in the table entry
DWORD dwBitIndex_FlagIndex; // Bit index of the flag index in the table entry
DWORD dwBitIndex_Unknown; // Bit index of ??? in the table entry
DWORD dwBitCount_FilePos; // Size of file offset (in bits) within table entry
DWORD dwBitCount_FileSize; // Size of file size (in bits) within table entry
DWORD dwBitCount_CmpSize; // Size of compressed file size (in bits) within table entry
DWORD dwBitCount_FlagIndex; // Size of flag index (in bits) within table entry
DWORD dwBitCount_Unknown; // Size of ??? (in bits) within table entry
DWORD dwBetHashSizeTotal; // Total size of bet hash
DWORD dwBetHashSizeExtra; // Extra bits in the bet hash
DWORD dwBetHashSize; // Effective size of the bet hash
DWORD dwFileCount; // Number of files (usually equal to maximum number of files)
DWORD dwFlagCount; // Number of entries in pFileFlags
};
// Archive handle structure
struct TMPQArchive
{
TFileStream * pStream; // Open stream for the MPQ
ULONGLONG UserDataPos; // Position of user data (relative to the begin of the file)
ULONGLONG MpqPos; // MPQ header offset (relative to the begin of the file)
TMPQArchive * haPatch; // Pointer to patch archive, if any
TMPQArchive * haBase; // Pointer to base ("previous version") archive, if any
char szPatchPrefix[MPQ_PATCH_PREFIX_LEN]; // Prefix for file names in patch MPQs
size_t cchPatchPrefix; // Length of the patch prefix, in characters
TMPQUserData * pUserData; // MPQ user data (NULL if not present in the file)
TMPQHeader * pHeader; // MPQ file header
TMPQBitmap * pBitmap; // MPQ bitmap
TMPQHash * pHashTable; // Hash table
TMPQHetTable * pHetTable; // Het table
TFileEntry * pFileTable; // File table
TMPQUserData UserData; // MPQ user data. Valid only when ID_MPQ_USERDATA has been found
BYTE HeaderData[MPQ_HEADER_SIZE_V4]; // Storage for MPQ header
DWORD dwHETBlockSize;
DWORD dwBETBlockSize;
DWORD dwFileTableSize; // Current size of the file table, e.g. index of the entry past the last occupied one
DWORD dwMaxFileCount; // Maximum number of files in the MPQ
DWORD dwSectorSize; // Default size of one file sector
DWORD dwFileFlags1; // Flags for (listfile)
DWORD dwFileFlags2; // Flags for (attributes)
DWORD dwAttrFlags; // Flags for the (attributes) file, see MPQ_ATTRIBUTE_XXX
DWORD dwFlags; // See MPQ_FLAG_XXXXX
};
// File handle structure
struct TMPQFile
{
TFileStream * pStream; // File stream. Only used on local files
TMPQArchive * ha; // Archive handle
TFileEntry * pFileEntry; // File entry for the file
DWORD dwFileKey; // Decryption key
DWORD dwFilePos; // Current file position
ULONGLONG RawFilePos; // Offset in MPQ archive (relative to file begin)
ULONGLONG MpqFilePos; // Offset in MPQ archive (relative to MPQ header)
DWORD dwMagic; // 'FILE'
TMPQFile * hfPatchFile; // Pointer to opened patch file
TPatchHeader * pPatchHeader; // Patch header. Only used if the file is a patch file
LPBYTE pbFileData; // Loaded and patched file data. Only used if the file is a patch file
DWORD cbFileData; // Size of loaded patched data
TPatchInfo * pPatchInfo; // Patch info block, preceding the sector table
DWORD * SectorOffsets; // Position of each file sector, relative to the begin of the file. Only for compressed files.
DWORD * SectorChksums; // Array of sector checksums (either ADLER32 or MD5) values for each file sector
DWORD dwSectorCount; // Number of sectors in the file
DWORD dwPatchedFileSize; // Size of patched file. Used when saving patch file to the MPQ
DWORD dwDataSize; // Size of data in the file (on patch files, this differs from file size in block table entry)
LPBYTE pbFileSector; // Last loaded file sector. For single unit files, entire file content
DWORD dwSectorOffs; // File position of currently loaded file sector
DWORD dwSectorSize; // Size of the file sector. For single unit files, this is equal to the file size
unsigned char hctx[HASH_STATE_SIZE];// Hash state for MD5. Used when saving file to MPQ
DWORD dwCrc32; // CRC32 value, used when saving file to MPQ
bool bLoadedSectorCRCs; // If true, we already tried to load sector CRCs
bool bCheckSectorCRCs; // If true, then SFileReadFile will check sector CRCs when reading the file
bool bIsWriteHandle; // If true, this handle has been created by SFileCreateFile
bool bErrorOccured; // If true, then at least one error occured during saving the file to the archive
};
// Structure for SFileFindFirstFile and SFileFindNextFile
typedef struct _SFILE_FIND_DATA
{
char cFileName[MAX_PATH]; // Full name of the found file
char * szPlainName; // Plain name of the found file
DWORD dwHashIndex; // Hash table index for the file
DWORD dwBlockIndex; // Block table index for the file
DWORD dwFileSize; // File size in bytes
DWORD dwFileFlags; // MPQ file flags
DWORD dwCompSize; // Compressed file size
DWORD dwFileTimeLo; // Low 32-bits of the file time (0 if not present)
DWORD dwFileTimeHi; // High 32-bits of the file time (0 if not present)
LCID lcLocale; // Locale version
} SFILE_FIND_DATA, *PSFILE_FIND_DATA;
typedef struct _SFILE_CREATE_MPQ
{
DWORD cbSize; // Size of this structure, in bytes
DWORD dwMpqVersion; // Version of the MPQ to be created
void *pvUserData; // Reserved, must be NULL
DWORD cbUserData; // Reserved, must be 0
DWORD dwStreamFlags; // Stream flags for creating the MPQ
DWORD dwFileFlags1; // File flags for (listfile). 0 = default
DWORD dwFileFlags2; // File flags for (attributes). 0 = default
DWORD dwAttrFlags; // Flags for the (attributes) file. If 0, no attributes will be created
DWORD dwSectorSize; // Sector size for compressed files
DWORD dwRawChunkSize; // Size of raw data chunk
DWORD dwMaxFileCount; // File limit for the MPQ
} SFILE_CREATE_MPQ, *PSFILE_CREATE_MPQ;
//-----------------------------------------------------------------------------
// Stream support - functions
TFileStream * FileStream_CreateFile(const TCHAR * szFileName, DWORD dwStreamFlags);
TFileStream * FileStream_OpenFile(const TCHAR * szFileName, DWORD dwStreamFlags);
TCHAR * FileStream_GetFileName(TFileStream * pStream);
bool FileStream_IsReadOnly(TFileStream * pStream);
bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead);
bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite);
bool FileStream_GetPos(TFileStream * pStream, ULONGLONG & ByteOffset);
bool FileStream_SetPos(TFileStream * pStream, ULONGLONG ByteOffset);
bool FileStream_GetSize(TFileStream * pStream, ULONGLONG & FileSize);
bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize);
bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFT);
bool FileStream_Switch(TFileStream * pStream, TFileStream * pTempStream);
bool FileStream_SetBitmap(TFileStream * pStream, TFileBitmap * pBitmap);
bool FileStream_GetBitmap(TFileStream * pStream, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded);
void FileStream_Close(TFileStream * pStream);
//-----------------------------------------------------------------------------
// Functions prototypes for Storm.dll
// Typedefs for functions exported by Storm.dll
typedef LCID (WINAPI * SFILESETLOCALE)(LCID);
typedef bool (WINAPI * SFILEOPENARCHIVE)(const char *, DWORD, DWORD, HANDLE *);
typedef bool (WINAPI * SFILECLOSEARCHIVE)(HANDLE);
typedef bool (WINAPI * SFILEOPENFILEEX)(HANDLE, const char *, DWORD, HANDLE *);
typedef bool (WINAPI * SFILECLOSEFILE)(HANDLE);
typedef DWORD (WINAPI * SFILEGETFILESIZE)(HANDLE, LPDWORD);
typedef DWORD (WINAPI * SFILESETFILEPOINTER)(HANDLE, LONG, LONG *, DWORD);
typedef bool (WINAPI * SFILEREADFILE)(HANDLE, void *, DWORD, LPDWORD, LPOVERLAPPED);
//-----------------------------------------------------------------------------
// Functions for manipulation with StormLib global flags
LCID WINAPI SFileGetLocale();
LCID WINAPI SFileSetLocale(LCID lcNewLocale);
//-----------------------------------------------------------------------------
// Functions for archive manipulation
bool WINAPI SFileOpenArchive(const TCHAR * szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE * phMpq);
bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwFlags, DWORD dwMaxFileCount, HANDLE * phMpq);
bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq);
bool WINAPI SFileGetArchiveBitmap(HANDLE hMpq, TFileBitmap * pBitmap, DWORD Length, LPDWORD LengthNeeded);
bool WINAPI SFileFlushArchive(HANDLE hMpq);
bool WINAPI SFileCloseArchive(HANDLE hMpq);
// Adds another listfile into MPQ. The currently added listfile(s) remain,
// so you can use this API to combining more listfiles.
// Note that this function is internally called by SFileFindFirstFile
int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile);
// Archive compacting
bool WINAPI SFileSetCompactCallback(HANDLE hMpq, SFILE_COMPACT_CALLBACK CompactCB, void * pvData);
bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile = NULL, bool bReserved = 0);
// Changing the maximum file count
DWORD WINAPI SFileGetMaxFileCount(HANDLE hMpq);
bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount);
// Changing (attributes) file
DWORD WINAPI SFileGetAttributes(HANDLE hMpq);
bool WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags);
bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName);
//-----------------------------------------------------------------------------
// Functions for manipulation with patch archives
bool WINAPI SFileOpenPatchArchive(HANDLE hMpq, const TCHAR * szPatchMpqName, const char * szPatchPathPrefix, DWORD dwFlags);
bool WINAPI SFileIsPatchedArchive(HANDLE hMpq);
//-----------------------------------------------------------------------------
// Functions for file manipulation
// Reading from MPQ file
bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile);
DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh = NULL);
DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod);
bool WINAPI SFileReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, LPDWORD pdwRead = NULL, LPOVERLAPPED lpOverlapped = NULL);
bool WINAPI SFileCloseFile(HANDLE hFile);
// Retrieving info about the file
bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName);
bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName);
bool WINAPI SFileGetFileInfo(HANDLE hMpqOrFile, DWORD dwInfoType, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded = NULL);
// High-level extract function
bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope = SFILE_OPEN_FROM_MPQ);
//-----------------------------------------------------------------------------
// Functions for file and archive verification
// Generates file CRC32
bool WINAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWORD pdwCrc32, char * pMD5);
// Verifies file against its checksums stored in (attributes) attributes (depending on dwFlags).
// For dwFlags, use one or more of MPQ_ATTRIBUTE_MD5
DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags);
// Verifies raw data of the archive. Only works for MPQs version 4 or newer
int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * szFileName);
// Verifies the signature, if present
DWORD WINAPI SFileVerifyArchive(HANDLE hMpq);
//-----------------------------------------------------------------------------
// Functions for file searching
HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const char * szListFile);
bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData);
bool WINAPI SFileFindClose(HANDLE hFind);
HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData);
bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData);
bool WINAPI SListFileFindClose(HANDLE hFind);
// Locale support
int WINAPI SFileEnumLocales(HANDLE hMpq, const char * szFileName, LCID * plcLocales, LPDWORD pdwMaxLocales, DWORD dwSearchScope);
//-----------------------------------------------------------------------------
// Support for adding files to the MPQ
bool WINAPI SFileCreateFile(HANDLE hMpq, const char * szArchivedName, ULONGLONG FileTime, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, HANDLE * phFile);
bool WINAPI SFileWriteFile(HANDLE hFile, const void * pvData, DWORD dwSize, DWORD dwCompression);
bool WINAPI SFileFinishFile(HANDLE hFile);
bool WINAPI SFileAddFileEx(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwCompression, DWORD dwCompressionNext = MPQ_COMPRESSION_NEXT_SAME);
bool WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags);
bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality);
bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope = SFILE_OPEN_FROM_MPQ);
bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szOldFileName, const char * szNewFileName);
bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale);
bool WINAPI SFileSetDataCompression(DWORD DataCompression);
bool WINAPI SFileSetAddFileCallback(HANDLE hMpq, SFILE_ADDFILE_CALLBACK AddFileCB, void * pvData);
//-----------------------------------------------------------------------------
// Compression and decompression
int WINAPI SCompImplode (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer);
int WINAPI SCompExplode (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer);
int WINAPI SCompCompress (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel);
int WINAPI SCompDecompress (char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer);
int WINAPI SCompDecompress2(char * pbOutBuffer, int * pcbOutBuffer, char * pbInBuffer, int cbInBuffer);
//-----------------------------------------------------------------------------
// Non-Windows support for SetLastError/GetLastError
#ifndef PLATFORM_WINDOWS
void SetLastError(int err);
int GetLastError();
#endif
//-----------------------------------------------------------------------------
// Functions from Storm.dll. They use slightly different names for keeping
// possibility to use them together with StormLib (StormXXX instead of SFileXXX)
#ifdef __LINK_STORM_DLL__
#define STORM_ALTERNATE_NAMES // Force storm_dll.h to use alternate fnc names
#include "..\storm_dll\storm_dll.h"
#endif // __LINK_STORM_DLL__
#ifdef __cplusplus
} // extern "C"
#endif
#endif // __STORMLIB_H__

View file

@ -0,0 +1,235 @@
/*****************************************************************************/
/* StormPort.h Copyright (c) Marko Friedemann 2001 */
/*---------------------------------------------------------------------------*/
/* Portability module for the StormLib library. Contains a wrapper symbols */
/* to make the compilation under Linux work */
/* */
/* Author: Marko Friedemann <marko.friedemann@bmx-chemnitz.de> */
/* Created at: Mon Jan 29 18:26:01 CEST 2001 */
/* Computer: whiplash.flachland-chemnitz.de */
/* System: Linux 2.4.0 on i686 */
/* */
/* Author: Sam Wilkins <swilkins1337@gmail.com> */
/* System: Mac OS X and port to big endian processor */
/* */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 29.01.01 1.00 Mar Created */
/* 24.03.03 1.01 Lad Some cosmetic changes */
/* 12.11.03 1.02 Dan Macintosh compatibility */
/* 24.07.04 1.03 Sam Mac OS X compatibility */
/* 22.11.06 1.04 Sam Mac OS X compatibility (for StormLib 6.0) */
/* 31.12.06 1.05 XPinguin Full GNU/Linux compatibility */
/*****************************************************************************/
#ifndef __STORMPORT_H__
#define __STORMPORT_H__
#ifndef __cplusplus
#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
#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
#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>
#define PKEXPORT
#define __SYS_ZLIB
#define __SYS_BZLIB
#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>
#define PLATFORM_LITTLE_ENDIAN
#define PLATFORM_LINUX
#define PLATFORM_DEFINED
#endif
// Definition of Windows-specific structures for non-Windows platforms
#ifndef PLATFORM_WINDOWS
#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;
#ifdef PLATFORM_32BIT
#define _LZMA_UINT32_IS_ULONG
#endif
// Some Windows-specific defines
#ifndef MAX_PATH
#define MAX_PATH 1024
#endif
#define WINAPI
#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 _stricmp strcasecmp
#define _strnicmp strncasecmp
#define _tcsnicmp strncasecmp
#endif // !WIN32
// 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
#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
#endif
#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) {}
#else
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);
#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))
#endif
#endif // __STORMPORT_H__

View 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);
}

View 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,282 @@
/*-------------------------------------------------------------*/
/*--- Public header file for the library. ---*/
/*--- bzlib.h ---*/
/*-------------------------------------------------------------*/
/* ------------------------------------------------------------------
This file is part of bzip2/libbzip2, a program and library for
lossless, block-sorting data compression.
bzip2/libbzip2 version 1.0.5 of 10 December 2007
Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
Please read the WARNING, DISCLAIMER and PATENTS sections in the
README file.
This program is released under the terms of the license contained
in the file LICENSE.
------------------------------------------------------------------ */
#ifndef _BZLIB_H
#define _BZLIB_H
#ifdef __cplusplus
extern "C" {
#endif
#define BZ_RUN 0
#define BZ_FLUSH 1
#define BZ_FINISH 2
#define BZ_OK 0
#define BZ_RUN_OK 1
#define BZ_FLUSH_OK 2
#define BZ_FINISH_OK 3
#define BZ_STREAM_END 4
#define BZ_SEQUENCE_ERROR (-1)
#define BZ_PARAM_ERROR (-2)
#define BZ_MEM_ERROR (-3)
#define BZ_DATA_ERROR (-4)
#define BZ_DATA_ERROR_MAGIC (-5)
#define BZ_IO_ERROR (-6)
#define BZ_UNEXPECTED_EOF (-7)
#define BZ_OUTBUFF_FULL (-8)
#define BZ_CONFIG_ERROR (-9)
typedef
struct {
char *next_in;
unsigned int avail_in;
unsigned int total_in_lo32;
unsigned int total_in_hi32;
char *next_out;
unsigned int avail_out;
unsigned int total_out_lo32;
unsigned int total_out_hi32;
void *state;
void *(*bzalloc)(void *,int,int);
void (*bzfree)(void *,void *);
void *opaque;
}
bz_stream;
#ifndef BZ_IMPORT
#define BZ_EXPORT
#endif
#ifndef BZ_NO_STDIO
/* Need a definitition for FILE */
#include <stdio.h>
#endif
#ifdef _WIN32
# include <windows.h>
# ifdef small
/* windows.h define small to char */
# undef small
# endif
# ifdef BZ_EXPORT
# define BZ_API(func) WINAPI func
# define BZ_EXTERN extern
# else
/* import windows dll dynamically */
# define BZ_API(func) (WINAPI * func)
# define BZ_EXTERN
# endif
#else
# define BZ_API(func) func
# define BZ_EXTERN extern
#endif
/*-- Core (low-level) library functions --*/
BZ_EXTERN int BZ_API(BZ2_bzCompressInit) (
bz_stream* strm,
int blockSize100k,
int verbosity,
int workFactor
);
BZ_EXTERN int BZ_API(BZ2_bzCompress) (
bz_stream* strm,
int action
);
BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) (
bz_stream* strm
);
BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) (
bz_stream *strm,
int verbosity,
int small
);
BZ_EXTERN int BZ_API(BZ2_bzDecompress) (
bz_stream* strm
);
BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) (
bz_stream *strm
);
/*-- High(er) level library functions --*/
#ifndef BZ_NO_STDIO
#define BZ_MAX_UNUSED 5000
typedef void BZFILE;
BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) (
int* bzerror,
FILE* f,
int verbosity,
int small,
void* unused,
int nUnused
);
BZ_EXTERN void BZ_API(BZ2_bzReadClose) (
int* bzerror,
BZFILE* b
);
BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) (
int* bzerror,
BZFILE* b,
void** unused,
int* nUnused
);
BZ_EXTERN int BZ_API(BZ2_bzRead) (
int* bzerror,
BZFILE* b,
void* buf,
int len
);
BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) (
int* bzerror,
FILE* f,
int blockSize100k,
int verbosity,
int workFactor
);
BZ_EXTERN void BZ_API(BZ2_bzWrite) (
int* bzerror,
BZFILE* b,
void* buf,
int len
);
BZ_EXTERN void BZ_API(BZ2_bzWriteClose) (
int* bzerror,
BZFILE* b,
int abandon,
unsigned int* nbytes_in,
unsigned int* nbytes_out
);
BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) (
int* bzerror,
BZFILE* b,
int abandon,
unsigned int* nbytes_in_lo32,
unsigned int* nbytes_in_hi32,
unsigned int* nbytes_out_lo32,
unsigned int* nbytes_out_hi32
);
#endif
/*-- Utility functions --*/
BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) (
char* dest,
unsigned int* destLen,
char* source,
unsigned int sourceLen,
int blockSize100k,
int verbosity,
int workFactor
);
BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) (
char* dest,
unsigned int* destLen,
char* source,
unsigned int sourceLen,
int small,
int verbosity
);
/*--
Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp)
to support better zlib compatibility.
This code is not _officially_ part of libbzip2 (yet);
I haven't tested it, documented it, or considered the
threading-safeness of it.
If this code breaks, please contact both Yoshioka and me.
--*/
BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) (
void
);
#ifndef BZ_NO_STDIO
BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) (
const char *path,
const char *mode
);
BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) (
int fd,
const char *mode
);
BZ_EXTERN int BZ_API(BZ2_bzread) (
BZFILE* b,
void* buf,
int len
);
BZ_EXTERN int BZ_API(BZ2_bzwrite) (
BZFILE* b,
void* buf,
int len
);
BZ_EXTERN int BZ_API(BZ2_bzflush) (
BZFILE* b
);
BZ_EXTERN void BZ_API(BZ2_bzclose) (
BZFILE* b
);
BZ_EXTERN const char * BZ_API(BZ2_bzerror) (
BZFILE *b,
int *errnum
);
#endif
#ifdef __cplusplus
}
#endif
#endif
/*-------------------------------------------------------------*/
/*--- end bzlib.h ---*/
/*-------------------------------------------------------------*/

View file

@ -0,0 +1,509 @@
/*-------------------------------------------------------------*/
/*--- Private header file for the library. ---*/
/*--- bzlib_private.h ---*/
/*-------------------------------------------------------------*/
/* ------------------------------------------------------------------
This file is part of bzip2/libbzip2, a program and library for
lossless, block-sorting data compression.
bzip2/libbzip2 version 1.0.5 of 10 December 2007
Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
Please read the WARNING, DISCLAIMER and PATENTS sections in the
README file.
This program is released under the terms of the license contained
in the file LICENSE.
------------------------------------------------------------------ */
#ifndef _BZLIB_PRIVATE_H
#define _BZLIB_PRIVATE_H
#include <stdlib.h>
#ifndef BZ_NO_STDIO
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#endif
#include "bzlib.h"
/*-- General stuff. --*/
#define BZ_VERSION "1.0.5, 10-Dec-2007"
typedef char Char;
typedef unsigned char Bool;
typedef unsigned char UChar;
typedef int Int32;
typedef unsigned int UInt32;
typedef short Int16;
typedef unsigned short UInt16;
#define True ((Bool)1)
#define False ((Bool)0)
#ifndef __GNUC__
#define __inline__ /* */
#endif
#ifndef BZ_NO_STDIO
extern void BZ2_bz__AssertH__fail ( int errcode );
#define AssertH(cond,errcode) \
{ if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); }
#if BZ_DEBUG
#define AssertD(cond,msg) \
{ if (!(cond)) { \
fprintf ( stderr, \
"\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\
exit(1); \
}}
#else
#define AssertD(cond,msg) /* */
#endif
#define VPrintf0(zf) \
fprintf(stderr,zf)
#define VPrintf1(zf,za1) \
fprintf(stderr,zf,za1)
#define VPrintf2(zf,za1,za2) \
fprintf(stderr,zf,za1,za2)
#define VPrintf3(zf,za1,za2,za3) \
fprintf(stderr,zf,za1,za2,za3)
#define VPrintf4(zf,za1,za2,za3,za4) \
fprintf(stderr,zf,za1,za2,za3,za4)
#define VPrintf5(zf,za1,za2,za3,za4,za5) \
fprintf(stderr,zf,za1,za2,za3,za4,za5)
#else
extern void bz_internal_error ( int errcode );
#define AssertH(cond,errcode) \
{ if (!(cond)) bz_internal_error ( errcode ); }
#define AssertD(cond,msg) do { } while (0)
#define VPrintf0(zf) do { } while (0)
#define VPrintf1(zf,za1) do { } while (0)
#define VPrintf2(zf,za1,za2) do { } while (0)
#define VPrintf3(zf,za1,za2,za3) do { } while (0)
#define VPrintf4(zf,za1,za2,za3,za4) do { } while (0)
#define VPrintf5(zf,za1,za2,za3,za4,za5) do { } while (0)
#endif
#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1)
#define BZFREE(ppp) (strm->bzfree)(strm->opaque,(ppp))
/*-- Header bytes. --*/
#define BZ_HDR_B 0x42 /* 'B' */
#define BZ_HDR_Z 0x5a /* 'Z' */
#define BZ_HDR_h 0x68 /* 'h' */
#define BZ_HDR_0 0x30 /* '0' */
/*-- Constants for the back end. --*/
#define BZ_MAX_ALPHA_SIZE 258
#define BZ_MAX_CODE_LEN 23
#define BZ_RUNA 0
#define BZ_RUNB 1
#define BZ_N_GROUPS 6
#define BZ_G_SIZE 50
#define BZ_N_ITERS 4
#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE))
/*-- Stuff for randomising repetitive blocks. --*/
extern Int32 BZ2_rNums[512];
#define BZ_RAND_DECLS \
Int32 rNToGo; \
Int32 rTPos \
#define BZ_RAND_INIT_MASK \
s->rNToGo = 0; \
s->rTPos = 0 \
#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0)
#define BZ_RAND_UPD_MASK \
if (s->rNToGo == 0) { \
s->rNToGo = BZ2_rNums[s->rTPos]; \
s->rTPos++; \
if (s->rTPos == 512) s->rTPos = 0; \
} \
s->rNToGo--;
/*-- Stuff for doing CRCs. --*/
extern UInt32 BZ2_crc32Table[256];
#define BZ_INITIALISE_CRC(crcVar) \
{ \
crcVar = 0xffffffffL; \
}
#define BZ_FINALISE_CRC(crcVar) \
{ \
crcVar = ~(crcVar); \
}
#define BZ_UPDATE_CRC(crcVar,cha) \
{ \
crcVar = (crcVar << 8) ^ \
BZ2_crc32Table[(crcVar >> 24) ^ \
((UChar)cha)]; \
}
/*-- States and modes for compression. --*/
#define BZ_M_IDLE 1
#define BZ_M_RUNNING 2
#define BZ_M_FLUSHING 3
#define BZ_M_FINISHING 4
#define BZ_S_OUTPUT 1
#define BZ_S_INPUT 2
#define BZ_N_RADIX 2
#define BZ_N_QSORT 12
#define BZ_N_SHELL 18
#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2)
/*-- Structure holding all the compression-side stuff. --*/
typedef
struct {
/* pointer back to the struct bz_stream */
bz_stream* strm;
/* mode this stream is in, and whether inputting */
/* or outputting data */
Int32 mode;
Int32 state;
/* remembers avail_in when flush/finish requested */
UInt32 avail_in_expect;
/* for doing the block sorting */
UInt32* arr1;
UInt32* arr2;
UInt32* ftab;
Int32 origPtr;
/* aliases for arr1 and arr2 */
UInt32* ptr;
UChar* block;
UInt16* mtfv;
UChar* zbits;
/* for deciding when to use the fallback sorting algorithm */
Int32 workFactor;
/* run-length-encoding of the input */
UInt32 state_in_ch;
Int32 state_in_len;
BZ_RAND_DECLS;
/* input and output limits and current posns */
Int32 nblock;
Int32 nblockMAX;
Int32 numZ;
Int32 state_out_pos;
/* map of bytes used in block */
Int32 nInUse;
Bool inUse[256];
UChar unseqToSeq[256];
/* the buffer for bit stream creation */
UInt32 bsBuff;
Int32 bsLive;
/* block and combined CRCs */
UInt32 blockCRC;
UInt32 combinedCRC;
/* misc administratium */
Int32 verbosity;
Int32 blockNo;
Int32 blockSize100k;
/* stuff for coding the MTF values */
Int32 nMTF;
Int32 mtfFreq [BZ_MAX_ALPHA_SIZE];
UChar selector [BZ_MAX_SELECTORS];
UChar selectorMtf[BZ_MAX_SELECTORS];
UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
Int32 code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
Int32 rfreq [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
/* second dimension: only 3 needed; 4 makes index calculations faster */
UInt32 len_pack[BZ_MAX_ALPHA_SIZE][4];
}
EState;
/*-- externs for compression. --*/
extern void
BZ2_blockSort ( EState* );
extern void
BZ2_compressBlock ( EState*, Bool );
extern void
BZ2_bsInitWrite ( EState* );
extern void
BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 );
extern void
BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 );
/*-- states for decompression. --*/
#define BZ_X_IDLE 1
#define BZ_X_OUTPUT 2
#define BZ_X_MAGIC_1 10
#define BZ_X_MAGIC_2 11
#define BZ_X_MAGIC_3 12
#define BZ_X_MAGIC_4 13
#define BZ_X_BLKHDR_1 14
#define BZ_X_BLKHDR_2 15
#define BZ_X_BLKHDR_3 16
#define BZ_X_BLKHDR_4 17
#define BZ_X_BLKHDR_5 18
#define BZ_X_BLKHDR_6 19
#define BZ_X_BCRC_1 20
#define BZ_X_BCRC_2 21
#define BZ_X_BCRC_3 22
#define BZ_X_BCRC_4 23
#define BZ_X_RANDBIT 24
#define BZ_X_ORIGPTR_1 25
#define BZ_X_ORIGPTR_2 26
#define BZ_X_ORIGPTR_3 27
#define BZ_X_MAPPING_1 28
#define BZ_X_MAPPING_2 29
#define BZ_X_SELECTOR_1 30
#define BZ_X_SELECTOR_2 31
#define BZ_X_SELECTOR_3 32
#define BZ_X_CODING_1 33
#define BZ_X_CODING_2 34
#define BZ_X_CODING_3 35
#define BZ_X_MTF_1 36
#define BZ_X_MTF_2 37
#define BZ_X_MTF_3 38
#define BZ_X_MTF_4 39
#define BZ_X_MTF_5 40
#define BZ_X_MTF_6 41
#define BZ_X_ENDHDR_2 42
#define BZ_X_ENDHDR_3 43
#define BZ_X_ENDHDR_4 44
#define BZ_X_ENDHDR_5 45
#define BZ_X_ENDHDR_6 46
#define BZ_X_CCRC_1 47
#define BZ_X_CCRC_2 48
#define BZ_X_CCRC_3 49
#define BZ_X_CCRC_4 50
/*-- Constants for the fast MTF decoder. --*/
#define MTFA_SIZE 4096
#define MTFL_SIZE 16
/*-- Structure holding all the decompression-side stuff. --*/
typedef
struct {
/* pointer back to the struct bz_stream */
bz_stream* strm;
/* state indicator for this stream */
Int32 state;
/* for doing the final run-length decoding */
UChar state_out_ch;
Int32 state_out_len;
Bool blockRandomised;
BZ_RAND_DECLS;
/* the buffer for bit stream reading */
UInt32 bsBuff;
Int32 bsLive;
/* misc administratium */
Int32 blockSize100k;
Bool smallDecompress;
Int32 currBlockNo;
Int32 verbosity;
/* for undoing the Burrows-Wheeler transform */
Int32 origPtr;
UInt32 tPos;
Int32 k0;
Int32 unzftab[256];
Int32 nblock_used;
Int32 cftab[257];
Int32 cftabCopy[257];
/* for undoing the Burrows-Wheeler transform (FAST) */
UInt32 *tt;
/* for undoing the Burrows-Wheeler transform (SMALL) */
UInt16 *ll16;
UChar *ll4;
/* stored and calculated CRCs */
UInt32 storedBlockCRC;
UInt32 storedCombinedCRC;
UInt32 calculatedBlockCRC;
UInt32 calculatedCombinedCRC;
/* map of bytes used in block */
Int32 nInUse;
Bool inUse[256];
Bool inUse16[16];
UChar seqToUnseq[256];
/* for decoding the MTF values */
UChar mtfa [MTFA_SIZE];
Int32 mtfbase[256 / MTFL_SIZE];
UChar selector [BZ_MAX_SELECTORS];
UChar selectorMtf[BZ_MAX_SELECTORS];
UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
Int32 limit [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
Int32 base [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
Int32 perm [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
Int32 minLens[BZ_N_GROUPS];
/* save area for scalars in the main decompress code */
Int32 save_i;
Int32 save_j;
Int32 save_t;
Int32 save_alphaSize;
Int32 save_nGroups;
Int32 save_nSelectors;
Int32 save_EOB;
Int32 save_groupNo;
Int32 save_groupPos;
Int32 save_nextSym;
Int32 save_nblockMAX;
Int32 save_nblock;
Int32 save_es;
Int32 save_N;
Int32 save_curr;
Int32 save_zt;
Int32 save_zn;
Int32 save_zvec;
Int32 save_zj;
Int32 save_gSel;
Int32 save_gMinlen;
Int32* save_gLimit;
Int32* save_gBase;
Int32* save_gPerm;
}
DState;
/*-- Macros for decompression. --*/
#define BZ_GET_FAST(cccc) \
/* c_tPos is unsigned, hence test < 0 is pointless. */ \
if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \
s->tPos = s->tt[s->tPos]; \
cccc = (UChar)(s->tPos & 0xff); \
s->tPos >>= 8;
#define BZ_GET_FAST_C(cccc) \
/* c_tPos is unsigned, hence test < 0 is pointless. */ \
if (c_tPos >= (UInt32)100000 * (UInt32)ro_blockSize100k) return True; \
c_tPos = c_tt[c_tPos]; \
cccc = (UChar)(c_tPos & 0xff); \
c_tPos >>= 8;
#define SET_LL4(i,n) \
{ if (((i) & 0x1) == 0) \
s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else \
s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4); \
}
#define GET_LL4(i) \
((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF)
#define SET_LL(i,n) \
{ s->ll16[i] = (UInt16)(n & 0x0000ffff); \
SET_LL4(i, n >> 16); \
}
#define GET_LL(i) \
(((UInt32)s->ll16[i]) | (GET_LL4(i) << 16))
#define BZ_GET_SMALL(cccc) \
/* c_tPos is unsigned, hence test < 0 is pointless. */ \
if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \
cccc = BZ2_indexIntoF ( s->tPos, s->cftab ); \
s->tPos = GET_LL(s->tPos);
/*-- externs for decompression. --*/
extern Int32
BZ2_indexIntoF ( Int32, Int32* );
extern Int32
BZ2_decompress ( DState* );
extern void
BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*,
Int32, Int32, Int32 );
#endif
/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/
#ifdef BZ_NO_STDIO
#ifndef NULL
#define NULL 0
#endif
#endif
/*-------------------------------------------------------------*/
/*--- end bzlib_private.h ---*/
/*-------------------------------------------------------------*/

View file

@ -0,0 +1,672 @@
/*-------------------------------------------------------------*/
/*--- Compression machinery (not incl block sorting) ---*/
/*--- compress.c ---*/
/*-------------------------------------------------------------*/
/* ------------------------------------------------------------------
This file is part of bzip2/libbzip2, a program and library for
lossless, block-sorting data compression.
bzip2/libbzip2 version 1.0.5 of 10 December 2007
Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
Please read the WARNING, DISCLAIMER and PATENTS sections in the
README file.
This program is released under the terms of the license contained
in the file LICENSE.
------------------------------------------------------------------ */
/* CHANGES
0.9.0 -- original version.
0.9.0a/b -- no changes in this file.
0.9.0c -- changed setting of nGroups in sendMTFValues()
so as to do a bit better on small files
*/
#include "bzlib_private.h"
/*---------------------------------------------------*/
/*--- Bit stream I/O ---*/
/*---------------------------------------------------*/
/*---------------------------------------------------*/
void BZ2_bsInitWrite ( EState* s )
{
s->bsLive = 0;
s->bsBuff = 0;
}
/*---------------------------------------------------*/
static
void bsFinishWrite ( EState* s )
{
while (s->bsLive > 0) {
s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24);
s->numZ++;
s->bsBuff <<= 8;
s->bsLive -= 8;
}
}
/*---------------------------------------------------*/
#define bsNEEDW(nz) \
{ \
while (s->bsLive >= 8) { \
s->zbits[s->numZ] \
= (UChar)(s->bsBuff >> 24); \
s->numZ++; \
s->bsBuff <<= 8; \
s->bsLive -= 8; \
} \
}
/*---------------------------------------------------*/
static
__inline__
void bsW ( EState* s, Int32 n, UInt32 v )
{
bsNEEDW ( n );
s->bsBuff |= (v << (32 - s->bsLive - n));
s->bsLive += n;
}
/*---------------------------------------------------*/
static
void bsPutUInt32 ( EState* s, UInt32 u )
{
bsW ( s, 8, (u >> 24) & 0xffL );
bsW ( s, 8, (u >> 16) & 0xffL );
bsW ( s, 8, (u >> 8) & 0xffL );
bsW ( s, 8, u & 0xffL );
}
/*---------------------------------------------------*/
static
void bsPutUChar ( EState* s, UChar c )
{
bsW( s, 8, (UInt32)c );
}
/*---------------------------------------------------*/
/*--- The back end proper ---*/
/*---------------------------------------------------*/
/*---------------------------------------------------*/
static
void makeMaps_e ( EState* s )
{
Int32 i;
s->nInUse = 0;
for (i = 0; i < 256; i++)
if (s->inUse[i]) {
s->unseqToSeq[i] = s->nInUse;
s->nInUse++;
}
}
/*---------------------------------------------------*/
static
void generateMTFValues ( EState* s )
{
UChar yy[256];
Int32 i, j;
Int32 zPend;
Int32 wr;
Int32 EOB;
/*
After sorting (eg, here),
s->arr1 [ 0 .. s->nblock-1 ] holds sorted order,
and
((UChar*)s->arr2) [ 0 .. s->nblock-1 ]
holds the original block data.
The first thing to do is generate the MTF values,
and put them in
((UInt16*)s->arr1) [ 0 .. s->nblock-1 ].
Because there are strictly fewer or equal MTF values
than block values, ptr values in this area are overwritten
with MTF values only when they are no longer needed.
The final compressed bitstream is generated into the
area starting at
(UChar*) (&((UChar*)s->arr2)[s->nblock])
These storage aliases are set up in bzCompressInit(),
except for the last one, which is arranged in
compressBlock().
*/
UInt32* ptr = s->ptr;
UChar* block = s->block;
UInt16* mtfv = s->mtfv;
makeMaps_e ( s );
EOB = s->nInUse+1;
for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0;
wr = 0;
zPend = 0;
for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i;
for (i = 0; i < s->nblock; i++) {
UChar ll_i;
AssertD ( wr <= i, "generateMTFValues(1)" );
j = ptr[i]-1; if (j < 0) j += s->nblock;
ll_i = s->unseqToSeq[block[j]];
AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" );
if (yy[0] == ll_i) {
zPend++;
} else {
if (zPend > 0) {
zPend--;
while (True) {
if (zPend & 1) {
mtfv[wr] = BZ_RUNB; wr++;
s->mtfFreq[BZ_RUNB]++;
} else {
mtfv[wr] = BZ_RUNA; wr++;
s->mtfFreq[BZ_RUNA]++;
}
if (zPend < 2) break;
zPend = (zPend - 2) / 2;
};
zPend = 0;
}
{
register UChar rtmp;
register UChar* ryy_j;
register UChar rll_i;
rtmp = yy[1];
yy[1] = yy[0];
ryy_j = &(yy[1]);
rll_i = ll_i;
while ( rll_i != rtmp ) {
register UChar rtmp2;
ryy_j++;
rtmp2 = rtmp;
rtmp = *ryy_j;
*ryy_j = rtmp2;
};
yy[0] = rtmp;
j = ryy_j - &(yy[0]);
mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++;
}
}
}
if (zPend > 0) {
zPend--;
while (True) {
if (zPend & 1) {
mtfv[wr] = BZ_RUNB; wr++;
s->mtfFreq[BZ_RUNB]++;
} else {
mtfv[wr] = BZ_RUNA; wr++;
s->mtfFreq[BZ_RUNA]++;
}
if (zPend < 2) break;
zPend = (zPend - 2) / 2;
};
zPend = 0;
}
mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++;
s->nMTF = wr;
}
/*---------------------------------------------------*/
#define BZ_LESSER_ICOST 0
#define BZ_GREATER_ICOST 15
static
void sendMTFValues ( EState* s )
{
Int32 v, t, i, j, gs, ge, totc, bt, bc, iter;
Int32 nSelectors, alphaSize, minLen, maxLen, selCtr;
Int32 nGroups, nBytes;
/*--
UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
is a global since the decoder also needs it.
Int32 code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
Int32 rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
are also globals only used in this proc.
Made global to keep stack frame size small.
--*/
UInt16 cost[BZ_N_GROUPS];
Int32 fave[BZ_N_GROUPS];
UInt16* mtfv = s->mtfv;
if (s->verbosity >= 3)
VPrintf3( " %d in block, %d after MTF & 1-2 coding, "
"%d+2 syms in use\n",
s->nblock, s->nMTF, s->nInUse );
alphaSize = s->nInUse+2;
for (t = 0; t < BZ_N_GROUPS; t++)
for (v = 0; v < alphaSize; v++)
s->len[t][v] = BZ_GREATER_ICOST;
/*--- Decide how many coding tables to use ---*/
AssertH ( s->nMTF > 0, 3001 );
if (s->nMTF < 200) nGroups = 2; else
if (s->nMTF < 600) nGroups = 3; else
if (s->nMTF < 1200) nGroups = 4; else
if (s->nMTF < 2400) nGroups = 5; else
nGroups = 6;
/*--- Generate an initial set of coding tables ---*/
{
Int32 nPart, remF, tFreq, aFreq;
nPart = nGroups;
remF = s->nMTF;
gs = 0;
while (nPart > 0) {
tFreq = remF / nPart;
ge = gs-1;
aFreq = 0;
while (aFreq < tFreq && ge < alphaSize-1) {
ge++;
aFreq += s->mtfFreq[ge];
}
if (ge > gs
&& nPart != nGroups && nPart != 1
&& ((nGroups-nPart) % 2 == 1)) {
aFreq -= s->mtfFreq[ge];
ge--;
}
if (s->verbosity >= 3)
VPrintf5( " initial group %d, [%d .. %d], "
"has %d syms (%4.1f%%)\n",
nPart, gs, ge, aFreq,
(100.0 * (float)aFreq) / (float)(s->nMTF) );
for (v = 0; v < alphaSize; v++)
if (v >= gs && v <= ge)
s->len[nPart-1][v] = BZ_LESSER_ICOST; else
s->len[nPart-1][v] = BZ_GREATER_ICOST;
nPart--;
gs = ge+1;
remF -= aFreq;
}
}
/*---
Iterate up to BZ_N_ITERS times to improve the tables.
---*/
for (iter = 0; iter < BZ_N_ITERS; iter++) {
for (t = 0; t < nGroups; t++) fave[t] = 0;
for (t = 0; t < nGroups; t++)
for (v = 0; v < alphaSize; v++)
s->rfreq[t][v] = 0;
/*---
Set up an auxiliary length table which is used to fast-track
the common case (nGroups == 6).
---*/
if (nGroups == 6) {
for (v = 0; v < alphaSize; v++) {
s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v];
s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v];
s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v];
}
}
nSelectors = 0;
totc = 0;
gs = 0;
while (True) {
/*--- Set group start & end marks. --*/
if (gs >= s->nMTF) break;
ge = gs + BZ_G_SIZE - 1;
if (ge >= s->nMTF) ge = s->nMTF-1;
/*--
Calculate the cost of this group as coded
by each of the coding tables.
--*/
for (t = 0; t < nGroups; t++) cost[t] = 0;
if (nGroups == 6 && 50 == ge-gs+1) {
/*--- fast track the common case ---*/
register UInt32 cost01, cost23, cost45;
register UInt16 icv;
cost01 = cost23 = cost45 = 0;
# define BZ_ITER(nn) \
icv = mtfv[gs+(nn)]; \
cost01 += s->len_pack[icv][0]; \
cost23 += s->len_pack[icv][1]; \
cost45 += s->len_pack[icv][2]; \
BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4);
BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9);
BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14);
BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19);
BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24);
BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29);
BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34);
BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39);
BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44);
BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49);
# undef BZ_ITER
cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16;
cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16;
cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16;
} else {
/*--- slow version which correctly handles all situations ---*/
for (i = gs; i <= ge; i++) {
UInt16 icv = mtfv[i];
for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv];
}
}
/*--
Find the coding table which is best for this group,
and record its identity in the selector table.
--*/
bc = 999999999; bt = -1;
for (t = 0; t < nGroups; t++)
if (cost[t] < bc) { bc = cost[t]; bt = t; };
totc += bc;
fave[bt]++;
s->selector[nSelectors] = bt;
nSelectors++;
/*--
Increment the symbol frequencies for the selected table.
--*/
if (nGroups == 6 && 50 == ge-gs+1) {
/*--- fast track the common case ---*/
# define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++
BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4);
BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9);
BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14);
BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19);
BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24);
BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29);
BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34);
BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39);
BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44);
BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49);
# undef BZ_ITUR
} else {
/*--- slow version which correctly handles all situations ---*/
for (i = gs; i <= ge; i++)
s->rfreq[bt][ mtfv[i] ]++;
}
gs = ge+1;
}
if (s->verbosity >= 3) {
VPrintf2 ( " pass %d: size is %d, grp uses are ",
iter+1, totc/8 );
for (t = 0; t < nGroups; t++)
VPrintf1 ( "%d ", fave[t] );
VPrintf0 ( "\n" );
}
/*--
Recompute the tables based on the accumulated frequencies.
--*/
/* maxLen was changed from 20 to 17 in bzip2-1.0.3. See
comment in huffman.c for details. */
for (t = 0; t < nGroups; t++)
BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]),
alphaSize, 17 /*20*/ );
}
AssertH( nGroups < 8, 3002 );
AssertH( nSelectors < 32768 &&
nSelectors <= (2 + (900000 / BZ_G_SIZE)),
3003 );
/*--- Compute MTF values for the selectors. ---*/
{
UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp;
for (i = 0; i < nGroups; i++) pos[i] = i;
for (i = 0; i < nSelectors; i++) {
ll_i = s->selector[i];
j = 0;
tmp = pos[j];
while ( ll_i != tmp ) {
j++;
tmp2 = tmp;
tmp = pos[j];
pos[j] = tmp2;
};
pos[0] = tmp;
s->selectorMtf[i] = j;
}
};
/*--- Assign actual codes for the tables. --*/
for (t = 0; t < nGroups; t++) {
minLen = 32;
maxLen = 0;
for (i = 0; i < alphaSize; i++) {
if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
if (s->len[t][i] < minLen) minLen = s->len[t][i];
}
AssertH ( !(maxLen > 17 /*20*/ ), 3004 );
AssertH ( !(minLen < 1), 3005 );
BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]),
minLen, maxLen, alphaSize );
}
/*--- Transmit the mapping table. ---*/
{
Bool inUse16[16];
for (i = 0; i < 16; i++) {
inUse16[i] = False;
for (j = 0; j < 16; j++)
if (s->inUse[i * 16 + j]) inUse16[i] = True;
}
nBytes = s->numZ;
for (i = 0; i < 16; i++)
if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0);
for (i = 0; i < 16; i++)
if (inUse16[i])
for (j = 0; j < 16; j++) {
if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0);
}
if (s->verbosity >= 3)
VPrintf1( " bytes: mapping %d, ", s->numZ-nBytes );
}
/*--- Now the selectors. ---*/
nBytes = s->numZ;
bsW ( s, 3, nGroups );
bsW ( s, 15, nSelectors );
for (i = 0; i < nSelectors; i++) {
for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1);
bsW(s,1,0);
}
if (s->verbosity >= 3)
VPrintf1( "selectors %d, ", s->numZ-nBytes );
/*--- Now the coding tables. ---*/
nBytes = s->numZ;
for (t = 0; t < nGroups; t++) {
Int32 curr = s->len[t][0];
bsW ( s, 5, curr );
for (i = 0; i < alphaSize; i++) {
while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ };
while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ };
bsW ( s, 1, 0 );
}
}
if (s->verbosity >= 3)
VPrintf1 ( "code lengths %d, ", s->numZ-nBytes );
/*--- And finally, the block data proper ---*/
nBytes = s->numZ;
selCtr = 0;
gs = 0;
while (True) {
if (gs >= s->nMTF) break;
ge = gs + BZ_G_SIZE - 1;
if (ge >= s->nMTF) ge = s->nMTF-1;
AssertH ( s->selector[selCtr] < nGroups, 3006 );
if (nGroups == 6 && 50 == ge-gs+1) {
/*--- fast track the common case ---*/
UInt16 mtfv_i;
UChar* s_len_sel_selCtr
= &(s->len[s->selector[selCtr]][0]);
Int32* s_code_sel_selCtr
= &(s->code[s->selector[selCtr]][0]);
# define BZ_ITAH(nn) \
mtfv_i = mtfv[gs+(nn)]; \
bsW ( s, \
s_len_sel_selCtr[mtfv_i], \
s_code_sel_selCtr[mtfv_i] )
BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4);
BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9);
BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14);
BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19);
BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24);
BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29);
BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34);
BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39);
BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44);
BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49);
# undef BZ_ITAH
} else {
/*--- slow version which correctly handles all situations ---*/
for (i = gs; i <= ge; i++) {
bsW ( s,
s->len [s->selector[selCtr]] [mtfv[i]],
s->code [s->selector[selCtr]] [mtfv[i]] );
}
}
gs = ge+1;
selCtr++;
}
AssertH( selCtr == nSelectors, 3007 );
if (s->verbosity >= 3)
VPrintf1( "codes %d\n", s->numZ-nBytes );
}
/*---------------------------------------------------*/
void BZ2_compressBlock ( EState* s, Bool is_last_block )
{
if (s->nblock > 0) {
BZ_FINALISE_CRC ( s->blockCRC );
s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31);
s->combinedCRC ^= s->blockCRC;
if (s->blockNo > 1) s->numZ = 0;
if (s->verbosity >= 2)
VPrintf4( " block %d: crc = 0x%08x, "
"combined CRC = 0x%08x, size = %d\n",
s->blockNo, s->blockCRC, s->combinedCRC, s->nblock );
BZ2_blockSort ( s );
}
s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]);
/*-- If this is the first block, create the stream header. --*/
if (s->blockNo == 1) {
BZ2_bsInitWrite ( s );
bsPutUChar ( s, BZ_HDR_B );
bsPutUChar ( s, BZ_HDR_Z );
bsPutUChar ( s, BZ_HDR_h );
bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) );
}
if (s->nblock > 0) {
bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 );
bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 );
bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 );
/*-- Now the block's CRC, so it is in a known place. --*/
bsPutUInt32 ( s, s->blockCRC );
/*--
Now a single bit indicating (non-)randomisation.
As of version 0.9.5, we use a better sorting algorithm
which makes randomisation unnecessary. So always set
the randomised bit to 'no'. Of course, the decoder
still needs to be able to handle randomised blocks
so as to maintain backwards compatibility with
older versions of bzip2.
--*/
bsW(s,1,0);
bsW ( s, 24, s->origPtr );
generateMTFValues ( s );
sendMTFValues ( s );
}
/*-- If this is the last block, add the stream trailer. --*/
if (is_last_block) {
bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 );
bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 );
bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 );
bsPutUInt32 ( s, s->combinedCRC );
if (s->verbosity >= 2)
VPrintf1( " final combined CRC = 0x%08x\n ", s->combinedCRC );
bsFinishWrite ( s );
}
}
/*-------------------------------------------------------------*/
/*--- end compress.c ---*/
/*-------------------------------------------------------------*/

View file

@ -0,0 +1,104 @@
/*-------------------------------------------------------------*/
/*--- Table for doing CRCs ---*/
/*--- crctable.c ---*/
/*-------------------------------------------------------------*/
/* ------------------------------------------------------------------
This file is part of bzip2/libbzip2, a program and library for
lossless, block-sorting data compression.
bzip2/libbzip2 version 1.0.5 of 10 December 2007
Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
Please read the WARNING, DISCLAIMER and PATENTS sections in the
README file.
This program is released under the terms of the license contained
in the file LICENSE.
------------------------------------------------------------------ */
#include "bzlib_private.h"
/*--
I think this is an implementation of the AUTODIN-II,
Ethernet & FDDI 32-bit CRC standard. Vaguely derived
from code by Rob Warnock, in Section 51 of the
comp.compression FAQ.
--*/
UInt32 BZ2_crc32Table[256] = {
/*-- Ugly, innit? --*/
0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L,
0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L,
0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L,
0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL,
0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L,
0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L,
0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L,
0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL,
0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L,
0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L,
0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L,
0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL,
0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L,
0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L,
0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L,
0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL,
0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL,
0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L,
0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L,
0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL,
0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL,
0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L,
0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L,
0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL,
0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL,
0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L,
0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L,
0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL,
0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL,
0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L,
0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L,
0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL,
0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L,
0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL,
0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL,
0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L,
0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L,
0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL,
0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL,
0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L,
0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L,
0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL,
0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL,
0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L,
0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L,
0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL,
0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL,
0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L,
0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L,
0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL,
0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L,
0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L,
0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L,
0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL,
0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L,
0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L,
0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L,
0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL,
0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L,
0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L,
0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L,
0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL,
0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L,
0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L
};
/*-------------------------------------------------------------*/
/*--- end crctable.c ---*/
/*-------------------------------------------------------------*/

Some files were not shown because too many files have changed in this diff Show more