/* .-=[ rch2extract ]======================================================-. *\
* | RCH 2.0 File Extractor 0.1 (c) 2007 by IceDragon of QuickFox.org | *
* |---------------------------------------------[ icedragon@quickfox.org ]-| *
* | This little program finds and extracts the RCH2 package from DEP's | *
* | Furcadia installer executable (furcsetup*.exe) and stores it on disk | *
* | for further manipulation. | *
* | | *
* | This program is fully compatible with compatible with furcsetup023.exe | *
* | and furcsetup024.exe installers. Others were not tested, but should | *
* | work regardless. | *
* |--[ Changelog ]---------------------------------------------------------| *
* | 0.1 - Initial release. | *
\* '-==============================================================[ 0.1 ]=-' */
/*** Includes ***/
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <cstring>
/*** Definitions ***/
#define min(a,b) (a > b) ? b : a
#define RCH2_HEADER "RCH2.0FZ"
using namespace std;
/*** Global Variables ***/
// List of possible offsets.
int rch2offsets[] = { 0x2f000, 0x30000, -1 };
// Input file descriptor.
ifstream fd;
/*** Functions ***/
// This function locates the RCH2.0 header within the file. First it tries the
// built-in locations in rch2offsets[] above, but if the header is not there,
// the system would perform a more primitive search to try and find the header
// manually.
int findHeader
()
{
char header[6];
int i = 0;
// Preset offset search.
while( rch2offsets[ i ] >= 0 )
{
fd.seekg( rch2offsets[i], ios::beg );
fd.read( header, 6 );
if( !strncmp( header, RCH2_HEADER, 6 ) )
{
fd.seekg( rch2offsets[i], ios::beg );
cout << " " << rch2offsets[i];
return rch2offsets[i];
}
i++;
}
// Searching further.
fd.seekg( 0, ios::beg );
int j,count;
char buffer[4096];
bool loop = true;
while( loop )
{
fd.read( buffer, 4096 );
count = fd.gcount();
for( i = 0; (i+8) < count; i++ )
{
for( j = 0; j < 8; j++ )
if( buffer[i+j] != RCH2_HEADER[j] )
break;
if( j >= 8 )
{
fd.seekg( (count * -1) + i, ios::cur );
cout << " " << fd.tellg();
return fd.tellg();
}
}
if( fd.eof() )
loop = false;
}
return -1; // Not found
}
// This function searches for the overall filesize of the RCH2 file so we can
// later extract it in an easy way from the EXE. Of course we could extract
// during the read, but it's a lot more complex and bug-prone IMO.
streamsize getFilesize
()
{
streamsize startPoint = fd.tellg();
streamsize fileSize;
fd.seekg( 6, ios::cur );
char buffer[4096];
bool loop = true;
int fnSize;
int fSize;
while ( loop )
{
fd.read( buffer, 2 );
if( strncmp( buffer, "FZ", 2 ) )
{
fd.seekg( -2, ios::cur );
break;
}
fd.read( buffer, 2 );
memcpy( &fnSize, buffer, 2 );
if( fnSize > 20000 )
{
fd.seekg( -2, ios::cur );
break;
}
fd.seekg( fnSize, ios::cur );
fd.read( buffer, 4 );
memcpy( &fSize, buffer, 4 );
fd.seekg( 4+fSize, ios::cur );
if( fd.eof() )
loop = false;
}
fileSize = (streamsize)fd.tellg() - startPoint;
fd.seekg( startPoint, ios::beg );
return fileSize;
}
/*** Main Function ***/
int main
( int argc, char *argv[] )
{
string fileName;
string targetFilename;
// Banner
cout << "--+ RCH2.0 Extractor (c) 2007 by IceDragon <icedragon@quickfox.org> +--" << endl << endl;
// Setting filename
if( argc < 2 )
{
cout << "Syntax: " << argv[0] << " <filename> [output_file]" << endl;
return -1;
}
else
{
fileName = argv[1];
targetFilename = ( argc > 2 ) ? argv[2] : "output.rc2";
}
// Opening installer
cout << ".. Opening file...";
fd.open( fileName.c_str(), ios::binary );
if( !fd.is_open() )
{
cout << " FAILED!" << endl;
return -2;
}
else
cout << " OK" << endl;
// Looking for RCH2 inside installer.
cout << ".. Searching for RCH2 signature...";
if( findHeader() < 0 )
{
fd.close();
cout << " FAILED!" << endl;
return -3;
}
else
cout << " OK" << endl;
// Calculating file size.
cout << ".. Calculating size...";
streamsize fileSize = getFilesize();
cout << " " << fileSize << " bytes" << endl;
// Extracting data.
cout << ".. Extracting data...";
ofstream fdOut ( targetFilename.c_str(), ios::binary );
if( !fdOut.is_open() )
{
cout << "ERROR - Unable to open target file " << targetFilename << "!" << endl;
fd.close();
return -4;
}
char buffer[4096];
while( fileSize )
{
fd.read( buffer, min( 4096, fileSize ) );
if( fd.bad() )
{
fd.close();
fdOut.close();
cout << "ERROR - Reading interrupted!" << endl;
return -5;
}
fdOut.write( buffer, fd.gcount() );
fileSize -= fd.gcount();
}
fd.close();
fdOut.close();
cout << " OK" << endl;
return EXIT_SUCCESS;
}
/*** End of File ***/