Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
695 lines
18 KiB
C++
695 lines
18 KiB
C++
#include "ProxyHeaders.hpp"
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Proxies::WriteTargaFile(
|
|
const char* filename,
|
|
int width,
|
|
int height,
|
|
DynamicArrayOf<TargaColor> &data
|
|
)
|
|
{
|
|
Check_Pointer(filename);
|
|
Check_Object(&data);
|
|
|
|
//
|
|
//---------------------------------------------
|
|
// Get the size of the image we will be copying
|
|
//---------------------------------------------
|
|
//
|
|
TargaHeader targa_header;
|
|
memset(&targa_header, 0, sizeof(targa_header));
|
|
targa_header.widthLow = static_cast<unsigned char>(width & 255);
|
|
targa_header.widthHigh = static_cast<unsigned char>(width >> 8);
|
|
targa_header.heightLow = static_cast<unsigned char>(height & 255);
|
|
targa_header.heightHigh = static_cast<unsigned char>(height >> 8);
|
|
targa_header.imageType = TargaHeader::RLERGB;
|
|
targa_header.pixelSize = 24;
|
|
targa_header.flags = TargaHeader::YReversed;
|
|
|
|
//
|
|
//---------------------
|
|
// Write out the header
|
|
//---------------------
|
|
//
|
|
Check_Object(FileStreamManager::Instance);
|
|
FileStream targa_file(filename, FileStream::WriteOnly);
|
|
targa_file.WriteBytes(&targa_header, sizeof(targa_header));
|
|
|
|
//
|
|
//----------------------------------------------
|
|
// See if we need to deal with the alpha channel
|
|
//----------------------------------------------
|
|
//
|
|
unsigned channel_size = height * width;
|
|
Verify(channel_size == data.GetLength());
|
|
unsigned pixel_size = 3;
|
|
unsigned buffer_size = channel_size * pixel_size;
|
|
DynamicArrayOf<BYTE> buffer(buffer_size);
|
|
unsigned p = 0;
|
|
unsigned max_row = Min(width, 128);
|
|
|
|
//
|
|
//-------------------------------------
|
|
// Start trying to make an RLE encoding
|
|
//-------------------------------------
|
|
//
|
|
int i=0;
|
|
while (i<channel_size)
|
|
{
|
|
unsigned rle_count;
|
|
int rle_type = 0;
|
|
unsigned j = i+1;
|
|
unsigned base = i;
|
|
unsigned max_run = max_row - (i%max_row);
|
|
for (rle_count = 1; rle_count<max_run && j<channel_size; ++rle_count, ++j)
|
|
{
|
|
//
|
|
//-------------------------------------
|
|
// See if the next pixel matches or not
|
|
//-------------------------------------
|
|
//
|
|
bool match =
|
|
data[j].red == data[base].red
|
|
&& data[j].blue == data[base].blue
|
|
&& data[j].green == data[base].green;
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// If it matches, we break the run if this is a singleton run. We
|
|
// will always try to match matching pixels in a run
|
|
//----------------------------------------------------------------
|
|
//
|
|
if (match)
|
|
{
|
|
if (rle_type == -1)
|
|
{
|
|
--j;
|
|
--rle_count;
|
|
break;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------
|
|
// Matching runs can go one longer than singleton runs
|
|
//----------------------------------------------------
|
|
//
|
|
else if (!rle_type)
|
|
rle_type = 1;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------
|
|
// It didn't match, so we better not be in a matching run. If we
|
|
// aren't, reset the base to this pixel
|
|
//-------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
if (rle_type == 1)
|
|
break;
|
|
base = j;
|
|
rle_type = -1;
|
|
}
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Write out the run based upon its type after making sure that there is
|
|
// enough room in the buffer
|
|
//----------------------------------------------------------------------
|
|
//
|
|
if (rle_type == 1)
|
|
{
|
|
if (buffer_size-p < pixel_size+1)
|
|
goto No_RLE;
|
|
Verify(rle_count > 0 && rle_count <= 128);
|
|
buffer[p++] = static_cast<BYTE>(rle_count + 127);
|
|
buffer[p++] = data[i].blue;
|
|
buffer[p++] = data[i].green;
|
|
buffer[p++] = data[i].red;
|
|
i = j;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------
|
|
// This is a singleton run, so check out its space requirements
|
|
//-------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
if (buffer_size-p < (pixel_size*rle_count)+1)
|
|
goto No_RLE;
|
|
|
|
//
|
|
//---------------
|
|
// Write them out
|
|
//---------------
|
|
//
|
|
Verify(rle_count > 0 && rle_count <= 128);
|
|
buffer[p++] = static_cast<BYTE>(rle_count-1);
|
|
for (; i<j; ++i)
|
|
{
|
|
buffer[p++] = data[i].blue;
|
|
buffer[p++] = data[i].green;
|
|
buffer[p++] = data[i].red;
|
|
}
|
|
}
|
|
}
|
|
goto Write_File;
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Interleave the channels w/no RLE. This should only be used if the RLE
|
|
// takes more space than the buffer allows
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
No_RLE:
|
|
targa_file.Close();
|
|
targa_file.Open(filename, FileStream::WriteOnly);
|
|
targa_header.imageType = TargaHeader::RGB;
|
|
targa_file.WriteBytes(&targa_header, sizeof(targa_header));
|
|
for (i=0,p=0; i<channel_size; ++i)
|
|
{
|
|
buffer[p++] = data[i].blue;
|
|
buffer[p++] = data[i].green;
|
|
buffer[p++] = data[i].red;
|
|
}
|
|
goto Write_File;
|
|
|
|
//
|
|
//------------------------------------
|
|
// Write out the buffer and discard it
|
|
//------------------------------------
|
|
//
|
|
Write_File:
|
|
Verify(i == channel_size);
|
|
Verify(p <= buffer_size);
|
|
targa_file.WriteBytes(&buffer[0], p);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Proxies::WriteTargaFile(
|
|
const char* filename,
|
|
int width,
|
|
int height,
|
|
DynamicArrayOf<TargaColorA> &data
|
|
)
|
|
{
|
|
Check_Pointer(filename);
|
|
Check_Object(&data);
|
|
|
|
//
|
|
//---------------------------------------------
|
|
// Get the size of the image we will be copying
|
|
//---------------------------------------------
|
|
//
|
|
TargaHeader targa_header;
|
|
memset(&targa_header, 0, sizeof(targa_header));
|
|
targa_header.widthLow = static_cast<unsigned char>(width & 255);
|
|
targa_header.widthHigh = static_cast<unsigned char>(width >> 8);
|
|
targa_header.heightLow = static_cast<unsigned char>(height & 255);
|
|
targa_header.heightHigh = static_cast<unsigned char>(height >> 8);
|
|
targa_header.imageType = TargaHeader::RLERGB;
|
|
targa_header.pixelSize = 32;
|
|
targa_header.flags = TargaHeader::YReversed;
|
|
|
|
//
|
|
//---------------------
|
|
// Write out the header
|
|
//---------------------
|
|
//
|
|
Check_Object(FileStreamManager::Instance);
|
|
FileStream targa_file(filename, FileStream::WriteOnly);
|
|
targa_file.WriteBytes(&targa_header, sizeof(targa_header));
|
|
|
|
//
|
|
//----------------------------------------------
|
|
// See if we need to deal with the alpha channel
|
|
//----------------------------------------------
|
|
//
|
|
unsigned channel_size = height * width;
|
|
Verify(channel_size == data.GetLength());
|
|
unsigned pixel_size = 4;
|
|
unsigned buffer_size = channel_size * pixel_size;
|
|
DynamicArrayOf<BYTE> buffer(buffer_size);
|
|
unsigned p = 0;
|
|
unsigned max_row = Min(width, 128);
|
|
|
|
//
|
|
//-------------------------------------
|
|
// Start trying to make an RLE encoding
|
|
//-------------------------------------
|
|
//
|
|
int i=0;
|
|
while (i<channel_size)
|
|
{
|
|
unsigned rle_count;
|
|
int rle_type = 0;
|
|
unsigned j = i+1;
|
|
unsigned base = i;
|
|
unsigned max_run = max_row - (i%max_row);
|
|
for (rle_count = 1; rle_count<max_run && j<channel_size; ++rle_count, ++j)
|
|
{
|
|
//
|
|
//-------------------------------------
|
|
// See if the next pixel matches or not
|
|
//-------------------------------------
|
|
//
|
|
bool match =
|
|
data[j].red == data[base].red
|
|
&& data[j].blue == data[base].blue
|
|
&& data[j].green == data[base].green
|
|
&& data[j].alpha == data[base].alpha;
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// If it matches, we break the run if this is a singleton run. We
|
|
// will always try to match matching pixels in a run
|
|
//----------------------------------------------------------------
|
|
//
|
|
if (match)
|
|
{
|
|
if (rle_type == -1)
|
|
{
|
|
--j;
|
|
--rle_count;
|
|
break;
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------
|
|
// Matching runs can go one longer than singleton runs
|
|
//----------------------------------------------------
|
|
//
|
|
else if (!rle_type)
|
|
rle_type = 1;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------
|
|
// It didn't match, so we better not be in a matching run. If we
|
|
// aren't, reset the base to this pixel
|
|
//-------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
if (rle_type == 1)
|
|
break;
|
|
base = j;
|
|
rle_type = -1;
|
|
}
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Write out the run based upon its type after making sure that there is
|
|
// enough room in the buffer
|
|
//----------------------------------------------------------------------
|
|
//
|
|
if (rle_type == 1)
|
|
{
|
|
if (buffer_size-p < pixel_size+1)
|
|
goto No_RLE;
|
|
Verify(rle_count > 0 && rle_count <= 128);
|
|
buffer[p++] = static_cast<BYTE>(rle_count + 127);
|
|
buffer[p++] = data[i].blue;
|
|
buffer[p++] = data[i].green;
|
|
buffer[p++] = data[i].red;
|
|
buffer[p++] = data[i].alpha;
|
|
i = j;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------
|
|
// This is a singleton run, so check out its space requirements
|
|
//-------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
if (buffer_size-p < (pixel_size*rle_count)+1)
|
|
goto No_RLE;
|
|
|
|
//
|
|
//---------------
|
|
// Write them out
|
|
//---------------
|
|
//
|
|
Verify(rle_count > 0 && rle_count <= 128);
|
|
buffer[p++] = static_cast<BYTE>(rle_count-1);
|
|
for (; i<j; ++i)
|
|
{
|
|
buffer[p++] = data[i].blue;
|
|
buffer[p++] = data[i].green;
|
|
buffer[p++] = data[i].red;
|
|
buffer[p++] = data[i].alpha;
|
|
}
|
|
}
|
|
}
|
|
goto Write_File;
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Interleave the channels w/no RLE. This should only be used if the RLE
|
|
// takes more space than the buffer allows
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
No_RLE:
|
|
targa_file.Close();
|
|
targa_file.Open(filename, FileStream::WriteOnly);
|
|
targa_header.imageType = TargaHeader::RGB;
|
|
targa_file.WriteBytes(&targa_header, sizeof(targa_header));
|
|
for (i=0,p=0; i<channel_size; ++i)
|
|
{
|
|
buffer[p++] = data[i].blue;
|
|
buffer[p++] = data[i].green;
|
|
buffer[p++] = data[i].red;
|
|
buffer[p++] = data[i].alpha;
|
|
}
|
|
goto Write_File;
|
|
|
|
//
|
|
//------------------------------------
|
|
// Write out the buffer and discard it
|
|
//------------------------------------
|
|
//
|
|
Write_File:
|
|
Verify(i == channel_size);
|
|
Verify(p <= buffer_size);
|
|
targa_file.WriteBytes(&buffer[0], p);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Proxies::ReadTargaHeader(
|
|
TargaHeader *header,
|
|
MemoryStream *file,
|
|
int *width,
|
|
int *height,
|
|
unsigned *red_depth,
|
|
unsigned *green_depth,
|
|
unsigned *blue_depth,
|
|
unsigned *alpha_depth
|
|
)
|
|
{
|
|
Check_Pointer(header);
|
|
Check_Object(file);
|
|
Check_Pointer(width);
|
|
Check_Pointer(height);
|
|
Check_Pointer(red_depth);
|
|
Check_Pointer(green_depth);
|
|
Check_Pointer(blue_depth);
|
|
Check_Pointer(alpha_depth);
|
|
|
|
//
|
|
//---------------------------------------------
|
|
// Get the size of the image we will be copying
|
|
//---------------------------------------------
|
|
//
|
|
file->ReadBytes(header, sizeof(*header));
|
|
*width =
|
|
static_cast<int>(header->widthLow)
|
|
+ (static_cast<int>(header->widthHigh)<<8);
|
|
*height =
|
|
static_cast<int>(header->heightLow)
|
|
+ (static_cast<int>(header->heightHigh)<<8);
|
|
Verify(header->pixelSize == 24 || header->pixelSize == 32);
|
|
*red_depth = 8;
|
|
*green_depth = 8;
|
|
*blue_depth = 8;
|
|
*alpha_depth = (header->pixelSize == 24) ? 0 : 8;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Proxies::ReadTargaChannels(
|
|
MemoryStream *file,
|
|
TargaHeader *header,
|
|
DynamicArrayOf<TargaColor> *data
|
|
)
|
|
{
|
|
Check_Object(file);
|
|
Check_Pointer(header);
|
|
Check_Object(data);
|
|
|
|
//
|
|
//----------------------------
|
|
// Read the file into a buffer
|
|
//----------------------------
|
|
//
|
|
unsigned len = file->GetBytesRemaining();
|
|
DynamicArrayOf<BYTE> buffer(len);
|
|
file->ReadBytes(&buffer[0], len);
|
|
|
|
//
|
|
//-----------------------------
|
|
// Set up the process variables
|
|
//-----------------------------
|
|
//
|
|
bool
|
|
x_reversed = ((header->flags & TargaHeader::XReversed) != 0),
|
|
y_reversed = ((header->flags & TargaHeader::YReversed) != 0),
|
|
compressed = (header->imageType == TargaHeader::RLERGB);
|
|
unsigned width =
|
|
static_cast<int>(header->widthLow)
|
|
+ (static_cast<int>(header->widthHigh)<<8);
|
|
unsigned height =
|
|
static_cast<int>(header->heightLow)
|
|
+ (static_cast<int>(header->heightHigh)<<8);
|
|
BYTE
|
|
run_count = 0,
|
|
copy_count = 0,
|
|
control_byte = 0,
|
|
red = 0,
|
|
green = 0,
|
|
blue = 0;
|
|
unsigned
|
|
dest = ((y_reversed) ? 0 : width * (height-1)),
|
|
source = 0;
|
|
if (x_reversed)
|
|
dest += width;
|
|
|
|
//
|
|
//------------------------
|
|
// Spin through each texel
|
|
//------------------------
|
|
//
|
|
for (unsigned y=0; y<height; ++y)
|
|
{
|
|
for (unsigned x=0; x<width; ++x)
|
|
{
|
|
Verify(
|
|
dest == ((y_reversed)?y:(height-1-y))*width + (x_reversed)?(width-x):x
|
|
);
|
|
|
|
//
|
|
//-----------------------------------------
|
|
// Handle the control bytes for compression
|
|
//-----------------------------------------
|
|
//
|
|
if (compressed)
|
|
{
|
|
if (!copy_count && !run_count)
|
|
control_byte=buffer[source++];
|
|
if (!run_count)
|
|
{
|
|
blue = buffer[source++];
|
|
green = buffer[source++];
|
|
red = buffer[source++];
|
|
}
|
|
if (!copy_count && !run_count)
|
|
{
|
|
if (control_byte >= 128)
|
|
run_count = static_cast<BYTE>(control_byte-128);
|
|
else
|
|
copy_count = control_byte;
|
|
}
|
|
else if (run_count)
|
|
run_count--;
|
|
else if (copy_count)
|
|
copy_count--;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------
|
|
// Otherwise, just get the next color
|
|
//-----------------------------------
|
|
//
|
|
else
|
|
{
|
|
blue = buffer[source++];
|
|
green = buffer[source++];
|
|
red = buffer[source++];
|
|
}
|
|
|
|
//
|
|
//------------------------------
|
|
// Put the color in the channels
|
|
//------------------------------
|
|
//
|
|
if (x_reversed)
|
|
{
|
|
(*data)[--dest].red = red;
|
|
(*data)[dest].green = green;
|
|
(*data)[dest].blue = blue;
|
|
}
|
|
else
|
|
{
|
|
(*data)[dest].red = red;
|
|
(*data)[dest].green = green;
|
|
(*data)[dest++].blue = blue;
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------------------
|
|
// Move the destination pointer
|
|
//-----------------------------
|
|
//
|
|
if (x_reversed == y_reversed)
|
|
dest += ((y_reversed) ? 2 : -2) * width;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Proxies::ReadTargaChannels(
|
|
MemoryStream *file,
|
|
TargaHeader *header,
|
|
DynamicArrayOf<TargaColorA> *data
|
|
)
|
|
{
|
|
Check_Object(file);
|
|
Check_Pointer(header);
|
|
Check_Object(data);
|
|
|
|
//
|
|
//----------------------------
|
|
// Read the file into a buffer
|
|
//----------------------------
|
|
//
|
|
unsigned len = file->GetBytesRemaining();
|
|
DynamicArrayOf<BYTE> buffer(len);
|
|
file->ReadBytes(&buffer[0], len);
|
|
|
|
//
|
|
//-----------------------------
|
|
// Set up the process variables
|
|
//-----------------------------
|
|
//
|
|
bool
|
|
x_reversed = ((header->flags & TargaHeader::XReversed) != 0),
|
|
y_reversed = ((header->flags & TargaHeader::YReversed) != 0),
|
|
compressed = (header->imageType == TargaHeader::RLERGB);
|
|
unsigned width =
|
|
static_cast<int>(header->widthLow)
|
|
+ (static_cast<int>(header->widthHigh)<<8);
|
|
unsigned height =
|
|
static_cast<int>(header->heightLow)
|
|
+ (static_cast<int>(header->heightHigh)<<8);
|
|
BYTE
|
|
run_count = 0,
|
|
copy_count = 0,
|
|
control_byte = 0,
|
|
red = 0,
|
|
green = 0,
|
|
blue = 0,
|
|
alpha = 0;
|
|
unsigned
|
|
dest = ((y_reversed) ? 0 : width * (height-1)),
|
|
source = 0;
|
|
if (x_reversed)
|
|
dest += width;
|
|
|
|
//
|
|
//------------------------
|
|
// Spin through each texel
|
|
//------------------------
|
|
//
|
|
for (unsigned y=0; y<height; ++y)
|
|
{
|
|
for (unsigned x=0; x<width; ++x)
|
|
{
|
|
Verify(
|
|
dest == ((y_reversed)?y:(height-1-y))*width + (x_reversed)?(width-x):x
|
|
);
|
|
|
|
//
|
|
//-----------------------------------------
|
|
// Handle the control bytes for compression
|
|
//-----------------------------------------
|
|
//
|
|
if (compressed)
|
|
{
|
|
if (!copy_count && !run_count)
|
|
control_byte=buffer[source++];
|
|
if (!run_count)
|
|
{
|
|
blue = buffer[source++];
|
|
green = buffer[source++];
|
|
red = buffer[source++];
|
|
alpha = buffer[source++];
|
|
}
|
|
if (!copy_count && !run_count)
|
|
{
|
|
if (control_byte >= 128)
|
|
run_count = static_cast<BYTE>(control_byte-128);
|
|
else
|
|
copy_count = control_byte;
|
|
}
|
|
else if (run_count)
|
|
run_count--;
|
|
else if (copy_count)
|
|
copy_count--;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------
|
|
// Otherwise, just get the next color
|
|
//-----------------------------------
|
|
//
|
|
else
|
|
{
|
|
blue = buffer[source++];
|
|
green = buffer[source++];
|
|
red = buffer[source++];
|
|
alpha = buffer[source++];
|
|
}
|
|
|
|
//
|
|
//------------------------------
|
|
// Put the color in the channels
|
|
//------------------------------
|
|
//
|
|
if (x_reversed)
|
|
{
|
|
(*data)[--dest].red = red;
|
|
(*data)[dest].green = green;
|
|
(*data)[dest].blue = blue;
|
|
(*data)[dest].alpha = alpha;
|
|
}
|
|
else
|
|
{
|
|
(*data)[dest].red = red;
|
|
(*data)[dest].green = green;
|
|
(*data)[dest].blue = blue;
|
|
(*data)[dest++].alpha = alpha;
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------------------
|
|
// Move the destination pointer
|
|
//-----------------------------
|
|
//
|
|
if (x_reversed == y_reversed)
|
|
dest += ((y_reversed) ? 2 : -2) * width;
|
|
}
|
|
}
|