656 lines
11 KiB
C++
656 lines
11 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/>.
|
|
*/
|
|
|
|
//
|
|
// loadsave.cpp
|
|
//
|
|
|
|
#include "stdAfx.h"
|
|
#include "iff.h"
|
|
#include "TransDB.h"
|
|
#include "Babylondlg.h"
|
|
|
|
#define FORM_BABYLONDB MakeID ('N','X','D','B')
|
|
#define FORM_LABEL MakeID ('N','L','B','L')
|
|
#define FORM_TEXT MakeID ('N','T','X','T')
|
|
#define FORM_TRANS MakeID ('N','T','R','N')
|
|
#define CHUNK_COMMENT MakeID ('C','M','N','T')
|
|
#define CHUNK_CONTEXT MakeID ('C','T','X','T')
|
|
#define CHUNK_SPEAKER MakeID ('S','P','K','R')
|
|
#define CHUNK_LISTENER MakeID ('L','T','N','R')
|
|
#define CHUNK_TEXT MakeID ('T','E','X','T')
|
|
#define CHUNK_WAVE MakeID ('W','A','V','E')
|
|
#define CHUNK_WAVE_INFO MakeID ('W','V','I','N')
|
|
#define CHUNK_INFO MakeID ('I','N','F','O')
|
|
#define CHUNK_NAME MakeID ('N','A','M','E')
|
|
|
|
#define MAX_BUFFER (100*1024)
|
|
|
|
static OLECHAR buffer[MAX_BUFFER];
|
|
typedef struct
|
|
{
|
|
int num_labels;
|
|
int next_id;
|
|
|
|
} DBINFO;
|
|
|
|
typedef struct
|
|
{
|
|
int max_len;
|
|
|
|
} LBINFO;
|
|
|
|
typedef struct
|
|
{
|
|
int id;
|
|
int revision;
|
|
|
|
} TXINFO;
|
|
|
|
typedef struct
|
|
{
|
|
LangID lang;
|
|
int revision;
|
|
|
|
} TRINFO;
|
|
|
|
typedef struct
|
|
{
|
|
int valid;
|
|
DWORD lo;
|
|
DWORD hi;
|
|
|
|
} WVINFO;
|
|
|
|
|
|
static int writeString ( IFF_FILE *iff, OLECHAR *string, int chunk_id )
|
|
{
|
|
int len = (wcslen ( string ) );
|
|
int bytes = (len+1)*sizeof(OLECHAR);
|
|
|
|
if ( len )
|
|
{
|
|
IFF_NEWCHUNK ( iff, chunk_id, error );
|
|
IFF_WRITE ( iff, string, bytes, error);
|
|
IFF_CloseChunk ( iff );
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static int readString ( IFF_FILE *iff, OLECHAR *string )
|
|
{
|
|
|
|
*string = 0;
|
|
|
|
IFF_READ ( iff, string, iff->ChunkSize, error);
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static int writeTransForm ( IFF_FILE *iff, Translation *trans )
|
|
{
|
|
TRINFO trinfo;
|
|
WVINFO wvinfo;
|
|
|
|
if ( !IFF_NewForm ( iff, FORM_TRANS ))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
trinfo.lang = trans->GetLangID ();
|
|
trinfo.revision = trans->Revision ();
|
|
|
|
if ( !IFF_NewChunk ( iff, CHUNK_INFO ))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
IFF_Write ( iff, &trinfo, sizeof ( trinfo ));
|
|
|
|
IFF_CloseChunk ( iff );
|
|
|
|
writeString ( iff, trans->Get (), CHUNK_TEXT );
|
|
writeString ( iff, trans->Comment (), CHUNK_COMMENT );
|
|
|
|
if ( (wvinfo.valid = trans->WaveInfo.Valid()) )
|
|
{
|
|
wvinfo.lo = trans->WaveInfo.Lo ();
|
|
wvinfo.hi = trans->WaveInfo.Hi ();
|
|
|
|
if ( !IFF_NewChunk ( iff, CHUNK_WAVE_INFO ))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
IFF_Write ( iff, &wvinfo, sizeof ( wvinfo ));
|
|
|
|
IFF_CloseChunk ( iff );
|
|
}
|
|
|
|
IFF_CloseForm ( iff );
|
|
|
|
return TRUE;
|
|
error:
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
static int writeTextForm ( IFF_FILE *iff, BabylonText *text )
|
|
{
|
|
TXINFO txinfo;
|
|
WVINFO wvinfo;
|
|
|
|
if ( !IFF_NewForm ( iff, FORM_TEXT ))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
txinfo.id = text->ID ();
|
|
txinfo.revision = text->Revision ();
|
|
|
|
if ( !IFF_NewChunk ( iff, CHUNK_INFO ))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
IFF_Write ( iff, &txinfo, sizeof ( txinfo ));
|
|
|
|
IFF_CloseChunk ( iff );
|
|
writeString ( iff, text->Get (), CHUNK_TEXT );
|
|
writeString ( iff, text->Wave (), CHUNK_WAVE );
|
|
|
|
if ( (wvinfo.valid = text->WaveInfo.Valid()) )
|
|
{
|
|
wvinfo.lo = text->WaveInfo.Lo ();
|
|
wvinfo.hi = text->WaveInfo.Hi ();
|
|
|
|
if ( !IFF_NewChunk ( iff, CHUNK_WAVE_INFO ))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
IFF_Write ( iff, &wvinfo, sizeof ( wvinfo ));
|
|
|
|
IFF_CloseChunk ( iff );
|
|
}
|
|
|
|
IFF_CloseForm ( iff );
|
|
|
|
return TRUE;
|
|
error:
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
int WriteMainDB(TransDB *db, const char *filename, CBabylonDlg *dlg )
|
|
{
|
|
BabylonText *text;
|
|
BabylonLabel *label;
|
|
ListSearch sh_label;
|
|
ListSearch sh_text;
|
|
int count = 0;
|
|
IFF_FILE *iff = NULL;
|
|
DBINFO dbinfo;
|
|
int ok = FALSE;
|
|
|
|
if ( dlg )
|
|
{
|
|
dlg->InitProgress ( db->NumLabels ());
|
|
}
|
|
|
|
|
|
if ( !( iff = IFF_New ( filename )))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
if ( !IFF_NewForm ( iff, FORM_BABYLONDB ) )
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
dbinfo.next_id = db->ID ();
|
|
dbinfo.num_labels = db->NumLabels ();
|
|
|
|
if ( !IFF_NewChunk ( iff, CHUNK_INFO ))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
IFF_Write ( iff, &dbinfo, sizeof ( dbinfo ));
|
|
|
|
IFF_CloseChunk ( iff );
|
|
IFF_CloseForm ( iff );
|
|
|
|
text = db->FirstObsolete ( sh_text );
|
|
|
|
while ( text )
|
|
{
|
|
if ( !writeTextForm ( iff, text ) )
|
|
{
|
|
goto error;
|
|
}
|
|
text = db->NextObsolete ( sh_text );
|
|
}
|
|
|
|
|
|
|
|
label = db->FirstLabel ( sh_label );
|
|
|
|
while ( label )
|
|
{
|
|
LBINFO lbinfo;
|
|
|
|
if ( !IFF_NewForm ( iff, FORM_LABEL ))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
lbinfo.max_len = label->MaxLen ();
|
|
|
|
if ( !IFF_NewChunk ( iff, CHUNK_INFO ))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
IFF_Write ( iff, &lbinfo, sizeof ( lbinfo ));
|
|
|
|
IFF_CloseChunk ( iff );
|
|
|
|
writeString ( iff, label->Name (), CHUNK_NAME );
|
|
writeString ( iff, label->Comment (), CHUNK_COMMENT );
|
|
writeString ( iff, label->Context (), CHUNK_CONTEXT );
|
|
writeString ( iff, label->Speaker (), CHUNK_SPEAKER );
|
|
writeString ( iff, label->Listener (), CHUNK_LISTENER );
|
|
IFF_CloseForm ( iff );
|
|
|
|
text = label->FirstText ( sh_text );
|
|
|
|
while ( text )
|
|
{
|
|
Translation *trans;
|
|
ListSearch sh_trans;
|
|
if ( !writeTextForm ( iff, text ) )
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
trans = text->FirstTranslation ( sh_trans );
|
|
|
|
while ( trans )
|
|
{
|
|
if ( !writeTransForm ( iff, trans ) )
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
trans = text->NextTranslation ( sh_trans );
|
|
}
|
|
|
|
text = label->NextText ( sh_text );
|
|
}
|
|
|
|
count++;
|
|
if ( dlg )
|
|
{
|
|
dlg->SetProgress ( count );
|
|
}
|
|
label = db->NextLabel ( sh_label );
|
|
}
|
|
|
|
ok = TRUE;
|
|
db->ClearChanges ();
|
|
|
|
error:
|
|
if ( iff )
|
|
{
|
|
IFF_Close ( iff );
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
int LoadMainDB(TransDB *db, const char *filename, void (*cb) (void) )
|
|
{
|
|
BabylonLabel *label = NULL;
|
|
BabylonText *text = NULL;
|
|
Translation *trans = NULL;
|
|
int count = 0;
|
|
IFF_FILE *iff = NULL;
|
|
DBINFO dbinfo;
|
|
int ok = FALSE;
|
|
|
|
|
|
if ( !(iff = IFF_Load ( filename ) ) )
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
if ( !IFF_NextForm ( iff ) || iff->FormID != FORM_BABYLONDB )
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
dbinfo.next_id = -1;
|
|
dbinfo.num_labels = 0;
|
|
|
|
while ( IFF_NextChunk ( iff ))
|
|
{
|
|
switch (iff->ChunkID )
|
|
{
|
|
case CHUNK_INFO:
|
|
IFF_READ ( iff, &dbinfo, sizeof ( dbinfo ), error );
|
|
break;
|
|
}
|
|
}
|
|
|
|
db->SetID ( dbinfo.next_id );
|
|
|
|
while ( IFF_NextForm ( iff ) )
|
|
{
|
|
switch ( iff->FormID )
|
|
{
|
|
case FORM_LABEL:
|
|
{
|
|
LBINFO lbinfo;
|
|
// new label
|
|
|
|
if ( text )
|
|
{
|
|
if ( label )
|
|
{
|
|
label->AddText ( text );
|
|
}
|
|
else
|
|
{
|
|
db->AddObsolete ( text );
|
|
}
|
|
|
|
text = NULL;
|
|
}
|
|
|
|
if ( label )
|
|
{
|
|
count++;
|
|
db->AddLabel ( label );
|
|
label = NULL;
|
|
if ( cb )
|
|
{
|
|
cb ();
|
|
}
|
|
}
|
|
|
|
if ( ! (label = new BabylonLabel ()))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
while ( IFF_NextChunk ( iff ))
|
|
{
|
|
switch ( iff->ChunkID )
|
|
{
|
|
case CHUNK_INFO:
|
|
IFF_READ ( iff, &lbinfo, sizeof (lbinfo), error );
|
|
label->SetMaxLen ( lbinfo.max_len );
|
|
break;
|
|
case CHUNK_COMMENT:
|
|
readString ( iff, buffer );
|
|
label->SetComment ( buffer );
|
|
break;
|
|
case CHUNK_CONTEXT:
|
|
readString ( iff, buffer );
|
|
label->SetContext ( buffer );
|
|
break;
|
|
case CHUNK_SPEAKER:
|
|
readString ( iff, buffer );
|
|
label->SetSpeaker ( buffer );
|
|
break;
|
|
case CHUNK_LISTENER:
|
|
readString ( iff, buffer );
|
|
label->SetListener ( buffer );
|
|
break;
|
|
case CHUNK_NAME:
|
|
readString ( iff, buffer );
|
|
label->SetName ( buffer );
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case FORM_TEXT:
|
|
{
|
|
TXINFO txinfo;
|
|
|
|
if ( text )
|
|
{
|
|
if ( label )
|
|
{
|
|
label->AddText ( text );
|
|
}
|
|
else
|
|
{
|
|
db->AddObsolete ( text );
|
|
}
|
|
|
|
text = NULL;
|
|
}
|
|
|
|
if ( ! (text = new BabylonText ()))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
while ( IFF_NextChunk ( iff ))
|
|
{
|
|
switch ( iff->ChunkID )
|
|
{
|
|
case CHUNK_INFO:
|
|
IFF_READ ( iff, &txinfo, sizeof (txinfo), error );
|
|
text->SetID ( txinfo.id );
|
|
text->SetRevision ( txinfo.revision );
|
|
break;
|
|
case CHUNK_TEXT:
|
|
readString ( iff, buffer );
|
|
text->Set ( buffer );
|
|
break;
|
|
|
|
case CHUNK_WAVE:
|
|
readString ( iff, buffer );
|
|
text->SetWave ( buffer );
|
|
break;
|
|
case CHUNK_WAVE_INFO:
|
|
{
|
|
WVINFO wvinfo;
|
|
IFF_READ ( iff, &wvinfo, sizeof (wvinfo), error );
|
|
text->WaveInfo.SetValid ( wvinfo.valid );
|
|
text->WaveInfo.SetLo ( wvinfo.lo );
|
|
text->WaveInfo.SetHi ( wvinfo.hi );
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
case FORM_TRANS:
|
|
{
|
|
TRINFO trinfo;
|
|
|
|
if ( ! (trans = new Translation ()))
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
while ( IFF_NextChunk ( iff ))
|
|
{
|
|
switch ( iff->ChunkID )
|
|
{
|
|
case CHUNK_INFO:
|
|
IFF_READ ( iff, &trinfo, sizeof (trinfo), error );
|
|
trans->SetLangID ( trinfo.lang );
|
|
trans->SetRevision ( trinfo.revision );
|
|
break;
|
|
case CHUNK_TEXT:
|
|
readString ( iff, buffer );
|
|
trans->Set ( buffer );
|
|
break;
|
|
|
|
case CHUNK_COMMENT:
|
|
readString ( iff, buffer );
|
|
trans->SetComment ( buffer );
|
|
break;
|
|
case CHUNK_WAVE_INFO:
|
|
{
|
|
WVINFO wvinfo;
|
|
IFF_READ ( iff, &wvinfo, sizeof (wvinfo), error );
|
|
trans->WaveInfo.SetValid ( wvinfo.valid );
|
|
trans->WaveInfo.SetLo ( wvinfo.lo );
|
|
trans->WaveInfo.SetHi ( wvinfo.hi );
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
if ( text )
|
|
{
|
|
text->AddTranslation ( trans );
|
|
}
|
|
else
|
|
{
|
|
delete trans;
|
|
}
|
|
trans = NULL;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( text )
|
|
{
|
|
if ( label )
|
|
{
|
|
label->AddText ( text );
|
|
}
|
|
else
|
|
{
|
|
db->AddObsolete ( text );
|
|
}
|
|
|
|
text = NULL;
|
|
}
|
|
|
|
if ( label )
|
|
{
|
|
count++;
|
|
db->AddLabel ( label );
|
|
label = NULL;
|
|
if ( cb )
|
|
{
|
|
cb ();
|
|
}
|
|
}
|
|
|
|
ok = TRUE;
|
|
|
|
error:
|
|
|
|
if ( label )
|
|
{
|
|
delete label;
|
|
}
|
|
|
|
if ( text )
|
|
{
|
|
delete text;
|
|
}
|
|
|
|
if ( trans )
|
|
{
|
|
delete trans;
|
|
}
|
|
|
|
if ( iff )
|
|
{
|
|
IFF_Close ( iff );
|
|
}
|
|
|
|
db->ClearChanges ();
|
|
|
|
if ( !ok )
|
|
{
|
|
db->Clear ();
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
|
|
int GetLabelCountDB ( char *filename )
|
|
{
|
|
IFF_FILE *iff = NULL;
|
|
DBINFO dbinfo;
|
|
int count = 0;
|
|
|
|
|
|
if ( !(iff = IFF_Open ( filename ) ) )
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
if ( !IFF_NextForm ( iff ) || iff->FormID != FORM_BABYLONDB )
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
dbinfo.next_id = -1;
|
|
dbinfo.num_labels = 0;
|
|
|
|
while ( IFF_NextChunk ( iff ))
|
|
{
|
|
switch (iff->ChunkID )
|
|
{
|
|
case CHUNK_INFO:
|
|
IFF_READ ( iff, &dbinfo, sizeof ( dbinfo ), error );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
count = dbinfo.num_labels;
|
|
|
|
error:
|
|
if ( iff )
|
|
{
|
|
IFF_Close ( iff );
|
|
}
|
|
return count;
|
|
} |