Skip to content

Instantly share code, notes, and snippets.

@IsaMorphic
Last active May 4, 2022 22:45
Show Gist options
  • Save IsaMorphic/0d068471262dc88b2514a84e19860619 to your computer and use it in GitHub Desktop.
Save IsaMorphic/0d068471262dc88b2514a84e19860619 to your computer and use it in GitHub Desktop.
XBAND News2Bin (Windows/Linux port)
// 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