320 lines
8.1 KiB
C++
320 lines
8.1 KiB
C++
![]() |
/*
|
||
|
** Command & Conquer Generals Zero Hour(tm)
|
||
|
** Copyright 2025 Electronic Arts 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, either version 3 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, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
|
||
|
***********************************************************************************************
|
||
|
* *
|
||
|
* Project Name : W3D Tools *
|
||
|
* *
|
||
|
* $Archive:: /Commando/Code/Tools/max2w3d/bchannel.cpp $*
|
||
|
* *
|
||
|
* $Author:: Greg_h $*
|
||
|
* *
|
||
|
* $Modtime:: 10/30/00 5:25p $*
|
||
|
* *
|
||
|
* $Revision:: 6 $*
|
||
|
* *
|
||
|
*---------------------------------------------------------------------------------------------*
|
||
|
* Functions: *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
#include "bchannel.h"
|
||
|
#include "w3d_file.h"
|
||
|
#include "logdlg.h"
|
||
|
#include "exportlog.h"
|
||
|
|
||
|
|
||
|
BitChannelClass::BitChannelClass
|
||
|
(
|
||
|
uint32 id,
|
||
|
int maxframes,
|
||
|
uint32 chntype,
|
||
|
bool default_val
|
||
|
) :
|
||
|
ID(id),
|
||
|
ChannelType(chntype),
|
||
|
MaxFrames(maxframes),
|
||
|
IsEmpty(true),
|
||
|
DefaultVal(default_val),
|
||
|
Data(maxframes),
|
||
|
Begin(0),
|
||
|
End(0)
|
||
|
{
|
||
|
// start "Begin" at the end of the array, whenever we set a value
|
||
|
// at an index less than "Begin", we push "Begin" back.
|
||
|
Begin = MaxFrames;
|
||
|
End = 0;
|
||
|
}
|
||
|
|
||
|
BitChannelClass::~BitChannelClass(void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void BitChannelClass::Set_Bit(int frameidx,bool bit)
|
||
|
{
|
||
|
assert(frameidx >= 0);
|
||
|
assert(frameidx < MaxFrames);
|
||
|
|
||
|
Data[frameidx] = bit;
|
||
|
|
||
|
if (!is_default(bit)) {
|
||
|
IsEmpty = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void BitChannelClass::Set_Bits(BooleanVectorClass & bits)
|
||
|
{
|
||
|
for (int i=0; i<bits.Length(); i++) {
|
||
|
Set_Bit(i,bits[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
bool BitChannelClass::Get_Bit(int frameidx)
|
||
|
{
|
||
|
assert(frameidx >= 0);
|
||
|
assert(frameidx < MaxFrames);
|
||
|
|
||
|
return Data[frameidx];
|
||
|
}
|
||
|
|
||
|
|
||
|
bool BitChannelClass::Save(ChunkSaveClass & csave, bool compress)
|
||
|
{
|
||
|
if (IsEmpty) return true;
|
||
|
|
||
|
if (compress) {
|
||
|
// Save the Channel Data Compressed
|
||
|
// TIMECODED
|
||
|
if (!csave.Begin_Chunk(W3D_CHUNK_COMPRESSED_BIT_CHANNEL)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
uint32 channelsize = sizeof(W3dTimeCodedBitChannelStruct);
|
||
|
uint32 packetsize = sizeof(uint32);
|
||
|
|
||
|
channelsize += packetsize * MaxFrames;
|
||
|
channelsize -= sizeof(uint32);
|
||
|
|
||
|
W3dTimeCodedBitChannelStruct * chn = (W3dTimeCodedBitChannelStruct *)malloc(channelsize);
|
||
|
|
||
|
if (chn == NULL) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
chn->NumTimeCodes = MaxFrames;
|
||
|
chn->Pivot = ID;
|
||
|
chn->Flags = ChannelType;
|
||
|
chn->DefaultVal = DefaultVal;
|
||
|
|
||
|
// copy data into the channel struct, in timecoded raw format
|
||
|
|
||
|
for (uint32 fcount=0; fcount < chn->NumTimeCodes; fcount++) {
|
||
|
|
||
|
if (Get_Bit(fcount)) {
|
||
|
chn->Data[fcount] = fcount | W3D_TIMECODED_BIT_MASK;
|
||
|
}
|
||
|
else {
|
||
|
chn->Data[fcount] = fcount;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Compress the new structure
|
||
|
|
||
|
BitChannelClass::compress( chn );
|
||
|
|
||
|
float originalchannelsize = channelsize;
|
||
|
|
||
|
// Update Channel Size
|
||
|
channelsize = sizeof(W3dTimeCodedBitChannelStruct);
|
||
|
channelsize += packetsize * chn->NumTimeCodes;
|
||
|
channelsize -= sizeof(uint32);
|
||
|
|
||
|
float percent = (((float) channelsize) / originalchannelsize) * 100.0f;
|
||
|
|
||
|
ExportLog::printf("%.0f", percent);
|
||
|
|
||
|
|
||
|
// save
|
||
|
|
||
|
if (csave.Write(chn,channelsize) != channelsize) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (chn != NULL) {
|
||
|
free(chn);
|
||
|
}
|
||
|
|
||
|
if (!csave.End_Chunk()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
// Stock Raw Save
|
||
|
if (!csave.Begin_Chunk(W3D_CHUNK_BIT_CHANNEL)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
compute_range();
|
||
|
|
||
|
int numbits = End - Begin + 1;
|
||
|
assert(numbits > 0);
|
||
|
int numbytes = (numbits + 7) / 8;
|
||
|
|
||
|
unsigned int channelsize = sizeof(W3dBitChannelStruct);
|
||
|
channelsize += numbytes - 1; // one byte inside the W3dBitChannelStruct...
|
||
|
|
||
|
W3dBitChannelStruct * chn = (W3dBitChannelStruct *)malloc(channelsize);
|
||
|
|
||
|
if (chn == NULL) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
chn->FirstFrame = Begin;
|
||
|
chn->LastFrame = End;
|
||
|
chn->Flags = ChannelType;
|
||
|
chn->Pivot = ID;
|
||
|
chn->DefaultVal = DefaultVal;
|
||
|
|
||
|
uint8 * bits = (uint8 *)&(chn->Data[0]);
|
||
|
|
||
|
for (int fcount=0; fcount < End-Begin+1; fcount++) {
|
||
|
::Set_Bit(bits,fcount,Get_Bit(Begin + fcount));
|
||
|
}
|
||
|
|
||
|
if (csave.Write(chn,channelsize) != channelsize) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (chn != NULL) {
|
||
|
free(chn);
|
||
|
}
|
||
|
|
||
|
if (!csave.End_Chunk()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool BitChannelClass::is_default(bool bit)
|
||
|
{
|
||
|
return (bit == DefaultVal);
|
||
|
}
|
||
|
|
||
|
void BitChannelClass::compute_range(void)
|
||
|
{
|
||
|
Begin = 0;
|
||
|
while ((Begin < MaxFrames) && (is_default(Get_Bit(Begin)))) {
|
||
|
Begin++;
|
||
|
}
|
||
|
|
||
|
End = MaxFrames-1;
|
||
|
while ((End >= 0) && (is_default(Get_Bit(End)))) {
|
||
|
End--;
|
||
|
}
|
||
|
} // compute_range
|
||
|
|
||
|
|
||
|
//
|
||
|
// find a packet that isn't needed, and return the index
|
||
|
// if all packets are necessary, then return back PACKETS_ALL_USEFUL
|
||
|
// a useless packet is defined, as a packet that can be recreated
|
||
|
//
|
||
|
#define PACKETS_ALL_USEFUL (0xFFFFFFFF)
|
||
|
//
|
||
|
uint32 BitChannelClass::find_useless_packet(W3dTimeCodedBitChannelStruct * c)
|
||
|
{
|
||
|
|
||
|
assert( c ); // make sure pointer exists
|
||
|
assert( c->NumTimeCodes ); // make sure some packets exist
|
||
|
|
||
|
if (c->NumTimeCodes > 2) {
|
||
|
|
||
|
for(uint32 try_idx = 0; try_idx < (c->NumTimeCodes - 1); try_idx++) {
|
||
|
|
||
|
if ((c->Data[try_idx] & W3D_TIMECODED_BIT_MASK) ==
|
||
|
(c->Data[try_idx+1] & W3D_TIMECODED_BIT_MASK)) {
|
||
|
return(try_idx + 1);
|
||
|
}
|
||
|
|
||
|
} // for
|
||
|
}
|
||
|
|
||
|
return( PACKETS_ALL_USEFUL );
|
||
|
|
||
|
} // find_useless_packet
|
||
|
|
||
|
|
||
|
//
|
||
|
// Remove a packet from a W3dTimeCodedBitChannelStruct
|
||
|
//
|
||
|
void BitChannelClass::remove_packet(W3dTimeCodedBitChannelStruct * c, uint32 packet_idx)
|
||
|
{
|
||
|
assert( c );
|
||
|
assert( c->NumTimeCodes > 1 );
|
||
|
|
||
|
uint32 packet_size = 1;
|
||
|
uint32 packet_len = packet_size * sizeof(uint32);
|
||
|
|
||
|
uint32 *src, *dst;
|
||
|
|
||
|
dst = (uint32 *) &c->Data[ packet_size * packet_idx ];
|
||
|
src = (uint32 *) &c->Data[ packet_size * (packet_idx + 1) ];
|
||
|
|
||
|
uint32 copy_length = (c->NumTimeCodes - (packet_idx + 1)) * packet_len;
|
||
|
|
||
|
if (copy_length) {
|
||
|
|
||
|
memcpy(dst, src, copy_length);
|
||
|
|
||
|
}
|
||
|
|
||
|
// Decrement Packet Count
|
||
|
c->NumTimeCodes--;
|
||
|
|
||
|
} // remove_packet
|
||
|
|
||
|
//
|
||
|
// Take a non-compressed TimeCoded Bit Channel
|
||
|
// and compress the packets
|
||
|
//
|
||
|
void BitChannelClass::compress(W3dTimeCodedBitChannelStruct * c)
|
||
|
{
|
||
|
while(1) {
|
||
|
|
||
|
uint32 idx = find_useless_packet( c );
|
||
|
|
||
|
if (PACKETS_ALL_USEFUL == idx) break;
|
||
|
|
||
|
remove_packet( c, idx );
|
||
|
|
||
|
}
|
||
|
|
||
|
} // compress
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// EOF - bchannel.cpp
|