Last active
May 4, 2022 22:45
-
-
Save IsaMorphic/0d068471262dc88b2514a84e19860619 to your computer and use it in GitHub Desktop.
XBAND News2Bin (Windows/Linux port)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Patched version for modern platforms by Ian Santin. See line 466 for details. | |
// This code is NOT MINE. All rights are reserved by the original XBAND team at Catapult Entertainment. | |
/* | |
* Convert a NewsPages file to its eventual binary representation. | |
* | |
* The individual news pages are deposited in separate files, since that's | |
* how they'll be compressed. | |
*/ | |
#include <stdio.h> | |
#include <string.h> | |
#include <errno.h> | |
typedef unsigned char DBID; | |
typedef long DBType; | |
typedef short FontID; | |
typedef int Err; | |
typedef int boolean; | |
typedef int Boolean; | |
typedef int ListHead; // don't wanna use this | |
typedef int ListNode; // don't wanna use this | |
#define kNoError 0 | |
#define false 0 | |
#define true 1 | |
#define kDailyNews 'NEWS' | |
#define kOtherNews 'NEW1' | |
#define kMainFont 0 | |
#define kXBandBold10Font 1 | |
#define kAlternate2BitFont 3 | |
#define kXBandHeavy 2 | |
#define kHeadlineFont 4 | |
#define kXBandLight9Font 6 | |
#define kJustLeft 0 | |
#define kJustCenter 1 | |
#define kJustRight 2 | |
#define kNewDaysNewsFlag 0x01 | |
#define kLastNewsPageMask 0x80 | |
#define kPageFlag_Headline 1 | |
#define kPageFlag_Tag 2 | |
#define kPageFlag_Date 4 | |
#define kPageFlag_PageNum 8 | |
#define kPageFlag_Body1 0x10 | |
#define kPageFlag_Body2 0x20 | |
#define kPageFlag_DisableXBandAnimation 0x40 | |
#define kColorFlag_Headline 1 | |
#define kColorFlag_Tag 2 | |
#define kColorFlag_Date 4 | |
#define kColorFlag_PageNum 8 | |
#define kColorFlag_Body1 0x10 | |
#define kColorFlag_Body2 0x20 | |
#define kColorFlag_DisableXBandHeadline 0x40 | |
#define kColorFlag_IsBitMapRefAnIcon 0x80 | |
// | |
// if the pageFlag has the Headline bit set, then the first string | |
// of the text is the headline. | |
// if the pageFlag has the Tag bit set, then the next string is | |
// for the tag field. | |
// then the last string is the paragraph for the page. | |
// | |
typedef struct NewsPage | |
{ | |
DBID newsFormID; | |
DBID animation1; | |
DBID backgroundSoundFX; | |
DBID bitmapID; | |
FontID headlineFontID; | |
FontID taglineFontID; | |
FontID dateFontID; | |
FontID pageNumFontID; | |
FontID body1FontID; | |
FontID body2FontID; | |
short bitmapXpos; | |
short bitmapYpos; | |
char pageNum; | |
char pageFlags; | |
char colorFlags; | |
char headlineColor0; | |
char headlineColor1; | |
char headlineColor2; | |
char headlineDrawFlags; | |
char taglineColor0; | |
char taglineColor1; | |
char taglineColor2; | |
char taglineNotUsed; | |
char pageNumColor0; | |
char pageNumColor1; | |
char pageNumColor2; | |
char pageNumNotUsed1; | |
char dateColor0; | |
char dateColor1; | |
char dateColor2; | |
char dateNotUsed; | |
char body1Color0; | |
char body1Color1; | |
char body1Color2; | |
char body1DrawFlags; | |
char body2Color0; | |
char body2Color1; | |
char body2Color2; | |
char body2DrawFlags; | |
char text[1]; | |
} NewsPage; | |
// news | |
typedef struct ServerNewsPage { | |
DBType type; | |
long length; | |
NewsPage *page; | |
} ServerNewsPage; | |
typedef struct SDBNewsPaper { | |
DBType pageType; | |
ListHead *pageList; // list of ServerNewsPage; | |
} SDBNewsPaper; | |
// | |
// DB news header. | |
// | |
typedef struct SDBNews { | |
ListHead *paperList; // list of SDBNewsPaper; | |
ListHead *animList; // list of animations (DbItem) | |
} SDBNews; | |
Boolean DataBase_TranslateConstant(char *line, long *value); | |
void DataBase_ReplaceEscapedChars(char *str); | |
Boolean DataBase_ParseNewsDataLine(char *line, FontID *fontid, char *color0, char *color1, char *color2, char *color3); | |
void PackStrings(char *packed, long numStrings, char **strings); | |
// vicious | |
#define ASSERT(x) ((x) ? 0 : abort()) | |
#define NELEM(x) (sizeof(x) / sizeof(x[0])) | |
static int strip = 0; | |
// | |
// Load news into database. A news file may contain several "newspapers", | |
// each of which is divided into "newspages". | |
// | |
// Syntax rules (similar to Mail stuff): | |
// - Blank lines and lines beginning with '#' are ignored. | |
// - Everything up to the first "%%" is ignored. | |
// - Every newspaper must be followed by "%%". Every page must begin with | |
// "&&". | |
// - There are no optional fields. Some fields may be left blank, but | |
// all of the tags must be there. | |
// - If garbage is detected, this routine scans ahead to the next %% (i.e. | |
// to the next newspaper). | |
// - If the data tag for a line is blank, then that field is ignored. However, | |
// both data and text fields should still be there (just blank). (This makes | |
// it easier to parse, and makes sure that you aren't forgetting fields.) | |
// - String constant substitution is performed on the PAGTY line. It may | |
// be added for other lines. | |
// | |
// | |
// Tags ending with "d" are data, tags ending with "t" are text. The text | |
// tags are simple strings, the data tags look like: | |
// | |
// fontID color0 color1 color2 color3 | |
// | |
// # | |
// # Sample news file. | |
// # | |
// %% | |
// PAGTY kOtherNews | |
// DBADD [ database additions, like animations ] | |
// && | |
// HEADd 4 13 0 0 0 | |
// HEADt THE HEADLINE | |
// TAGLd 0 0 9 8 0 | |
// TAGLt Tag Line Here | |
// BOD1d 0 0 2 1 0 | |
// BOD1t This is the main part of the message. | |
// BOD2d 0 0 2 1 0 | |
// BOD2t Second, smaller part | |
// DATEd 0 0 11 10 0 | |
// DATEt 1994 8 7 | |
// PGNMd 0 0 15 14 0 | |
// PGNMt Page 1 | |
// ANIM1 0 | |
// MUSIC 2 | |
// BITMP kNBAJamLogo | |
// BITXP 2 | |
// BITYP 13 | |
// CLRFL 0 | |
// && | |
// HEADd 4 13 0 0 0 | |
// HEADt NEXT HEADLINE | |
// TAGLd 0 0 9 8 0 | |
// TAGLt Early Edition | |
// ... | |
// BITXP 2 | |
// BITYP 13 | |
// CLRFL 0 | |
// %% | |
// | |
// PAGTY kNewsForm | |
// && | |
// HEADd 4 13 0 0 0 | |
// HEADt XBAND ON THE AIR | |
// ... | |
// %% | |
// | |
#define MY_MAX_NEWS_LINE_SIZE 800 // all lines | |
#define MY_MAX_SHORT_LINE_SIZE 64 // short character strings | |
#define STR_OFFSET 6 | |
Err DataBase_LoadNewsPages(char *newsfile) | |
{ | |
FILE *fp; | |
char pageNum; | |
int state, ptentry, len, i; | |
long lval, year, month, day; | |
NewsPage tmpNewsPage, *newPage; | |
SDBNewsPaper *newPaper; | |
ServerNewsPage *snp; | |
DBType pageType; | |
FontID font; | |
SDBNews *news; | |
//ListNode *node; | |
char col0, col1, col2, col3; | |
char linebuf[MY_MAX_NEWS_LINE_SIZE+7]; | |
int line = 0; | |
//ServerDbItem *dbobj; | |
// String parsing buffers. | |
char tmpHead[MY_MAX_NEWS_LINE_SIZE /*MY_MAX_SHORT_LINE_SIZE*/], | |
tmpTagl[MY_MAX_SHORT_LINE_SIZE], | |
tmpBod1[MY_MAX_NEWS_LINE_SIZE], | |
tmpBod2[MY_MAX_NEWS_LINE_SIZE], | |
tmpDate[MY_MAX_SHORT_LINE_SIZE], | |
tmpPgnm[MY_MAX_SHORT_LINE_SIZE]; | |
char *fakeStrings[6]; // 6 == #of string buffers above | |
int numFakeStrings; | |
// Parse table. This is a job for lex... | |
enum { kInit, kReset, kLookForPercent, kLookForAmperOrDbadd, kLookForPercOrAmper, | |
kPAGTY, kDBADD, kNSFID, | |
kHEADd, kHEADt, kTAGLd, kTAGLt, kBOD1d, kBOD1t, | |
kBOD2d, kBOD2t, kDATEd, kDATEt, kPGNMd, kPGNMt, | |
kANIM1, kMUSIC, kBITMP, kBITXP, kBITYP, kCLRFL | |
}; | |
static struct ParseTable { | |
int currentState; | |
char *stateName; | |
int nextState; | |
} parseTable[] = { | |
// (handle kReset & kInit explicitly) | |
{ kLookForPercent, "%%", kPAGTY }, | |
{ kPAGTY, "PAGTY", kLookForAmperOrDbadd }, | |
//{ kLookForAmper, "&&", kNSFID }, | |
// (handle kLookForAmperOrAnim explicitly) | |
{ kNSFID, "NSFID", kHEADd }, | |
{ kHEADd, "HEADd", kHEADt }, | |
{ kHEADt, "HEADt", kTAGLd }, | |
{ kTAGLd, "TAGLd", kTAGLt }, | |
{ kTAGLt, "TAGLt", kBOD1d }, | |
{ kBOD1d, "BOD1d", kBOD1t }, | |
{ kBOD1t, "BOD1t", kBOD2d }, | |
{ kBOD2d, "BOD2d", kBOD2t }, | |
{ kBOD2t, "BOD2t", kDATEd }, | |
{ kDATEd, "DATEd", kDATEt }, | |
{ kDATEt, "DATEt", kPGNMd }, | |
{ kPGNMd, "PGNMd", kPGNMt }, | |
{ kPGNMt, "PGNMt", kANIM1 }, | |
{ kANIM1, "ANIM1", kMUSIC }, | |
{ kMUSIC, "MUSIC", kBITMP }, | |
{ kBITMP, "BITMP", kBITXP }, | |
{ kBITXP, "BITXP", kBITYP }, | |
{ kBITYP, "BITYP", kCLRFL }, | |
{ kCLRFL, "CLRFL", kLookForPercOrAmper }, | |
// (handle kLookForPercOrAmper explicitly) | |
}; | |
#ifdef FUBAR | |
news = SDB_GetNews(gSDB); | |
// This is what determines whether or not the box's copy of the news | |
// is older than ours. | |
// | |
loadDate = time(0); | |
#endif | |
#ifdef FUBAR | |
if (NumListNodesInList(news->paperList) != 0) { | |
ERROR_MESG("HEY: LoadNews was called twice!\n"); // fix later?? | |
PLogmsg(LOGP_NOTICE, | |
"DataBase_LoadNewsPages: LoadNews was called twice!\n"); | |
DataBase_FreeNewsPaperList(news->paperList); | |
} | |
#endif | |
if ((fp = fopen(newsfile, "r")) == NULL) { // do NOT want "rb" | |
fprintf(stderr, "NOTE: couldn't open '%s'\n", newsfile); | |
return (kNoError); // little white lie | |
} | |
printf("--- Working on '%s'\n", newsfile); | |
state = kInit; | |
while (1) { | |
if (state == kInit || state == kReset) { | |
// (re)initialize data | |
pageType = 0; | |
pageNum = 0; | |
memset((char *)&tmpNewsPage, 0, sizeof(tmpNewsPage)); | |
tmpHead[0] = tmpTagl[0] = tmpBod1[0] = tmpBod2[0] = tmpDate[0] = | |
tmpPgnm[0] = '\0'; | |
newPaper = NULL; | |
if (state == kInit) | |
state = kLookForPercent; // find initial '%%' | |
else | |
state = kPAGTY; // starting new newspaper | |
continue; | |
} | |
fgets(linebuf, MY_MAX_NEWS_LINE_SIZE+7, fp); | |
line++; | |
if (feof(fp) || ferror(fp)) break; | |
if (linebuf[strlen(linebuf)-1] == '\n') | |
linebuf[strlen(linebuf)-1] = '\0'; | |
if (linebuf[0] == '\0' || linebuf[0] == '#') continue; | |
// Icky nasty: if line is too short, linebuf+STR_OFFSET will get | |
// garbage. | |
if (strlen(linebuf) < STR_OFFSET) | |
linebuf[STR_OFFSET] = '\0'; | |
// This doesn't work as a table entry, so deal with it directly. | |
if (state == kLookForPercOrAmper) { | |
if (strcmp(linebuf, "&&") == 0 || strcmp(linebuf, "%%") == 0) { | |
// end of page, do something appropriate | |
tmpNewsPage.pageNum = ++pageNum; | |
// BUG: if we get an error reading a news page, this will | |
// never get set. Either need to amputate the entire | |
// newspaper, or run through at the end and set this. | |
// | |
// If kLastNewsPageMask doesn't get set, the right icon | |
// gets highlighted even if the page isn't there. No big | |
// deal. | |
// | |
// (I'm leaving it be for now, assuming that moving it | |
// into the database will partially resolve the problem.) | |
if (strcmp(linebuf, "%%") == 0) | |
tmpNewsPage.pageNum |= kLastNewsPageMask; | |
// alloc a newspaper if this is a new one | |
if (newPaper == NULL) { | |
newPaper = (SDBNewsPaper *)malloc(sizeof(SDBNewsPaper)); | |
ASSERT(newPaper); | |
newPaper->pageType = pageType; | |
newPaper->pageList = NULL; | |
//node = NewListNode((Ptr)newPaper); | |
//AddListNodeToList(news->paperList, node); | |
} | |
// modify the pageFlags and colorFlags | |
// | |
// for the DisableXBandHeadline shit: | |
// - for BANDWIDTH news, both should be set | |
// - for first page of XBAND news, both should be clear | |
// - for 2nd and later pages of XBAND news, kColorFlag is | |
// clear, kPageFlag is set | |
if (pageType == kOtherNews) { | |
// XBAND news page | |
tmpNewsPage.colorFlags &= ~kColorFlag_DisableXBandHeadline; | |
if (pageNum == 1) | |
tmpNewsPage.pageFlags &= ~kPageFlag_DisableXBandAnimation; | |
else | |
tmpNewsPage.pageFlags |= kPageFlag_DisableXBandAnimation; | |
} else if (pageType == kOtherNews) { | |
// BANDWIDTH news page | |
tmpNewsPage.colorFlags |= kColorFlag_DisableXBandHeadline; | |
tmpNewsPage.pageFlags |= kPageFlag_DisableXBandAnimation; | |
} | |
// set up fakeStrings, numFakeStrings, and len | |
numFakeStrings = len = 0; | |
if (tmpNewsPage.pageFlags & kPageFlag_Headline) { | |
len += strlen(tmpHead) +1; | |
fakeStrings[numFakeStrings++] = tmpHead; | |
} | |
if (tmpNewsPage.pageFlags & kPageFlag_Tag) { | |
len += strlen(tmpTagl) +1; | |
fakeStrings[numFakeStrings++] = tmpTagl; | |
} | |
if (tmpNewsPage.pageFlags & kPageFlag_Body1) { | |
len += strlen(tmpBod1) +1; | |
fakeStrings[numFakeStrings++] = tmpBod1; | |
} | |
if (tmpNewsPage.pageFlags & kPageFlag_Body2) { | |
len += strlen(tmpBod2) +1; | |
fakeStrings[numFakeStrings++] = tmpBod2; | |
} | |
if (tmpNewsPage.pageFlags & kPageFlag_Date) { | |
len += strlen(tmpDate) +1; | |
fakeStrings[numFakeStrings++] = tmpDate; | |
} | |
if (tmpNewsPage.pageFlags & kPageFlag_PageNum) { | |
len += strlen(tmpPgnm) +1; | |
fakeStrings[numFakeStrings++] = tmpPgnm; | |
} | |
// alloc the newspage | |
len += (sizeof(NewsPage) -1); | |
newPage = (NewsPage *)malloc(len); | |
ASSERT(newPage); | |
// copy the body over | |
memcpy(newPage, &tmpNewsPage, sizeof(NewsPage)); | |
PackStrings(newPage->text, numFakeStrings, fakeStrings); | |
// Need to associate the NewsPage with a length, so we | |
// use a ServerNewsPage. | |
snp = (ServerNewsPage *)malloc(sizeof(ServerNewsPage)); | |
ASSERT(snp); | |
snp->type = pageType; | |
snp->length = len; | |
snp->page = newPage; | |
// The end of the line: dump what we got as binary data | |
// | |
{ | |
FILE *outfp; | |
char *cp; | |
char outfile[1024]; | |
if ((cp = strrchr(newsfile, '/')) == NULL) | |
cp = newsfile; | |
else { | |
cp++; // skip past '/' | |
if (*cp == '\0') | |
cp = newsfile; | |
} | |
sprintf(outfile, "%s%c%c", cp, 'A' + pageNum -1, | |
(pageType == kOtherNews) ? '-' : '+'); | |
printf("Dumping %s%d to '%s'\n", | |
strip ? "stripped " : "", pageNum, outfile); | |
if ((outfp = fopen(outfile, "wb")) == NULL) { // changed from "w" to "wb" to circumvent icky crlf on Windows | |
fprintf(stderr, "Open of outfile failed (%d)\n", errno); | |
goto error; | |
} | |
#define HDRSIZE 0x2f | |
if (strip) { | |
if (fwrite(((char *)snp->page) + HDRSIZE, snp->length - HDRSIZE, 1, outfp) != 1) | |
{ | |
fprintf(stderr, "outfp write failed (%d)\n", errno); | |
fclose(outfp); | |
goto error; | |
} | |
} else { | |
if (fwrite(snp->page, snp->length, 1, outfp) != 1) { | |
fprintf(stderr, "outfp write failed (%d)\n", errno); | |
fclose(outfp); | |
goto error; | |
} | |
} | |
fclose(outfp); | |
} | |
// add the ServerNewsPage to the list | |
//node = NewSortedListNode((Ptr) snp, | |
// ((unsigned char)snp->page->pageNum & ~kLastNewsPageMask)-1); | |
//AddListNodeToSortedList(newPaper->pageList, node); | |
// get on with it | |
if (strcmp(linebuf, "&&") == 0) { | |
state = kNSFID; | |
memset((char *)&tmpNewsPage, 0, sizeof(tmpNewsPage)); | |
tmpHead[0] = tmpTagl[0] = tmpBod1[0] = tmpBod2[0] = | |
tmpDate[0] = tmpPgnm[0] = '\0'; | |
} else | |
state = kReset; | |
} | |
continue; | |
} | |
// Look for && or one or more DBADDs | |
if (state == kLookForAmperOrDbadd) { | |
if (strcmp(linebuf, "&&") == 0) { | |
state = kNSFID; | |
continue; | |
} | |
#ifdef FUBAR | |
dbobj = DataBase_NewDbItem(linebuf+STR_OFFSET); | |
if (dbobj) { | |
if (dbobj->type == kAnimation) { | |
if (SearchList(news->animList, dbobj->id) == NULL) { | |
node = NewSortedListNode((Ptr)dbobj, (long)dbobj->id); | |
AddListNodeToSortedList(news->animList, node); | |
} else { | |
// already in the cache list ! | |
PLogmsg(LOGP_FLAW, | |
"NEWS: line %d:Ignoring duplicate Animation ID %d\n", | |
line, dbobj->id); | |
DataBase_FreeDbItem(dbobj); | |
} | |
} else | |
PLogmsg(LOGP_FLAW, | |
"NEWS: line %d: Unknown DBType = %d\n", | |
line, dbobj->type); | |
} else { | |
PLogmsg(LOGP_FLAW, | |
"NEWS: line %d: Malformed DBADD - %s\n", | |
line, linebuf); | |
} | |
// stay in same state | |
#endif | |
continue; | |
} | |
// Find state in table. This could be made more efficient, but | |
// it's only done during startup (and when new news arrives?), so | |
// just keep it simple. | |
for (ptentry = 0; ptentry < NELEM(parseTable); ptentry++) { | |
if (state == parseTable[ptentry].currentState) | |
break; | |
} | |
if (ptentry == NELEM(parseTable)) { | |
fprintf(stderr, "Bogus state in LoadNewsPages."); | |
goto error; | |
} | |
if (strncmp(linebuf, parseTable[ptentry].stateName, | |
strlen(parseTable[ptentry].stateName) != 0)) | |
{ | |
// got the wrong keyword here; user is a pinhead | |
// (could be a garbaged file and we're scanning for next '%%') | |
if (state != kLookForPercent) { | |
fprintf(stderr, | |
"NEWS: line %d: bad keyword '%s', expected '%s' (%d)\n", | |
line, linebuf, parseTable[ptentry].stateName, ptentry); | |
} | |
state = kInit; | |
continue; | |
} | |
// | |
// We got it, now do something with it. | |
// | |
switch (state) { | |
case kLookForPercent: | |
break; | |
case kPAGTY: | |
// Should probably check to see if the type is a valid pageType, | |
// but sometimes it's funny to watch the user hose himself. | |
if (!DataBase_TranslateConstant(linebuf+STR_OFFSET, &lval)) { | |
state = kInit; | |
fprintf(stderr, "NEWS: line %d: missing PAGTY rhs\n", line); | |
continue; | |
} | |
pageType = (DBType) lval; | |
break; | |
case kNSFID: | |
if (!DataBase_TranslateConstant(linebuf+STR_OFFSET, &lval)) { | |
state = kInit; | |
fprintf(stderr, "NEWS: line %d: missing NSFID rhs\n", line); | |
continue; | |
} | |
tmpNewsPage.newsFormID = lval; | |
break; | |
case kHEADd: | |
if (!DataBase_ParseNewsDataLine(linebuf+STR_OFFSET, | |
&font, &col0, &col1, &col2, &col3)) | |
{ | |
break; | |
} | |
tmpNewsPage.headlineFontID = font; | |
tmpNewsPage.headlineColor0 = col0; | |
tmpNewsPage.headlineColor1 = col1; | |
tmpNewsPage.headlineColor2 = col2; | |
tmpNewsPage.headlineDrawFlags = col3; | |
tmpNewsPage.pageFlags |= kPageFlag_Headline; | |
break; | |
case kHEADt: | |
if (tmpNewsPage.pageFlags & kPageFlag_Headline) { | |
strcpy(tmpHead, linebuf+STR_OFFSET); | |
DataBase_ReplaceEscapedChars(tmpHead); | |
} | |
break; | |
case kTAGLd: | |
if (!DataBase_ParseNewsDataLine(linebuf+STR_OFFSET, | |
&font, &col0, &col1, &col2, &col3)) | |
{ | |
break; | |
} | |
tmpNewsPage.taglineFontID = font; | |
tmpNewsPage.taglineColor0 = col0; | |
tmpNewsPage.taglineColor1 = col1; | |
tmpNewsPage.taglineColor2 = col2; | |
tmpNewsPage.taglineNotUsed = col3; | |
tmpNewsPage.pageFlags |= kPageFlag_Tag; | |
break; | |
case kTAGLt: | |
if (tmpNewsPage.pageFlags & kPageFlag_Tag) | |
strcpy(tmpTagl, linebuf+STR_OFFSET); | |
break; | |
case kBOD1d: | |
if (!DataBase_ParseNewsDataLine(linebuf+STR_OFFSET, | |
&font, &col0, &col1, &col2, &col3)) | |
{ | |
break; | |
} | |
tmpNewsPage.body1FontID = font; | |
tmpNewsPage.body1Color0 = col0; | |
tmpNewsPage.body1Color1 = col1; | |
tmpNewsPage.body1Color2 = col2; | |
tmpNewsPage.body1DrawFlags = col3; | |
tmpNewsPage.pageFlags |= kPageFlag_Body1; | |
break; | |
case kBOD1t: | |
if (tmpNewsPage.pageFlags & kPageFlag_Body1) { | |
strcpy(tmpBod1, linebuf+STR_OFFSET); | |
DataBase_ReplaceEscapedChars(tmpBod1); | |
} | |
break; | |
case kBOD2d: | |
if (!DataBase_ParseNewsDataLine(linebuf+STR_OFFSET, | |
&font, &col0, &col1, &col2, &col3)) | |
{ | |
break; | |
} | |
tmpNewsPage.body2FontID = font; | |
tmpNewsPage.body2Color0 = col0; | |
tmpNewsPage.body2Color1 = col1; | |
tmpNewsPage.body2Color2 = col2; | |
tmpNewsPage.body2DrawFlags = col3; | |
tmpNewsPage.pageFlags |= kPageFlag_Body2; | |
break; | |
case kBOD2t: | |
if (tmpNewsPage.pageFlags & kPageFlag_Body2) { | |
strcpy(tmpBod2, linebuf+STR_OFFSET); | |
DataBase_ReplaceEscapedChars(tmpBod2); | |
} | |
break; | |
case kDATEd: | |
if (!DataBase_ParseNewsDataLine(linebuf+STR_OFFSET, | |
&font, &col0, &col1, &col2, &col3)) | |
{ | |
break; | |
} | |
tmpNewsPage.dateFontID = font; | |
tmpNewsPage.dateColor0 = col0; | |
tmpNewsPage.dateColor1 = col1; | |
tmpNewsPage.dateColor2 = col2; | |
tmpNewsPage.dateNotUsed = col3; | |
tmpNewsPage.pageFlags |= kPageFlag_Date; | |
break; | |
case kDATEt: | |
if (tmpNewsPage.pageFlags & kPageFlag_Date) | |
strcpy(tmpDate, linebuf+STR_OFFSET); | |
break; | |
case kPGNMd: | |
if (!DataBase_ParseNewsDataLine(linebuf+STR_OFFSET, | |
&font, &col0, &col1, &col2, &col3)) | |
{ | |
break; | |
} | |
tmpNewsPage.pageNumFontID = font; | |
tmpNewsPage.pageNumColor0 = col0; | |
tmpNewsPage.pageNumColor1 = col1; | |
tmpNewsPage.pageNumColor2 = col2; | |
tmpNewsPage.pageNumNotUsed1 = col3; | |
tmpNewsPage.pageFlags |= kPageFlag_PageNum; | |
break; | |
case kPGNMt: | |
if (tmpNewsPage.pageFlags & kPageFlag_PageNum) | |
strcpy(tmpPgnm, linebuf+STR_OFFSET); | |
break; | |
case kANIM1: | |
if (!strlen(linebuf+STR_OFFSET)) { | |
state = kInit; | |
fprintf(stderr, "NEWS: line %d: missing ANIM1 rhs\n", line); | |
continue; | |
} | |
tmpNewsPage.animation1 = strtol(linebuf+STR_OFFSET, NULL, 0); | |
break; | |
case kMUSIC: | |
if (!strlen(linebuf+STR_OFFSET)) { | |
state = kInit; | |
fprintf(stderr, "NEWS: line %d: missing MUSIC rhs\n", line); | |
continue; | |
} | |
tmpNewsPage.backgroundSoundFX = strtol(linebuf+STR_OFFSET, NULL, 0); | |
break; | |
case kBITMP: | |
if (!DataBase_TranslateConstant(linebuf+STR_OFFSET, &lval)) { | |
state = kInit; | |
fprintf(stderr, "NEWS: line %d: missing BITMP rhs\n", line); | |
continue; | |
} | |
tmpNewsPage.bitmapID = lval; | |
break; | |
case kBITXP: | |
if (!strlen(linebuf+STR_OFFSET)) { | |
state = kInit; | |
fprintf(stderr, "NEWS: line %d: missing BITXP rhs\n", line); | |
continue; | |
} | |
tmpNewsPage.bitmapXpos = strtol(linebuf+STR_OFFSET, NULL, 0); | |
break; | |
case kBITYP: | |
if (!strlen(linebuf+STR_OFFSET)) { | |
state = kInit; | |
fprintf(stderr, "NEWS: line %d: missing kBITYP rhs\n", line); | |
continue; | |
} | |
tmpNewsPage.bitmapYpos = strtol(linebuf+STR_OFFSET, NULL, 0); | |
break; | |
case kCLRFL: | |
if (!strlen(linebuf+STR_OFFSET)) { | |
state = kInit; | |
fprintf(stderr, "NEWS: line %d: missing CLRFL rhs\n", line); | |
continue; | |
} | |
tmpNewsPage.colorFlags = strtol(linebuf+STR_OFFSET, NULL, 0); | |
break; | |
default: | |
fprintf(stderr, "Invalid state %d in LoadNews"); | |
goto error; | |
break; | |
} | |
// On to the next state. | |
state = parseTable[ptentry].nextState; | |
} | |
error: | |
fclose(fp); | |
//free(fakem); | |
return (kNoError); // little white lie when exiting via error | |
} | |
#undef MY_MAX_NEWS_LINE_SIZE | |
#undef MY_MAX_SHORT_LINE_SIZE | |
#undef STR_OFFSET | |
// | |
// Parse an XXXXd line from the NewsPages file. | |
// | |
Boolean DataBase_ParseNewsDataLine(char *line, FontID *fontid, \ | |
char *color0, char *color1, char *color2, char *color3) | |
{ | |
long f, c0, c1, c2, c3; | |
char fontBuf[64], justBuf[64]; | |
if (sscanf(line, "%s %ld %ld %ld %s", fontBuf, &c0, &c1, &c2, justBuf) != 5) | |
return (false); | |
DataBase_TranslateConstant(fontBuf, &f); | |
*fontid = f; | |
*color0 = c0; | |
*color1 = c1; | |
*color2 = c2; | |
DataBase_TranslateConstant(justBuf, &c3); | |
*color3 = c3; | |
return (true); | |
} | |
#define BASE16(x) ( \ | |
((x) >= '0' && (x) <= '9') ? (x) - '0' : \ | |
(x) >= 'a' && (x) <= 'f' ? (x) + 10 - 'a' : \ | |
(x) >= 'A' && (x) <= 'F' ? (x) + 10 - 'A' : \ | |
0) | |
// | |
// Replace escaped characters (like \n, \r, \x07) with the actual value. | |
// String is modified in place; does not become longer. | |
// | |
void DataBase_ReplaceEscapedChars(char *str) | |
{ | |
char *cp = str; | |
unsigned char val; | |
while (*str) { | |
if (*str == '\\') { | |
str++; | |
if (!*str) continue; // can't escape newline | |
switch (*str) { | |
case 't': | |
*cp = 0x09; // TAB | |
break; | |
case 'n': | |
*cp = 0x0a; // LF | |
break; | |
case 'r': | |
*cp = 0x0d; // CR | |
break; | |
case 'x': // hex replacement | |
str++; | |
if (!*str || !*(str+1)) continue; | |
val = BASE16(*str) * 16 + BASE16(*(str+1)); | |
*cp = val; | |
str++; // leave one char under it | |
break; | |
default: | |
*cp = *str; // unknown escape, just pass char through | |
break; | |
} | |
} else { | |
*cp = *str; | |
} | |
str++; | |
cp++; | |
} | |
*cp = *str; // copy the NULL | |
} | |
// Values for DataBase_TranslateConstant | |
static struct ConstantTable { | |
char *name; | |
long value; | |
} constantTable[] = { | |
// News types | |
{ "kDailyNews", kDailyNews }, | |
{ "kOtherNews", kOtherNews }, | |
// Text layout | |
{ "kJustLeft", kJustLeft }, | |
{ "kJustCenter", kJustCenter }, | |
{ "kJustRight", kJustRight }, | |
// Fonts | |
{ "kMainFont", kMainFont }, | |
{ "kXBandBold10Font", kXBandBold10Font }, | |
{ "kXBandHeavy", kXBandHeavy }, | |
{ "kAlternate2BitFont", kAlternate2BitFont }, | |
{ "kHeadlineFont", kHeadlineFont }, | |
{ "kXBandLight9Font", kXBandLight9Font }, | |
// Bitmaps & Icons | |
{ "kLemonHeadIcon", 4 }, | |
}; | |
// | |
// Translate a constant from a string into whatever value it has internally. | |
// If the first character of "line" is a digit, the number is treated as | |
// a decimal integer instead. | |
// | |
Boolean DataBase_TranslateConstant(char *line, long *value) | |
{ | |
int i; | |
if (!*line) | |
return (false); | |
if (isdigit(line[0])) { | |
*value = strtol(line, NULL, 0); | |
return (true); | |
} | |
for (i = 0; i < NELEM(constantTable); i++) | |
if (strcmp(constantTable[i].name, line) == 0) { | |
*value = constantTable[i].value; | |
return (true); | |
} | |
fprintf(stderr, "ERROR: unknown constant '%s'\n", line); | |
*value = 0; | |
return (false); | |
} | |
// | |
// A handy string packer used for news and rankings. | |
// | |
void PackStrings(char *packed, long numStrings, char **strings) | |
{ | |
char *s, *t; | |
long a; | |
ASSERT(packed); | |
ASSERT(strings); | |
s = packed; | |
for(a = 0; a < numStrings; a++) | |
{ | |
for(t = strings[a]; *t; ) | |
*s++ = *t++; | |
*s++ = *t; // copy the NULL. | |
} | |
} | |
// =========================================================================== | |
// Main stuff | |
// =========================================================================== | |
static int debug = 1; | |
static int verbose = 1; | |
void | |
Usage(char *argv0) | |
{ | |
fprintf(stderr, "Usage: %s [-s] files\n\n", argv0); | |
fprintf(stderr, "Use '-s' to strip off the news header\n"); | |
} | |
int | |
main(int argc, char *argv[]) | |
{ | |
extern char *optarg; | |
extern int optind; | |
int c, errflg, result; | |
long templ; | |
errflg = 0; | |
while ((c = getopt(argc, argv, "Dvs")) != EOF) { | |
switch (c) { | |
case 'D': | |
debug++; | |
break; | |
case 'v': | |
verbose = 1 - verbose; | |
break; | |
case 's': | |
strip = true; | |
break; | |
default: | |
errflg++; | |
break; | |
} | |
} | |
if (errflg) { | |
Usage(argv[0]); | |
exit(2); | |
} | |
// For each file specified, convert. | |
// | |
if (optind >= argc) { | |
fprintf(stderr, "Error: no files to convert\n"); | |
} else { | |
result = 0; | |
for (; optind < argc; optind++) | |
DataBase_LoadNewsPages(argv[optind]); | |
} | |
printf("Done.\n"); | |
exit(0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment