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
 |