CPKREDIR v0.41 - Korama's CPK file redirector for Sonic Generations
===============================================================================
E-Mail: amarokorama(at)msn.com


0. What's new
-------------
v0.1 - initial release, quite buggy, only worked for Sonic Generations v1.0.0.5

v0.2 - some bugfixes and other improvements, working with all versions of Sonic
  Generations now

v0.3 - major overhaul, improved stability, introduced "advanced mode" and added
  a patcher for SonicGenerations.exe, included sample INI files for Dario FF's
  "Classic Sonic Adventures"

v0.31 - fixed a bug that prevented redirection of the sound CPKs

v0.4 - introduced save file redirection, "size0" command,
  disabled logging by default, some minor fixes

v0.41 - fixed save file redirection for older versions of Sonic Generations,
  added SaveFileReadThrough and SaveFileOverride settings



1. What is it?
--------------
A DLL that is loaded by SonicGenerations.exe, intercepts all read calls to CPK
archives and redirects them to external user files, or to the original internal
files. This is achieved by building virtual CPK archives on the fly, which
contain a user-defined collection of original internal and modified external
files.

The purpose of CPKREDIR is to facilitate the development and distribution of
mods for Sonic Generations. CPKREDIR makes time-consuming unpacking and
repacking of CPK archives unnecessary. Since version 0.3, it is possible to
cascade multiple mods (ie. load several different mods simultaneously)!

The usual disclaimer: Use this software at your own risk.
Normally, the worst thing that this software can do is crash Sonic Generations.
Without any permanent side-effects, so this is not dangerous at all.
But nevertheless: use at your own risk!



2. Installation instructions
----------------------------
Since version 0.3, a simple installer is provided, cpkredirInst.exe.
Just run it, it should automatically find the location of SonicGenerations.exe.
If not, execute it in the same folder as SonicGenerations.exe.

Alternatively, you can open SonicGenerations.exe with a hex editor of your
choice, search for the string "imagehlp.dll" and replace it with "cpkredir.dll".

Then place the file cpkredir.dll in the same directory as SonicGenerations.exe,
so that it'll get loaded when the game starts.

To uninstall, run cpkredirInst.exe again (or patch the string "imagehlp.dll"
back into SonicGenerations.exe with a hex editor) and delete all cpkredir files.



3. Configuration and settings
-----------------------------
To change certain aspects of CPKREDIR's operation, you can place an INI file
next to cpkredir.dll (exact same path and file name, but with ".ini" extension
instead of ".dll").
Note: Please check out chapter 5 for some general information on INI file
syntax as used by CPKREDIR.
The content of the cpkredir.ini should look like this:

[CPKREDIR]
Enabled=1
PlaceTocAtEnd=1
HandleCpksWithoutExtFiles=0
LogType=console
LogFile="cpkredir.log"
ReadBlockSizeKB=4096
ModsDbIni="mods\ModsDB.ini"
EnableSaveFileRedirection=1
SaveFileReadThrough=1
SaveFileFallback="cpkredir.sav"
SaveFileOverride=


Enabled (boolean, default=1) - enables or disables all CPKREDIR features.
  You can use this to completely deactivate CPKREDIR without uninstalling
  it from SonicGenerations.exe.
PlaceTocAtEnd (boolean, default=1) - controls placement of the table of
  contents in the virtual CPK archives that CPKREDIR creates. Whether this
  setting is enabled or disabled should make no difference in this version,
  but I suggest keeping it enabled.
HandleCpksWithoutExtFiles (boolean, default=0) - controls whether CPKREDIR will
  handle all CPK files or only those CPK files that have external files (or had
  their internal files changed). This is effectively a logging option. Having
  this enabled will produce more logging output, so keep it disabled unless
  you're interested in the log.
LogType (string, default="") - determines the type of CPKREDIR's log.
  Use "con" or "console" for logging to console, "file" for logging to file.
  Other values mean logging is disabled.
  Running Sonic Generations in windowed mode with the logging console enabled
  can be useful for testing or tracking down problems. But be aware that the
  console can slow down the game and even cause errors for some people.
  You can use the program "DxWnd" (http://sourceforge.net/p/dxwnd/) to play
  Sonic Generations in windowed mode.
LogFile (string, default=cpkredir.log) - path and name of the output file that
  is used when LogType=file. The file will be cleared and overwritten each time
  CPKREDIR is launched with file logging enabled.
ReadBlockSizeKB (integer, default=1024) - Sonic Generations loads bigger
  resources in chunks of 1024 KiB (1 MiB) by default. Therefore, to load a file
  that is 10 MiB in size, for example, the game has to issue 10 read calls with
  all their associated overhead. This option lets you change that read block
  size. Setting it to a value such as 4096 KiB (4 MiB) or larger might be a good
  idea and should increase performance a bit. Careful though, unreasonable
  values will hurt performance or crash the game.
ModsDbIni (string, default="") - path and name of an INI file that contains
  information about mods for Sonic Generations. If this value is present and
  points to an existing file, CPKREDIR will use its new advanced mode. Otherwise
  it'll operate in simple mode. More on that in the following chapters.
EnableSaveFileRedirection (boolean, default=1) - enables or disables savegame
  file redirection. It's a good idea to use a separate save file when you're
  playing mods, to avoid spoiling your legitimate records.
SaveFileReadThrough (boolean, default=1) - controls what happens when save file
  redirection is enabled but points to a non-existing save file. If true, your
  regular save data will be loaded. Otherwise (if false), you have to start the
  game from the beginning with a blank slate (New Game).
SaveFileFallback (string, default=cpkredir.sav) - path and name of the fallback
  save file. This value is only used when save file redirection is enabled and
  no other save file is specified by an active mod, or if CPKREDIR is running in
  simple mode.
SaveFileOverride (string, default="") - path and name of the override save file.
  This value can be used to force the use of a given save file, even if some
  active mods would like to use their custom save files.



4. Usage notes
--------------
Since version 0.3, there are two ways to use CPKREDIR, simple and advanced.
What mode is active depends on the "ModsDbIni" setting explained above.

4.1. Usage notes - Simple
-------------------------
In simple mode, the external files for each CPK archive are expected in a folder
that has the same path and name as the respective CPK archive, without the
".cpk" extension.
Example: to enable redirection for "disk\bb2.cpk", place your files inside the
folder "disk\bb2\". This redirection folder can and should have subfolders where
appropriate (take a look at how the files are organized in the original CPK).

You can not only override files that already exist inside a CPK archive, but
also add completely new ones. In fact, each and every file inside a redirection
folder is added to the respective virtual CPK file. That means you should not
stuff those folders with lots of unrelated files that are useless for the
game, or else it might crash.

For technical reasons, you have to restart the game whenever you change
something in a redirection folder, eg. remove a file or add one. You might be
able to edit data files on the fly while the game is running, but only if the
file size stays identical. However, in any case it's better to close the game
before changes are made.

4.2. Usage notes - Advanced
---------------------------
In advanced mode, CPKREDIR will not use the basic redirection folders from
simple mode. Instead, it will parse information provided by a chain of INI
files. This is a lot more flexible. It allows external files in various
locations, manipulation of CPK internal files and even a cascaded loading
of multiple mods at once.

Example: Let's say you have three mods on your disk. One that swaps the
levels of modern and classic Sonic, one that replaces Sonic's 3D model with
a model of Shadow and one that removes the HUD from the game. With CPKREDIR's
simple mode, you could play each one of those mods by copying the respective
files into the disk\bb*\ redirection folders. If you wanted to play all three
mods at once, you'd have to copy all the files and overwrite duplicates where
necessary. Not very convenient, especially not when you realize you've copied
the files in the wrong order, so that the Shadow mod overwrote the no HUD
mod, for example.

That's where the advanced mode comes in. It lets you organize all mods in a
location of your choice (eg. "Sonic Generations\mods"), cascade them in any
order you like and activate/deactivate them by simply editing INI files.
However, as neat as the idea of cascading mods sounds, it doesn't always work
in practice. Some mods just don't play nice with each other, because they are
trying to make different changes to the same data files. In those cases, the
game might crash, of course.

The required format of the INI chain is described in the following chapters.

4.3. Usage notes - Save files
-----------------------------
As long as you play Sonic Generations with mods and CPKREDIR, I suggest keeping
save file redirection enabled at all times. This ensures that your legitimate
records in the game won't be spoiled. And it also allows advanced mods to
make use of red rings, amongst other things, by providing custom save files.
If you want to play such a mod with the skills and settings from your non-mod
save file, you can simply use the SaveFileOverride key in cpkredir.ini for that.
Set it to the same value as SaveFileFallback, for example ("cpkredir.sav" by
default). But don't forget to disable the override again when you're done
playing or when starting a new mod that comes with a custom save.
I also suggest having SaveFileReadThrough enabled at all times, because it will
automatically make a copy of your non-mod save file (without changing it, of
course) whenever the redirected save file does not yet exist.



5. INI file syntax
------------------
INI files are very simple, so what's to explain here?
Well, CPKREDIR does not use the GetPrivateProfile Windows API functions, in case
you are familiar with those. It treats some minor things a bit differently than
you might be used to from other software. Feel free to skip to chapter 5.2. or
even to chapter 6 if you are a 1337 h4x0r.

5.1. The basics
---------------
* INI files are text files that contain at least one section
* A section is initiated by a name in square brackets, eg. [Section1]
* Sections contain lines that usually have the form key=value (or name=value)
* In certain cases, a line may contain just a name, without the =value part
* Values may be enclosed in double or single quotes, eg. n="v" or n='v'
* Names must NOT be enclosed in quotes
* Spaces and tabs are ignored, with the exception of quoted values.
  So, n =  v    is the same as n=v, but not the same as n="    v   "
* Lines with a semicolon (;) as first non-space character are ignored (comments)
* Empty lines are ignored
* Names, including section names, are case insensitive. And since for CPKREDIR
  most values are file names, those are case insensitive as well, as usual in
  Windows.
* Section names should be unique within a file. (If two or more sections have
  the same name, the one that appears last will be used.)
* Keys of key=value pairs should be unique within a section. (If a key occurs
  more than once in a section, the last one will be used.)

5.2. Data types
---------------
CPKREDIR treats values from INI files as one of three data types:
boolean, integer or string.
* Boolean values may be in string form, "true" or "false" (case insensitive),
  or in integer form (0 = false, any other number = true).
* Integer values may contain a "0x" or "$" prefix to denote hexadecimal numbers.
* Most string values that CPKREDIR expects are file names. File names can be
  absolute (eg. "C:\Users\JohnDoe\bla") or relative (eg. "..\test\bla").
  If they are relative, they are treated as relative to the location of the
  INI file in which they are used. This is done so that the mod INIs can use
  relative names and work regardless from where the user decides to place
  the actual mod files.
  Furthermore, file names may include environment variables, CPKREDIR will
  expand them. That way, if Sonic Generations is installed on a multi user PC
  and each user would like to have his/her own mods database, you could set the
  ModsDbIni key in cpkredir.ini to something like "%USERPROFILE%\modsDB.ini".
  %USERPROFILE% is an environment variable that points to the "home" folder of
  the current user, eg. "C:\Users\JohnDoe".



6. The INI chain
----------------
Chapter 3 described CPKREDIR's main configuration INI. In order to use the
advanced redirection mode, the ModsDbIni key there should point to another INI
file that acts as a database, or collection, of one or more mods.

6.1. The mods database
----------------------
The mods database INI requires two sections, "Main" and "Mods".
Here's an example:

[Main]
ActiveModCount=10
ActiveMod0="No HUD"
ActiveMod1="Classic Sonic Adventures"
ActiveMod2="Shadow Model Swap"
;ActiveMod3="WaveOcean"
ReverseLoadOrder=True

[Mods]
Classic Sonic Adventures="CSA\mod.ini"
Shadow Model Swap="Shadow\mod.ini"
No HUD="No HUD\mod.ini"
WaveOcean="WaveOcean\mod.ini"

ActiveModCount (integer) - defines the highest "ActiveMod" slot number that
  CPKREDIR will load. Basically, it exists as an easy speed optimization so
  that CPKREDIR doesn't look for hundreds of "ActiveMod" slots that do not
  exist. If you remove this key, its value defaults to 10. The maximum allowed
  value is 1000 at the moment (that ought to be enough for anybody).
ActiveModX (string) - These are the slots that define the currently active
  cascade of mods. The values here are just some unique identifiers that point
  to keys of the same name in the "Mods" section. And the keys there point to
  the mod INI files (see chapter 6.2).
  CPKREDIR tries to read the keys ActiveMod0, ActiveMod1, ActiveMod2,
  etc. in sequence up to ActiveModN, with N being ActiveModCount minus 1.
  Any missing (eg. commented out) "ActiveMod" slots are simply ignored.  
  The lower the slot number, the higher the "priority".
  That means the mod that occupies slot 0 will override all other mods.
  Do not use leading zeroes in these keys. CPKREDIR looks for "ActiveMod1"
  and not for "ActiveMod01", for example.
ReverseLoadOrder (boolean) - controls how the "ActiveMod" slots are loaded.
  If false, CPKREDIR loads the slots from low number to high number (0..N) while
  forbidding any links to external files to be overwritten, if they were created
  by a previous slot. Let's say that the "No HUD" mod in the example above comes
  with a modified "Application.ar.00" data file. If any of the following, lower
  priority mods try to import their own version of "Application.ar.00", they
  won't succeed.
  If ReverseLoadOrder is true, CPKREDIR will actually load the slots from high
  number to low number (N..0) while overwriting any external links made by
  previous, lower priority mods.
  If all mods in the cascade are simply linking external files and aren't
  changing any CPK internals, then there should be no difference between
  ReverseLoadOrder true or false. However, for mods that tweak CPK internals
  (eg. Dario's "Classic Sonic Adventures"), the difference can be significant.
  In such cases, having ReverseLoadOrder set to true will probably be better.
  
6.2. The mod INI
-----------------
As described in the previous section, the mods database points to the individual
mods. Actually, it points to INI files that describe the respective mods.
Such a mod INI has a required section "Main" and an optional section "CPKs".
Here's an example:

[Main]
IncludeDirCount=1
IncludeDir0="."
SaveFile="waveocean.sav"

[Desc]
Title="Wave Ocean"
Version="RC1"
Author="Melpontro"

CPKREDIR does not care about the "Desc" section here. This is just an idea for
useful metadata. Whoever wants to write a nice graphical user interface for
managing mods and controlling all these INIs could use that metadata in his/her
program and come up with more keys.

IncludeDirCount (integer) - you probably guessed it: defines the highest
  "IncludeDir" slot number that CPKREDIR will load. As with "ActiveModCount",
  the default is 10 and the maximum is 1000.
IncludeDir (string) - Slots which point to directories/folders where CPKREDIR
  will search for external files. To be precise, the included folder is regarded
  as a base and CPKREDIR expects one subfolder there for each CPK.
  Let's say that the above mod INI is the file "D:\SGMods\WaveOcean\mod.ini" and
  that this mod is active. If Sonic Generations opens its CPK archive
  "disk\bb.cpk", then CPKREDIR will add all files and folders in
  "D:\SGMods\WaveOcean\disk\bb\" to the virtual version of "disk\bb.cpk".
  Should "D:\SGMods\WaveOcean\disk\bb\" not exist, then CPKREDIR will fall back
  to the bare name of the CPK and try to include "D:\SGMods\WaveOcean\bb\"
  instead.
  As with the ActiveMod slots, the slots are loaded in sequence according to
  their number and lower number IncludeDirs have a higher priority, they
  override slots that have higher numbers.
SaveFile (string) - specifies the path and name of the redirected save file.
  This setting is only effective if EnableSaveFileRedirection in cpkredir.ini
  is set to true and no SaveFileOverride is used. This allows the creator of a
  mod to supply a custom save file, e.g. one where red rings still need to be
  collected in the levels. It is meant for more elaborate mods that make large
  changes to the hub world and the levels. Mods that replace Sonic with some
  other character, or change HUD elements etc. should NOT use this setting, to
  avoid interfering with other mods that actually need it.
  Note that any custom save file data will be reset to the game's default values
  when the user chooses "New Game" in the main menu of Sonic Generations, which
  could have undesirable effects on your mod. Unfortunately, at the moment it is
  not possible to automatically load a custom initial save file when a
  "New Game" is activated. Nevertheless, I suggest that you provide an additional
  copy of your initial save file, if your mod uses this feature. This copy or
  backup could have the file extension ".new.sav" (Example: if your mod INI
  contains SaveFile=mysave.sav, then provide mysave.sav of course, but also
  mysave.new.sav as backup of the initial state.)

The IncludeDir mechanism is very similar to CPKREDIR's simple mode and should
suffice for most mods. However, there are times when more control is required.
That's what the optional "CPKs" section is for.
Here's another example mod INI:

[Desc]
Title="Classic Sonic Adventures"
Description="Swaps Acts 1 and 2, so that classic Sonic can play the modern ..."
Version="1.0"
Date="2011-11-21"
Author="Dario FF"
URL="http://forums.sonicretro.org/index.php?showtopic=27015"

[CPKs]
disk\bb.cpk="bb.ini"
disk\bb2.cpk="bb2.ini"
disk\bb3.cpk="bb3.ini"

As you can see, this one has no IncludeDirs at all (so it should better contain
"IncludeDirCount=0", but that's not absolutely necessary).
Instead, it uses the "CPKs" section to associate some of the game's CPK archives
with the final part of the INI chain, the CPK INI.
As with the subfolders in the IncludeDirs, CPKREDIR will fall back to the bare
name of the CPK. So, if Sonic Generations opens "disk\bb.cpk", CPKREDIR will
go to the "CPKs" section and first look for a key called exactly this. If that
key does not exist, CPKREDIR will try the key "bb.cpk" instead.

6.3. The CPK INI
----------------
A CPK INI requires a "Main" section with a format that should have become
familiar by now. But it also introduces some differences. Its purpose is to
allow manipulations of the CPK internal file system.

[Main]
CommandCount=2
Command0=Swap:SwapGHZ
Command1=Add:AddFixed

[SwapGHZ]
; Green Hill Zone file swap
#ghz100.arl   = #ghz200.arl
#ghz100.ar.00 = #ghz200.ar.00
ghz100.arl    = ghz200.arl
ghz100.ar.00  = ghz200.ar.00
ghz100.ar.01  = ghz200.ar.01
; Green Hill Zone stage swap
Packed\ghz100\ = Packed\ghz200\
Packed\ghz100\ghz200.arl   = Packed\ghz100\ghz100.arl
Packed\ghz100\ghz200.ar.00 = Packed\ghz100\ghz100.ar.00
Packed\ghz100\ghz200.ar.01 = Packed\ghz100\ghz100.ar.01
Packed\ghz100\ghz200.ar.02 = Packed\ghz100\ghz100.ar.02
Packed\ghz100\ghz200.ar.03 = Packed\ghz100\ghz100.ar.03
Packed\ghz200\ghz100.arl   = Packed\ghz200\ghz200.arl
Packed\ghz200\ghz100.ar.00 = Packed\ghz200\ghz200.ar.00
Packed\ghz200\ghz100.ar.01 = Packed\ghz200\ghz200.ar.01
Packed\ghz200\ghz100.ar.02 = Packed\ghz200\ghz200.ar.02

[AddFixed]
; add fixed files
#ghz100.ar.00

CommandCount (integer) - do I still need to explain this? Defines the highest
  "Command" slot number that CPKREDIR will execute. As with "IncludeDirCount",
  the default is 10 and the maximum is 1000.
Command (string) - Slots whose values have the form "CommandName:SectionName".
  The slots are executed in sequence, according to their number, from low to
  high (0..N). Missing slot numbers are ignored.
  SectionName can be chosen arbitrarily, CommandName must be one out of this
  list: "add, copy, delete, move, swap, size0" (all case insensitive).
  
6.3.1. The CPK INI commands
---------------------------
Every command executes the lines in its section one after the other, in their
given order.
All directory operations are recursive and will affect all subdirectories.
If an operand refers to a directory rather than a file, it is recommended, but
not strictly necessary, to use trailing backslashes. In the above example, you
can see this in action in the "SwapGHZ" section with the line
"Packed\ghz100\ = Packed\ghz200\".
To refer to the internal root directory of a CPK, use a single backslash "\".

The ADD command:
  Allows you to add external files to the virtual CPK. You can also add entire
  directories, but for that you might just as well use the IncludeDir mechanism
  of the mod INI.
  The lines are expected to be in "InternalName=ExternalName" format, or
  alternatively in just "ExternalName" format. In the latter case, the
  internal name is the external one made relative to the location of the CPK
  INI. If you use relative file names to begin with (you should), as in the
  example above with "#ghz100.ar.00", then the internal name will be exactly
  the same. So, you don't have to write "#ghz100.ar.00=#ghz100.ar.00", but you
  could.
The DELETE command
  Removes single files or entire directories from the virtual CPK.
  The lines are expected to be in "InternalName" format.
The COPY command:
  Allows you to make a copy of a CPK internal file, or a copy of an external
  file that has been "internalized" by a previous ADD command, of course.
  You can also copy entire directories.
  Careful, the lines are expected to be in "TargetName=SourceName" format. This
  is contrary to the common "copy source target" order.
The MOVE command:
  Just like COPY, with the difference that the source is deleted. This basically
  makes it a rename command, "NewName=OldName".
The SWAP command:
  Allows you to swap two files, or two directories, and expects lines in
  "NameA=NameB" format. It could be emulated with three MOVEs (tmp=NameA, 
  NameA=NameB, NameB=tmp), but having a dedicated command is more convenient,
  of course. For the sake of convenience and simplicity, SWAP can also be
  abused as a move/rename command, if either "NameA" or "NameB" doesn't exist.
  In the example above, "ghz100.ar.01" does not exist in the CPK, so the line
  "ghz100.ar.01 = ghz200.ar.01" will not really swap anything, but just rename
  "ghz200.ar.01" to "ghz100.ar.01".
The SIZE0 command:
  Sets the file size of a CPK internal file to zero. If the specified file does
  not exist, then an entry for it will be created, with a file size of zero.
  This probably isn't of much use. However, if there's a case where you want to
  DELETE a file from a CPK, but the game crashes, you could try this command
  instead, as it basically does delete a file's data, but not its entry in the
  table of contents. Maybe the game will like that better, maybe not.
  The lines are expected to be in "InternalName" format.

Note: CPK INIs have a higher priority than the IncludeDirs of mod INIs.
For ReverseLoadOrder=False this means that the CPK INI is parsed first and then
the external IncludeDir files are added, but only if they do not overwrite any
existing links to external files. 
For ReverseLoadOrder=True this means that IncludeDir files are added first, then
the CPK INI is executed and it will be able to overwrite existing external links
(add) as well as be able to copy/delete/move/swap both internal and external
links.



7. Acknowledgements
-------------------
This project is inspired by the amazing work that people such as Dario FF,
Twilightzoney and Chimera are doing with porting Sonic Unleashed levels to Sonic
Generations and reverse-engineering the necessary formats.

Kudos to Sega and Sonic Team for creating a pretty great game and to all people
who are making mods for it.

CPKREDIR is powered by Embarcadero Delphi.
