CnC_Remastered_Collection/TIBERIANDAWN/WINSTUB.CPP

938 lines
32 KiB
C++

//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code 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.
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
/***********************************************************************************************
*** 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 : Command & Conquer *
* *
* File Name : WINSTUB.CPP *
* *
* Programmer : Steve Tall *
* *
* Start Date : 10/04/95 *
* *
* Last Update : October 4th 1995 [ST] *
* *
*---------------------------------------------------------------------------------------------*
* Overview: *
* This file contains stubs for undefined externals when linked under Watcom for Win 95 *
* *
*---------------------------------------------------------------------------------------------*
* *
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "function.h"
#include "tcpip.h"
void output(short,short)
{}
bool InDebugger = false;
bool ReadyToQuit = false;
#if (0)
/***************************************************************************
* Extract_Shape_Count -- returns # of shapes in the given shape block *
* *
* The # of shapes in a shape block is the first WORD in the block, so *
* this is the value returned. *
* *
* INPUT: *
* buffer pointer to shape block, created with MAKESHPS.EXE *
* *
* OUTPUT: *
* # shapes in the block *
* *
* WARNINGS: *
* none *
* *
* HISTORY: *
* 06/09/1992 JLB : Created. *
* 08/19/1993 SKB : Split drawshp.asm into several modules. *
* 05/25/1994 BR : Converted to 32-bit *
*=========================================================================*/
int __cdecl Extract_Shape_Count(VOID const *buffer)
{
ShapeBlock_Type *block = (ShapeBlock_Type *)buffer;
return (block->NumShapes);
} /* end of Extract_Shape_Count */
/***************************************************************************
* Extract_Shape -- Gets pointer to shape in given shape block *
* *
* INPUT: *
* buffer pointer to shape block, created with MAKESHPS.EXE *
* shape index of shape to get *
* *
* OUTPUT: *
* pointer to shape in the shape block *
* *
* WARNINGS: *
* none *
* *
* HISTORY: *
* 06/09/1992 JLB : Created. *
* 08/19/1993 SKB : Split drawshp.asm into several modules. *
* 05/25/1994 BR : Converted to 32-bit *
*=========================================================================*/
VOID * __cdecl Extract_Shape(VOID const *buffer, int shape)
{
ShapeBlock_Type *block = (ShapeBlock_Type*) buffer;
long offset; // Offset of shape data, from start of block
char *bytebuf = (char*) buffer;
/*
----------------------- Return if invalid argument -----------------------
*/
if (!buffer || shape < 0 || shape >= block->NumShapes)
return(NULL);
offset = block->Offsets[shape];
return(bytebuf + 2 + offset);
} /* end of Extract_Shape */
#endif //(0)
unsigned long CCFocusMessage = WM_USER+50; //Private message for receiving application focus
extern void VQA_PauseAudio(void);
extern void VQA_ResumeAudio(void);
ThemeType OldTheme = THEME_NONE;
/***********************************************************************************************
* Focus_Loss -- this function is called when a library function detects focus loss *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 2/1/96 2:10PM ST : Created *
*=============================================================================================*/
void Focus_Loss(void)
{
if (SoundOn){
if (OldTheme == THEME_NONE){
OldTheme = Theme.What_Is_Playing();
}
}
Theme.Stop();
Stop_Primary_Sound_Buffer();
if (WWMouse) WWMouse->Clear_Cursor_Clip();
}
void Focus_Restore(void)
{
Restore_Cached_Icons();
Map.Flag_To_Redraw(true);
Start_Primary_Sound_Buffer(TRUE);
if (WWMouse) WWMouse->Set_Cursor_Clip();
VisiblePage.Clear();
HiddenPage.Clear();
}
/***********************************************************************************************
* Check_For_Focus_Loss -- check for the end of the focus loss *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 2/2/96 10:49AM ST : Created *
*=============================================================================================*/
void Check_For_Focus_Loss(void)
{
// ST - 1/3/2019 10:40AM
#if (0)
static BOOL focus_last_time = 1;
MSG msg;
if ( !GameInFocus ){
Focus_Loss();
while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD ) ){
if( !GetMessage( &msg, NULL, 0, 0 ) ){
return;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
if (!focus_last_time && GameInFocus){
VQA_PauseAudio();
CountDownTimerClass cd;
cd.Set(60*1);
do {
while (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE )) {
if( !GetMessage( &msg, NULL, 0, 0 ) ){
return;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} while(cd.Time());
VQA_ResumeAudio();
//AllSurfaces.Restore_Surfaces();
//VisiblePage.Clear();
//HiddenPage.Clear();
//Map.Flag_To_Redraw(true);
PostMessage (MainWindow, CCFocusMessage,0,0);
}
focus_last_time = GameInFocus;
#endif
}
extern BOOL InMovie;
#if (0) //PG_TO_FIX
long FAR PASCAL _export Windows_Procedure (HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
int low_param = LOWORD(wParam);
if (message == CCFocusMessage){
Start_Primary_Sound_Buffer(TRUE);
if (!InMovie){
Theme.Queue_Song(OldTheme);
OldTheme = THEME_NONE;
}
return(0);
}
switch ( message ){
case WM_MOUSEMOVE:
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDBLCLK:
Kbd.Message_Handler(hwnd, message, wParam, lParam);
return(0);
case WM_DESTROY:
CCDebugString ("C&C95 - WM_DESTROY message received.\n");
CCDebugString ("C&C95 - About to call Prog_End.\n");
Prog_End();
CCDebugString ("C&C95 - About to Invalidate_Cached_Icons.\n");
Invalidate_Cached_Icons();
CCDebugString ("C&C95 - About to release the video surfaces.\n");
VisiblePage.Un_Init();
HiddenPage.Un_Init();
AllSurfaces.Release();
if (!InDebugger){
CCDebugString ("C&C95 - About to reset the video mode.\n");
Reset_Video_Mode();
}
CCDebugString ("C&C95 - About to stop the profiler.\n");
Stop_Profiler();
CCDebugString ("C&C95 - Posting the quit message.\n");
PostQuitMessage( 0 );
/*
** If we are shutting down gracefully than flag that the message loop has finished.
** If this is a forced shutdown (ReadyToQuit == 0) then try and close down everything
** before we exit.
*/
if (ReadyToQuit){
CCDebugString ("C&C95 - We are now ready to quit.\n");
ReadyToQuit = 2;
}else{
CCDebugString ("C&C95 - Emergency shutdown.\n");
CCDebugString ("C&C95 - Shut down the network stuff.\n");
#ifndef DEMO
Shutdown_Network();
#endif
CCDebugString ("C&C95 - Kill the Winsock stuff.\n");
if (Winsock.Get_Connected()) Winsock.Close();
CCDebugString ("C&C95 - Call ExitProcess.\n");
ExitProcess(0);
}
CCDebugString ("C&C95 - Clean & ready to quit.\n");
return(0);
case WM_ACTIVATEAPP:
GameInFocus=(BOOL)wParam;
if (!GameInFocus) {
Focus_Loss();
}
AllSurfaces.Set_Surface_Focus (GameInFocus);
AllSurfaces.Restore_Surfaces();
// if (GameInFocus){
// Restore_Cached_Icons();
// Map.Flag_To_Redraw(true);
// Start_Primary_Sound_Buffer(TRUE);
// if (WWMouse) WWMouse->Set_Cursor_Clip();
// }
return(0);
#if (0)
case WM_ACTIVATE:
if (low_param == WA_INACTIVE){
GameInFocus = FALSE;
Focus_Loss();
}
return(0);
#endif //(0)
case WM_SYSCOMMAND:
switch ( wParam ) {
case SC_CLOSE:
/*
** Windows sent us a close message. Probably in response to Alt-F4. Ignore it by
** pretending to handle the message and returning 0;
*/
return (0);
case SC_SCREENSAVE:
/*
** Windoze is about to start the screen saver. If we just return without passing
** this message to DefWindowProc then the screen saver will not be allowed to start.
*/
return (0);
}
break;
#ifdef FORCE_WINSOCK
case WM_ACCEPT:
case WM_HOSTBYADDRESS:
case WM_HOSTBYNAME:
case WM_ASYNCEVENT:
case WM_UDPASYNCEVENT:
Winsock.Message_Handler(hwnd, message, wParam, lParam);
return (0);
#endif //FORCE_WINSOCK
}
return (DefWindowProc (hwnd, message, wParam, lParam));
}
#endif
/***********************************************************************************************
* Create_Main_Window -- opens the MainWindow for C&C *
* *
* *
* *
* INPUT: instance -- handle to program instance *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 10/10/95 4:08PM ST : Created *
*=============================================================================================*/
#define CC_ICON 1
int ShowCommand;
void Create_Main_Window ( HANDLE instance ,int command_show , int width , int height )
{
MainWindow = NULL;
return;
#if (0)
HWND hwnd ;
WNDCLASS wndclass ;
//
// Register the window class
//
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = Windows_Procedure ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = instance ;
wndclass.hIcon = LoadIcon (instance, MAKEINTRESOURCE(CC_ICON)) ;
wndclass.hCursor = NULL;
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName = "Command & Conquer"; //NULL
wndclass.lpszClassName = "Command & Conquer";
RegisterClass (&wndclass) ;
//
// Create our main window
//
hwnd = CreateWindowEx (
WS_EX_TOPMOST,
"Command & Conquer",
"Command & Conquer",
WS_POPUP | WS_MAXIMIZE,
0,
0,
width,
height,
NULL,
NULL,
instance,
NULL );
ShowWindow (hwnd, command_show );
ShowCommand = command_show;
UpdateWindow (hwnd);
SetFocus (hwnd);
MainWindow=hwnd; //Save the handle to our main window
hInstance = instance;
CCFocusMessage = RegisterWindowMessage ("CC_GOT_FOCUS");
Audio_Focus_Loss_Function = &Focus_Loss;
Misc_Focus_Loss_Function = &Focus_Loss;
Misc_Focus_Restore_Function = &Focus_Restore;
Gbuffer_Focus_Loss_Function = &Focus_Loss;
#endif
}
void Window_Dialog_Box(HANDLE hinst, LPCTSTR lpszTemplate, HWND hwndOwner, DLGPROC dlgprc)
{
#if (0)
MSG msg;
/*
** Get rid of the Westwood mouse cursor because we are showing a
** dialog box and we want to have the right windows cursor showing
** for it.
*/
Hide_Mouse();
ShowCursor(TRUE);
/*
** Pop up the dialog box and then run a standard message handler
** until the dialog box is closed.
*/
DialogBox(hinst, lpszTemplate, hwndOwner, dlgprc);
while (GetMessage(&msg, NULL, 0, 0) && !AllDone) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/*
** Restore the westwood mouse cursor and get rid of the windows one
** because it is now time to restore back to the westwood way of
** doing things.
*/
ShowCursor(FALSE);
Show_Mouse();
#endif
}
typedef struct tColourList {
char Red;
char Green;
char Blue;
} ColourList;
ColourList ColourLookup[9]={
0,0,0,
63,0,0,
0,63,0,
0,0,63,
63,0,63,
63,63,0,
0,63,63,
32,32,32,
63,63,63
};
int DebugColour=1;
extern "C" void Set_Palette_Register(int number,int red ,int green ,int blue);
//#pragma off (unreferenced)
void Colour_Debug (int call_number)
{
//#if 0
//if (DebugColour==call_number || !call_number){
//if (call_number){
// Wait_Vert_Blank();
//}
// ST - 1/3/2019 10:43AM
//Set_Palette_Register (0,ColourLookup[call_number].Red ,
// ColourLookup[call_number].Green,
// ColourLookup[call_number].Blue);
//}
//#endif
}
//#pragma on (unreferenced)
BOOL Any_Locked (void)
{
if (SeenBuff.Get_LockCount() ||
HidPage.Get_LockCount()){
return (TRUE);
}else{
return(FALSE);
}
}
HANDLE DebugFile = INVALID_HANDLE_VALUE;
/***********************************************************************************************
* CCDebugString -- sends a string to the debugger and echos it to disk *
* *
* *
* *
* INPUT: string *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 10/28/96 12:48PM ST : Created *
*=============================================================================================*/
void CCDebugString (char *string)
{
#if (0)
char outstr[256];
sprintf (outstr, "%s", string);
DWORD actual;
if (DebugFile == INVALID_HANDLE_VALUE){
DebugFile = CreateFile("debug.txt", GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
}else{
DebugFile = CreateFile("debug.txt", GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
}
if (DebugFile != INVALID_HANDLE_VALUE){
SetFilePointer (DebugFile, 0, NULL, FILE_END);
WriteFile(DebugFile, outstr, strlen(outstr)+1, &actual, NULL);
CloseHandle (DebugFile);
}
OutputDebugString (string);
#else
string = string;
#endif
}
//
// Miscellaneous stubs. Mainly for multi player stuff
//
//
//
//IPXAddressClass::IPXAddressClass(void) {
// int i;
// i++;
//}
//int IPXManagerClass::Num_Connections(void){ return (0); }
//int IPXManagerClass::Connection_ID( int ) { return (0); }
//IPXAddressClass * IPXManagerClass::Connection_Address( int ) { return ((IPXAddressClass*)0); }
//char * IPXManagerClass::Connection_Name( int ) { return ((char*)0); }
//int IPXAddressClass::Is_Broadcast() { return (0); }
//int IPXManagerClass::Send_Global_Message( void *, int, int, IPXAddressClass * ) { return (0); }
//int IPXManagerClass::Service() { return (0); }
//int IPXManagerClass::Get_Global_Message( void *, int *, IPXAddressClass *, short unsigned * ) { return (0); }
//int IPXAddressClass::operator ==( IPXAddressClass & ) { return (0); }
//IPXManagerClass::IPXManagerClass( int, int, int, int, short unsigned, short unsigned ) {}
//IPXManagerClass::~IPXManagerClass() {
// int i;
// i++;
// }
//int IPXManagerClass::Delete_Connection( int ) { return (0); }
//IPXAddressClass::IPXAddressClass( char unsigned *, char unsigned * ){}
//void IPXManagerClass::Set_Socket( short unsigned ){}
//int IPXManagerClass::Is_IPX() { return (0); }
//int IPXManagerClass::Init() { return (0); }
//void IPXAddressClass::Get_Address( char unsigned *, char unsigned * ){}
//void IPXManagerClass::Set_Bridge( char unsigned * ){}
//int IPXManagerClass::Global_Num_Send() { return (0); }
//void IPXManagerClass::Set_Timing( long unsigned, long unsigned, long unsigned ){}
//unsigned long IPXManagerClass::Global_Response_Time() { return (0); }
//int IPXManagerClass::Create_Connection( int, char *, IPXAddressClass * ) { return (0); }
//int IPXAddressClass::operator !=( IPXAddressClass & ) { return (0); }
//int IPXManagerClass::Send_Private_Message( void *, int, int, int ) { return (0); }
//int IPXManagerClass::Get_Private_Message( void *, int *, int * ) { return (0); }
//int IPXManagerClass::Connection_Index( int ) { return (0); }
//void IPXManagerClass::Reset_Response_Time(){}
//long unsigned IPXManagerClass::Response_Time() { return (0); }
//int IPXManagerClass::Private_Num_Send( int ) { return (0); }
//_VQAHandle * VQA_Alloc(void){ return ((_VQAHandle *)0); }
//void VQA_Init( _VQAHandle *, long ( *)()) {}
//long VQA_Open( _VQAHandle *, char const *, _VQAConfig * ) { return (0); }
//void VQA_Free( _VQAHandle * ) {}
//void VQA_Close( _VQAHandle * ) {}
//long VQA_Play( _VQAHandle *, long ) { return (0); }
//void VQA_Init(VQAHandle *, long(*)(VQAHandle *vqa, long action, void *buffer, long nbytes)){}
//long VQA_Open(VQAHandle *, char const *, VQAConfig *)
//{
// return (0);
//}
//void VQA_Close(VQAHandle *){}
//long VQA_Play(VQAHandle *, long)
//{
// return (0);
//}
unsigned char *VQPalette;
long VQNumBytes;
unsigned long VQSlowpal;
bool VQPaletteChange = false;
extern "C"{
void __cdecl SetPalette(unsigned char *palette,long numbytes,unsigned long slowpal);
}
void Flag_To_Set_Palette(unsigned char *palette,long numbytes,unsigned long slowpal)
{
VQPalette = palette;
VQNumBytes = numbytes;
VQSlowpal = slowpal;
VQPaletteChange = true;
}
void Check_VQ_Palette_Set(void)
{
if (VQPaletteChange){
SetPalette(VQPalette, VQNumBytes, VQSlowpal);
VQPaletteChange = false;
}
}
void __cdecl SetPalette(unsigned char *palette,long,unsigned long)
{
for (int i=0 ; i<256*3 ; i++){
*(palette+i)&=63;
}
Increase_Palette_Luminance(palette , 15 , 15 , 15 ,63);
if (PalettesRead){
memcpy (&PaletteInterpolationTable[0][0] , InterpolatedPalettes[PaletteCounter++] , 65536);
}
Set_Palette(palette);
}
/***********************************************************************************************
* Memory_Error_Handler -- Handle a possibly fatal failure to allocate memory *
* *
* *
* *
* INPUT: Nothing *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 5/22/96 3:57PM ST : Created *
*=============================================================================================*/
void Memory_Error_Handler(void)
{
GlyphX_Debug_Print("Error - out of memory.");
VisiblePage.Clear();
Set_Palette(GamePalette);
while (Get_Mouse_State()){Show_Mouse();};
CCMessageBox().Process("Error - out of memory.", "Abort", false);
Invalidate_Cached_Icons();
// Nope. ST - 1/10/2019 10:38AM
//PostQuitMessage( 0 );
//ExitProcess(0);
}
GraphicBufferClass* Read_PCX_File(char* name, char* Palette, void *Buff, long Size);
/***********************************************************************************************
* Load_Title_Screen -- loads the title screen into the given video buffer *
* *
* *
* *
* INPUT: screen name *
* video buffer *
* ptr to buffer for palette *
* *
* OUTPUT: Nothing *
* *
* WARNINGS: None *
* *
* HISTORY: *
* 7/5/96 11:30AM ST : Created *
*=============================================================================================*/
void Load_Title_Screen(char *name, GraphicViewPortClass *video_page, unsigned char *palette)
{
GraphicBufferClass *load_buffer;
load_buffer = Read_PCX_File (name, (char*)palette, NULL, 0);
if (load_buffer){
load_buffer->Blit(*video_page);
delete load_buffer;
}
}
#include "filepcx.h"
/***************************************************************************
* READ_PCX_FILE -- read a pcx file into a Graphic Buffer *
* *
* GraphicBufferClass* Read_PCX_File (char* name, char* palette ,void *Buff, long size ); *
* *
* *
* INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] *
* palette is optional, if palette != NULL the the color palette of *
* the pcx file will be place in the memory block pointed *
* by palette. *
* Buff is optinal, if Buff == NULL a new memory Buffer *
* will be allocated, otherwise the file will be placed *
* at location pointd by Buffer; *
* Size is the size in bytes of the memory block pointed by Buff *
* is also optional;
* *
* OUTPUT: on succes a pointer to a GraphicBufferClass cointaining the *
* pcx file, NULL othewise. *
* *
* WARNINGS: *
* Appears to be a comment-free zone *
* *
* HISTORY: *
* 05/03/1995 JRJ : Created. *
* 04/30/1996 ST : Tidied up and modified to use CCFileClass *
*=========================================================================*/
#define POOL_SIZE 2048
#define READ_CHAR() *file_ptr++ ; \
if ( file_ptr >= & pool [ POOL_SIZE ] ) { \
file_handle.Read (pool , POOL_SIZE ); \
file_ptr = pool ; \
}
/*
** The old RGB was signed, so the shift right didn't work. ST - 1/4/2019 4:49PM
*/
typedef struct {
unsigned char red ;
unsigned char green ;
unsigned char blue ;
} PG_RGB ;
GraphicBufferClass* Read_PCX_File(char* name, char* palette, void *Buff, long Size)
{
int i, j;
unsigned rle;
unsigned color;
int scan_pos;
unsigned char *file_ptr;
int width;
int height;
unsigned char *buffer;
PCX_HEADER header;
PG_RGB *pal;
unsigned char pool [POOL_SIZE];
GraphicBufferClass *pic;
CCFileClass file_handle(name);
if (!file_handle.Is_Available()) return (NULL);
file_handle.Open(READ);
file_handle.Read (&header, sizeof (PCX_HEADER));
if (header.id != 10 && header.version != 5 && header.pixelsize != 8 ) return NULL ;
width = header.width - header.x + 1;
height = header.height - header.y + 1;
if (Buff) {
buffer = (unsigned char*) Buff;
i = Size / width;
height = MIN (i - 1, height);
pic = new GraphicBufferClass(width, height, buffer, Size);
if ( !(pic && pic->Get_Buffer()) ) return NULL ;
} else {
pic = new GraphicBufferClass(width, height, NULL, width*(height+4));
if ( !(pic && pic->Get_Buffer()) ) return NULL ;
}
buffer = (unsigned char *) pic->Get_Buffer();
file_ptr = pool ;
file_handle.Read (pool , POOL_SIZE);
if ( header.byte_per_line != width ){
for ( scan_pos = j = 0 ; j < height ; j ++, scan_pos += width ) {
for ( i = 0 ; i < width ; ) {
rle = READ_CHAR ();
if ( rle > 192 ) {
rle -= 192 ;
color = READ_CHAR (); ;
memset ( buffer + scan_pos + i , color , rle );
i += rle;
} else {
*(buffer+scan_pos + i++ ) = (char)rle;
}
}
}
if ( i == width ) rle = READ_CHAR ();
if ( rle > 192 ) rle = READ_CHAR ();
} else {
for ( i = 0 ; i < width * height ; ) {
rle = READ_CHAR ();
rle &= 0xff;
if ( rle > 192 ) {
rle -= 192 ;
color = READ_CHAR ();
memset ( buffer + i , color , rle );
i += rle ;
}else{
*(buffer + i++) = rle;
}
}
}
if ( palette ) {
file_handle.Seek (- (256 * (int)sizeof ( PG_RGB )) , SEEK_END );
file_handle.Read (palette , 256L * sizeof ( PG_RGB ));
pal = ( PG_RGB * ) palette;
for (i = 0 ; i < 256 ; i ++) {
pal ->red >>= 2;
pal ->green >>= 2;
pal ->blue >>= 2;
pal ++ ;
}
}
file_handle.Close();
return pic;
}