diff options
| author | fusion32 <marcopuzziello@gmail.com> | 2025-05-22 01:02:13 -0300 |
|---|---|---|
| committer | fusion32 <marcopuzziello@gmail.com> | 2025-05-22 09:59:14 -0300 |
| commit | de848a4131beb288335b6aaa29c2a6a439c50e0e (patch) | |
| tree | 64aceaf19d47fc3ff3b26663cb36fd1de0fcdccf | |
| parent | 91d7adad320c34ff0cb25aa21b16d59b67387c43 (diff) | |
| download | game-de848a4131beb288335b6aaa29c2a6a439c50e0e.tar.gz game-de848a4131beb288335b6aaa29c2a6a439c50e0e.zip | |
implement script lexer
| -rw-r--r-- | TODO.md | 7 | ||||
| -rw-r--r-- | build.bat | 3 | ||||
| -rw-r--r-- | reference/game.c | 1050 | ||||
| -rw-r--r-- | reference/types.hh | 36 | ||||
| -rw-r--r-- | src/config.cc | 2 | ||||
| -rw-r--r-- | src/config.hh | 61 | ||||
| -rw-r--r-- | src/main.cc | 1 | ||||
| -rw-r--r-- | src/main.hh | 44 | ||||
| -rw-r--r-- | src/script.cc | 617 | ||||
| -rw-r--r-- | src/script.hh | 94 | ||||
| -rw-r--r-- | src/util.cc | 2 |
11 files changed, 787 insertions, 1130 deletions
@@ -7,3 +7,10 @@ ## Estimate The decompiled file has ~115K lines of C. If we take ~15K lines to be rubbish, this can be round to ~100K. Considering a low estimate of 200 lines per day, the whole process could take up to 500 days which is quite a bit but not impossible. Now considering a high estimate of 1K lines per day, it could take 100 days which is also quite a bit. + +## TODO AFTER FIRST PASS +- Trim some rough edges. +- Avoid unsafe libc functions like `strcpy`, `strncpy`, `strcat`, `sprintf` etc... +- Handle connections inline with `poll` (probably?). +- Review signal usage for timing (SIGALRM, etc...). +- Support Windows. @@ -10,7 +10,8 @@ set OUTPUTEXE=out.exe set OUTPUTPDB=out.pdb set INSTALLDIR=..\bin -set SRC="../src/creature.cc" "../src/main.cc" "../src/player.cc" "../src/skill.cc" +@REM set SRC="../src/creature.cc" "../src/main.cc" "../src/player.cc" "../src/skill.cc" +set SRC="../src/script.cc" "../src/util.cc" cl -W3 -WX -Zi -we4244 -we4456 -we4457 -wd4996 -std:c++14 -utf-8 -MP8 -DBUILD_DEBUG=1 -DARCH_X64=1 -D_CRT_SECURE_NO_WARNINGS=1 -Fe:"%OUTPUTEXE%" -Fd:"%OUTPUTPDB%" %* %SRC% /link -subsystem:console -incremental:no -opt:ref -dynamicbase "ws2_32.lib" if errorlevel 1 exit /b errorlevel diff --git a/reference/game.c b/reference/game.c index dc91196..1affc5c 100644 --- a/reference/game.c +++ b/reference/game.c @@ -1955,198 +1955,6 @@ void _GLOBAL__I_BeADaemon(void) return;
}
-bool isSpace(int c)
-
-{
- bool bVar1;
-
- bVar1 = false;
- if ((((c == 0x20) || (c == 0xc)) || (c == 10)) || (((c == 0xd || (c == 9)) || (c == 0xb)))) {
- bVar1 = true;
- }
- return bVar1;
-}
-
-
-
-bool isAlpha(int c)
-
-{
- bool bVar1;
-
- bVar1 = false;
- if ((((((c - 0x61U < 0x1a) || (c - 0x41U < 0x1a)) || (c == -0x1c)) || ((c == -10 || (c == -4))))
- || ((c == -0x3c || ((c == -0x2a || (c == -0x24)))))) || (c == -0x21)) {
- bVar1 = true;
- }
- return bVar1;
-}
-
-
-
-bool isEngAlpha(int c)
-
-{
- bool bVar1;
-
- bVar1 = false;
- if ((c - 0x61U < 0x1a) || (c - 0x41U < 0x1a)) {
- bVar1 = true;
- }
- return bVar1;
-}
-
-
-
-bool isDigit(int c)
-
-{
- return c - 0x30U < 10;
-}
-
-
-
-int toLower(int c)
-
-{
- if ((c - 0x41U < 0x1a) || ((byte)((char)(c - 0x41U) + 0x81U) < 0x1f)) {
- c = c + 0x20;
- }
- return c;
-}
-
-
-
-int toUpper(int c)
-
-{
- if ((c - 0x61U < 0x1a) || ((byte)((char)(c - 0x61U) + 0x81U) < 0x1f)) {
- c = c + -0x20;
- }
- return c;
-}
-
-
-
-char * strLower(char *s)
-
-{
- char cVar1;
- int i;
- int iVar2;
-
- iVar2 = 0;
- cVar1 = *s;
- while (cVar1 != '\0') {
- if (((int)cVar1 - 0x41U < 0x1a) || ((byte)((char)((int)cVar1 - 0x41U) + 0x81U) < 0x1f)) {
- cVar1 = cVar1 + ' ';
- }
- s[iVar2] = cVar1;
- iVar2 = iVar2 + 1;
- cVar1 = s[iVar2];
- }
- return s;
-}
-
-
-
-char * strUpper(char *s)
-
-{
- char cVar1;
- int i;
- int iVar2;
-
- iVar2 = 0;
- cVar1 = *s;
- while (cVar1 != '\0') {
- if (((int)cVar1 - 0x61U < 0x1a) || ((byte)((char)((int)cVar1 - 0x61U) + 0x81U) < 0x1f)) {
- cVar1 = cVar1 + -0x20;
- }
- s[iVar2] = cVar1;
- iVar2 = iVar2 + 1;
- cVar1 = s[iVar2];
- }
- return s;
-}
-
-
-
-int stricmp(char *s1,char *s2,int Pos)
-
-{
- char cVar1;
- int iVar2;
- int iVar3;
- int i;
- int iVar4;
-
- iVar4 = 0;
- while( true ) {
- if (iVar4 == Pos) {
- return 0;
- }
- cVar1 = s1[iVar4];
- iVar2 = (int)cVar1;
- if ((iVar2 - 0x41U < 0x1a) || ((byte)((char)(iVar2 - 0x41U) + 0x81U) < 0x1f)) {
- iVar2 = iVar2 + 0x20;
- }
- iVar3 = (int)s2[iVar4];
- if ((iVar3 - 0x41U < 0x1a) || ((byte)((char)(iVar3 - 0x41U) + 0x81U) < 0x1f)) {
- iVar3 = iVar3 + 0x20;
- }
- if (iVar3 < iVar2) break;
- iVar2 = (int)cVar1;
- if ((iVar2 - 0x41U < 0x1a) || ((byte)((char)(iVar2 - 0x41U) + 0x81U) < 0x1f)) {
- iVar2 = iVar2 + 0x20;
- }
- iVar3 = (int)s2[iVar4];
- if ((iVar3 - 0x41U < 0x1a) || ((byte)((char)(iVar3 - 0x41U) + 0x81U) < 0x1f)) {
- iVar3 = iVar3 + 0x20;
- }
- if (iVar2 < iVar3) {
- return -1;
- }
- if (cVar1 == '\0') {
- return 0;
- }
- iVar4 = iVar4 + 1;
- }
- return 1;
-}
-
-
-
-char * findFirst(char *s,char c)
-
-{
- char *pcVar1;
-
- pcVar1 = strchr(s,(int)c);
- return pcVar1;
-}
-
-
-
-char * findLast(char *s,char c)
-
-{
- char *pcVar1;
- char *p;
- char *pcVar2;
-
- pcVar2 = (char *)0x0;
- while( true ) {
- pcVar1 = strchr(s,(int)c);
- if (pcVar1 == (char *)0x0) break;
- s = pcVar1 + 1;
- pcVar2 = pcVar1;
- }
- return pcVar2;
-}
-
-
-
void SetPrintFunction(TPrintFunction *Function)
{
@@ -3786,864 +3594,6 @@ LAB_0804eb1e: return;
}
-
-
-// DWARF original prototype: void TReadScriptFile(TReadScriptFile * this)
-
-void __thiscall TReadScriptFile::TReadScriptFile(TReadScriptFile *this)
-
-{
- this->RecursionDepth = -1;
- this->Bytes = (uchar *)this->String;
- return;
-}
-
-
-
-// DWARF original prototype: void TReadScriptFile(TReadScriptFile * this)
-
-void __thiscall TReadScriptFile::TReadScriptFile(TReadScriptFile *this)
-
-{
- this->RecursionDepth = -1;
- this->Bytes = (uchar *)this->String;
- return;
-}
-
-
-
-// DWARF original prototype: void ~TReadScriptFile(TReadScriptFile * this, int __in_chrg)
-
-void __thiscall TReadScriptFile::~TReadScriptFile(TReadScriptFile *this,int __in_chrg)
-
-{
- int iVar1;
- int *piVar2;
-
- if (this->RecursionDepth != -1) {
- ::error("TReadScriptFile::~TReadScriptFile: Datei ist noch offen.\n");
- iVar1 = this->RecursionDepth;
- while (iVar1 != -1) {
- iVar1 = fclose((FILE *)this->File[iVar1]);
- if (iVar1 != 0) {
- piVar2 = __errno_location();
- ::error(&DAT_080ef4e0,*piVar2);
- }
- this->RecursionDepth = this->RecursionDepth + -1;
- iVar1 = this->RecursionDepth;
- }
- }
- return;
-}
-
-
-
-// DWARF original prototype: void ~TReadScriptFile(TReadScriptFile * this, int __in_chrg)
-
-void __thiscall TReadScriptFile::~TReadScriptFile(TReadScriptFile *this,int __in_chrg)
-
-{
- int iVar1;
- int *piVar2;
-
- if (this->RecursionDepth != -1) {
- ::error("TReadScriptFile::~TReadScriptFile: Datei ist noch offen.\n");
- iVar1 = this->RecursionDepth;
- while (iVar1 != -1) {
- iVar1 = fclose((FILE *)this->File[iVar1]);
- if (iVar1 != 0) {
- piVar2 = __errno_location();
- ::error(&DAT_080ef4e0,*piVar2);
- }
- this->RecursionDepth = this->RecursionDepth + -1;
- iVar1 = this->RecursionDepth;
- }
- }
- return;
-}
-
-
-
-// DWARF original prototype: void open(TReadScriptFile * this, char * FileName)
-
-int __thiscall TReadScriptFile::open(TReadScriptFile *this,char *__file,int __oflag,...)
-
-{
- int iVar1;
- char *p;
- int iVar2;
- char *pcVar3;
- char (*__dest) [4096];
- FILE *pFVar4;
- int *piVar5;
- undefined4 *puVar6;
- int ErrorCode;
-
- iVar1 = this->RecursionDepth;
- iVar2 = iVar1 + 1;
- this->RecursionDepth = iVar2;
- if (iVar2 == 3) {
- ::error(&DAT_080ef560);
- puVar6 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar6 = "Recursion depth too high";
- goto LAB_0804ee56;
- }
- if ((iVar2 < 1) || (*__file == '/')) {
-LAB_0804ee70:
- __dest = this->Filename + this->RecursionDepth;
- }
- else {
- strcpy(this->Filename[this->RecursionDepth],this->Filename[iVar1]);
- // try { // try from 0804eda1 to 0804ee91 has its CatchHandler @ 0804eea6
- pcVar3 = findLast(this->Filename[this->RecursionDepth],'/');
- if (pcVar3 == (char *)0x0) goto LAB_0804ee70;
- __dest = (char (*) [4096])(pcVar3 + 1);
- }
- strcpy(*__dest,__file);
- iVar1 = this->RecursionDepth;
- pFVar4 = fopen(this->Filename[this->RecursionDepth],"rb");
- iVar2 = this->RecursionDepth;
- this->File[iVar1] = (FILE *)pFVar4;
- if (this->File[iVar2] != (FILE *)0x0) {
- this->Line[iVar2] = 1;
- return 1;
- }
- piVar5 = __errno_location();
- iVar1 = *piVar5;
- ::error(&DAT_080ef520,this->Filename + iVar2);
- pcVar3 = strerror(iVar1);
- ::error("Fehler %d: %s.\n",iVar1,pcVar3);
- this->RecursionDepth = this->RecursionDepth + -1;
- puVar6 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar6 = "Cannot open script-file";
-LAB_0804ee56:
- // WARNING: Subroutine does not return
- __cxa_throw(puVar6,char_const*::typeinfo,0);
-}
-
-
-
-// DWARF original prototype: void close(TReadScriptFile * this)
-
-int __thiscall TReadScriptFile::close(TReadScriptFile *this,int __fd)
-
-{
- int iVar1;
- int *piVar2;
-
- iVar1 = this->RecursionDepth;
- if (iVar1 != -1) {
- iVar1 = fclose((FILE *)this->File[iVar1]);
- if (iVar1 != 0) {
- piVar2 = __errno_location();
- iVar1 = *piVar2;
- ::error(&DAT_080ef4e0,iVar1);
- }
- this->RecursionDepth = this->RecursionDepth + -1;
- return iVar1;
- }
- ::error("TReadScriptFile::close: Keine Datei offen.\n");
- return iVar1;
-}
-
-
-
-// DWARF original prototype: void error(TReadScriptFile * this, char * Text)
-
-void __thiscall TReadScriptFile::error(TReadScriptFile *this,char *Text)
-
-{
- char *pcVar1;
- int iVar2;
- int *piVar3;
- undefined4 *puVar4;
- char *p;
- char (*pacVar5) [4096];
-
- // try { // try from 0804ef52 to 0804f008 has its CatchHandler @ 0804f020
- pcVar1 = findLast(this->Filename[this->RecursionDepth],'/');
- if (pcVar1 == (char *)0x0) {
- iVar2 = this->RecursionDepth;
- pacVar5 = this->Filename + iVar2;
- }
- else {
- iVar2 = this->RecursionDepth;
- pacVar5 = (char (*) [4096])(pcVar1 + 1);
- }
- snprintf(ErrorString,100,"error in script-file \"%s\", line %d: %s",pacVar5,this->Line[iVar2],
- Text);
- iVar2 = this->RecursionDepth;
- while (iVar2 != -1) {
- iVar2 = fclose((FILE *)this->File[iVar2]);
- if (iVar2 != 0) {
- piVar3 = __errno_location();
- ::error(&DAT_080ef4e0,*piVar3);
- }
- this->RecursionDepth = this->RecursionDepth + -1;
- iVar2 = this->RecursionDepth;
- }
- puVar4 = (undefined4 *)__cxa_allocate_exception(4);
- *puVar4 = ErrorString;
- // WARNING: Subroutine does not return
- __cxa_throw(puVar4,char*::typeinfo,0);
-}
-
-
-
-// WARNING: Variable defined which should be unmapped: pos
-// DWARF original prototype: void nextToken(TReadScriptFile * this)
-
-void __thiscall TReadScriptFile::nextToken(TReadScriptFile *this)
-
-{
- char *__file;
- FILE *__stream;
- bool bVar1;
- uint uVar2;
- int iVar3;
- int *piVar4;
- int State;
- undefined4 uVar5;
- int c;
- char *pcVar6;
- int in_stack_ffffffcc;
- int local_1c;
- int Sign;
- FILE *f;
- int pos;
-
- if (this->RecursionDepth == -1) {
- ::error(&DAT_080ef660);
-LAB_0804f216:
- this->Token = ENDOFFILE;
- return;
- }
- __file = this->String;
-LAB_0804f062:
- f = (FILE *)0x0;
- uVar5 = 0;
- uVar2 = 4000;
- pcVar6 = __file;
- if (((uint)__file & 4) != 0) {
- pcVar6 = this->String + 4;
- uVar2 = 0xf9c;
- this->String[0] = '\0';
- this->String[1] = '\0';
- this->String[2] = '\0';
- this->String[3] = '\0';
- }
- for (uVar2 = uVar2 >> 2; uVar2 != 0; uVar2 = uVar2 - 1) {
- pcVar6[0] = '\0';
- pcVar6[1] = '\0';
- pcVar6[2] = '\0';
- pcVar6[3] = '\0';
- pcVar6 = pcVar6 + 4;
- }
- this->Number = 0;
- __stream = (FILE *)this->File[this->RecursionDepth];
- local_1c = 1;
-LAB_0804f0b6:
- if (f == (FILE *)0xf9f) {
- error(this,"string too long");
- }
- switch(uVar5) {
- case 0:
- iVar3 = getc(__stream);
- if (iVar3 != -1) {
- if (iVar3 == 10) {
- this->Line[this->RecursionDepth] = this->Line[this->RecursionDepth] + 1;
- }
- else {
- // try { // try from 0804f0f5 to 0804f92b has its CatchHandler @ 0804f931
- bVar1 = isSpace(iVar3);
- if (((!bVar1) && (uVar5 = 1, iVar3 != 0x23)) && (uVar5 = 0x1e, iVar3 != 0x40)) {
- bVar1 = isAlpha(iVar3);
- if (bVar1) {
- uVar5 = 2;
- this->String[(int)f] = (char)iVar3;
- f = (FILE *)((int)&f->_flags + 1);
- }
- else {
- bVar1 = isDigit(iVar3);
- if (bVar1) {
- this->Number = iVar3 + -0x30;
- uVar5 = 3;
- }
- else {
- uVar5 = 6;
- if (((iVar3 != 0x22) && (uVar5 = 0xb, iVar3 != 0x5b)) &&
- ((uVar5 = 0x16, iVar3 != 0x3c &&
- ((uVar5 = 0x19, iVar3 != 0x3e && (uVar5 = 0x1b, iVar3 != 0x2d))))))
- {
- uVar5 = 10;
- this->Special = (char)iVar3;
- }
- }
- }
- }
- }
- goto LAB_0804f0b6;
- }
-LAB_0804f1c2:
- iVar3 = this->RecursionDepth;
- if (iVar3 < 1) goto LAB_0804f216;
- if (iVar3 == -1) {
- ::error("TReadScriptFile::close: Keine Datei offen.\n");
- }
- else {
- iVar3 = fclose((FILE *)this->File[iVar3]);
- if (iVar3 != 0) {
- piVar4 = __errno_location();
- ::error(&DAT_080ef4e0,*piVar4);
- }
- this->RecursionDepth = this->RecursionDepth + -1;
- }
- goto LAB_0804f062;
- case 1:
- iVar3 = getc(__stream);
- if (iVar3 == -1) goto LAB_0804f1c2;
- if (iVar3 == 10) {
- this->Line[this->RecursionDepth] = this->Line[this->RecursionDepth] + 1;
- goto LAB_0804f24a;
- }
- goto LAB_0804f0b6;
- case 2:
- iVar3 = getc(__stream);
- if (f == (FILE *)0x1e) {
- error(this,"identifier too long");
- }
- if (iVar3 == -1) {
-LAB_0804f2aa:
- this->Token = IDENTIFIER;
- return;
- }
- bVar1 = isAlpha(iVar3);
- if (((!bVar1) && (bVar1 = isDigit(iVar3), !bVar1)) && (iVar3 != 0x5f)) {
- ungetc(iVar3,__stream);
- goto LAB_0804f2aa;
- }
- goto LAB_0804f275;
- case 3:
- iVar3 = getc(__stream);
- if (iVar3 == -1) {
-LAB_0804f31a:
- this->Token = NUMBER;
- return;
- }
- bVar1 = isDigit(iVar3);
- if (bVar1) goto LAB_0804f325;
- if (iVar3 != 0x2d) {
- ungetc(iVar3,__stream);
- goto LAB_0804f31a;
- }
- goto LAB_0804f2ea;
- case 4:
- iVar3 = getc(__stream);
- if (iVar3 == -1) {
- error(this,"unexpected end of file");
- }
- bVar1 = isDigit(iVar3);
- if (!bVar1) {
- error(this,"syntax error");
- goto switchD_0804f0cc_caseD_5;
- }
- uVar5 = 5;
- this->Number = iVar3 + -0x30;
- goto LAB_0804f0b6;
- case 5:
-switchD_0804f0cc_caseD_5:
- iVar3 = getc(__stream);
- if (iVar3 == -1) {
-LAB_0804f3a8:
- this->Bytes[(int)(f->_shortbuf + -0x47)] = (uchar)this->Number;
- this->Token = BYTES;
- return;
- }
- bVar1 = isDigit(iVar3);
- if (bVar1) goto LAB_0804f325;
- if (iVar3 != 0x2d) {
- ungetc(iVar3,__stream);
- goto LAB_0804f3a8;
- }
-LAB_0804f2ea:
- this->Bytes[(int)(f->_shortbuf + -0x47)] = (uchar)this->Number;
- f = (FILE *)((int)&f->_flags + 1);
- uVar5 = 4;
- goto LAB_0804f0b6;
- case 6:
- iVar3 = getc(__stream);
- if (iVar3 == -1) {
- error(this,"unexpected end of file");
- }
- if (iVar3 == 0x22) {
- uVar5 = 8;
- }
- else if (iVar3 == 0x5c) {
- uVar5 = 7;
- }
- else {
- if (iVar3 == 10) {
- this->Line[this->RecursionDepth] = this->Line[this->RecursionDepth] + 1;
- }
-LAB_0804f275:
- this->String[(int)f] = (char)iVar3;
- f = (FILE *)((int)&f->_flags + 1);
- }
- goto LAB_0804f0b6;
- case 7:
- iVar3 = getc(__stream);
- if (iVar3 == -1) {
- error(this,"unexpected end of file");
- }
- if (iVar3 == 0x6e) {
- this->String[(int)f] = '\n';
- }
- else {
- this->String[(int)f] = (char)iVar3;
- }
- f = (FILE *)((int)&f->_flags + 1);
- uVar5 = 6;
- goto LAB_0804f0b6;
- case 8:
- this->Token = STRING;
- return;
- default:
- ::error(&DAT_080ef620);
-LAB_0804f24a:
- uVar5 = 0;
- goto LAB_0804f0b6;
- case 10:
- break;
- case 0xb:
- iVar3 = getc(__stream);
- this->Special = '[';
- if (iVar3 == -1) break;
- bVar1 = isDigit(iVar3);
- if (bVar1) {
- local_1c = 1;
- this->Number = iVar3 + -0x30;
- }
- else {
- if (iVar3 != 0x2d) goto LAB_0804f507;
- local_1c = -1;
- this->Number = 0;
- }
- uVar5 = 0xc;
- goto LAB_0804f0b6;
- case 0xc:
- iVar3 = getc(__stream);
- if (iVar3 == -1) {
- error(this,"unexpected end of file");
- }
- bVar1 = isDigit(iVar3);
- if (bVar1) goto LAB_0804f325;
- if (iVar3 != 0x2c) {
- error(this,"syntax error");
- goto switchD_0804f0cc_caseD_d;
- }
- uVar5 = 0xd;
- this->CoordX = local_1c * this->Number;
- goto LAB_0804f0b6;
- case 0xd:
-switchD_0804f0cc_caseD_d:
- iVar3 = getc(__stream);
- if (iVar3 == -1) {
- error(this,"unexpected end of file");
- }
- bVar1 = isDigit(iVar3);
- if (bVar1) {
- local_1c = 1;
- this->Number = iVar3 + -0x30;
- }
- else {
- if (iVar3 != 0x2d) {
- error(this,"syntax error");
- goto switchD_0804f0cc_caseD_e;
- }
- local_1c = -1;
- this->Number = 0;
- }
- uVar5 = 0xe;
- goto LAB_0804f0b6;
- case 0xe:
-switchD_0804f0cc_caseD_e:
- iVar3 = getc(__stream);
- if (iVar3 == -1) {
- error(this,"unexpected end of file");
- }
- bVar1 = isDigit(iVar3);
- if (bVar1) goto LAB_0804f325;
- if (iVar3 != 0x2c) {
- error(this,"syntax error");
- goto switchD_0804f0cc_caseD_f;
- }
- uVar5 = 0xf;
- this->CoordY = local_1c * this->Number;
- goto LAB_0804f0b6;
- case 0xf:
-switchD_0804f0cc_caseD_f:
- iVar3 = getc(__stream);
- if (iVar3 == -1) {
- error(this,"unexpected end of file");
- }
- bVar1 = isDigit(iVar3);
- if (bVar1) {
- local_1c = 1;
- this->Number = iVar3 + -0x30;
- }
- else {
- if (iVar3 != 0x2d) {
- error(this,"syntax error");
- goto switchD_0804f0cc_caseD_10;
- }
- local_1c = -1;
- this->Number = 0;
- }
- uVar5 = 0x10;
- goto LAB_0804f0b6;
- case 0x10:
-switchD_0804f0cc_caseD_10:
- iVar3 = getc(__stream);
- if (iVar3 == -1) {
- error(this,"unexpected end of file");
- }
- bVar1 = isDigit(iVar3);
- if (bVar1) {
-LAB_0804f325:
- this->Number = iVar3 + -0x30 + this->Number * 10;
- }
- else {
- if (iVar3 != 0x5d) {
- error(this,"syntax error");
-switchD_0804f0cc_caseD_11:
- this->Token = COORDINATE;
- return;
- }
- uVar5 = 0x11;
- this->CoordZ = local_1c * this->Number;
- }
- goto LAB_0804f0b6;
- case 0x11:
- goto switchD_0804f0cc_caseD_11;
- case 0x16:
- iVar3 = getc(__stream);
- this->Special = '<';
- if (iVar3 == -1) break;
- uVar5 = 0x17;
- if ((iVar3 != 0x3d) && (uVar5 = 0x18, iVar3 != 0x3e)) goto LAB_0804f507;
- goto LAB_0804f0b6;
- case 0x17:
- this->Special = 'L';
- break;
- case 0x18:
- this->Special = 'N';
- break;
- case 0x19:
- iVar3 = getc(__stream);
- this->Special = '>';
- if (iVar3 == -1) break;
- uVar5 = 0x1a;
- if (iVar3 != 0x3d) goto LAB_0804f507;
- goto LAB_0804f0b6;
- case 0x1a:
- this->Special = 'G';
- break;
- case 0x1b:
- iVar3 = getc(__stream);
- this->Special = '-';
- if (iVar3 == -1) break;
- uVar5 = 0x1c;
- if (iVar3 == 0x3e) goto LAB_0804f0b6;
-LAB_0804f507:
- ungetc(iVar3,__stream);
- break;
- case 0x1c:
- this->Special = 'I';
- break;
- case 0x1e:
- iVar3 = getc(__stream);
- if (iVar3 == -1) {
- error(this,"unexpected end of file");
- }
- if (iVar3 != 0x22) {
- error(this,"syntax error");
- goto switchD_0804f0cc_caseD_1f;
- }
- uVar5 = 0x1f;
- goto LAB_0804f0b6;
- case 0x1f:
-switchD_0804f0cc_caseD_1f:
- iVar3 = getc(__stream);
- if (iVar3 == -1) {
- error(this,"unexpected end of file");
- }
- if (iVar3 != 0x22) goto LAB_0804f275;
- uVar5 = 0x20;
- goto LAB_0804f0b6;
- case 0x20:
- goto switchD_0804f0cc_caseD_20;
- }
- this->Token = SPECIAL;
- return;
-switchD_0804f0cc_caseD_20:
- open(this,__file,in_stack_ffffffcc);
- goto LAB_0804f062;
-}
-
-
-
-// DWARF original prototype: char * readIdentifier(TReadScriptFile * this)
-
-char * __thiscall TReadScriptFile::readIdentifier(TReadScriptFile *this)
-
-{
- // try { // try from 0804f95f to 0804f978 has its CatchHandler @ 0804f9b8
- nextToken(this);
- if (this->Token != IDENTIFIER) {
- error(this,"identifier expected");
- }
- if (this->Token != IDENTIFIER) {
- // try { // try from 0804f989 to 0804f99b has its CatchHandler @ 0804f9a5
- error(this,"identifier expected");
- }
- strLower(this->String);
- return this->String;
-}
-
-
-
-// DWARF original prototype: int readNumber(TReadScriptFile * this)
-
-int __thiscall TReadScriptFile::readNumber(TReadScriptFile *this)
-
-{
- TOKEN TVar1;
- int sgn;
- int iVar2;
-
- // try { // try from 0804f9e2 to 0804fa07 has its CatchHandler @ 0804fa70
- nextToken(this);
- TVar1 = this->Token;
- iVar2 = 1;
- if ((TVar1 == SPECIAL) && (this->Special == '-')) {
- iVar2 = -1;
- // try { // try from 0804fa46 to 0804fa4a has its CatchHandler @ 0804fa70
- nextToken(this);
- TVar1 = this->Token;
- }
- if (TVar1 != NUMBER) {
- error(this,"number expected");
- }
- if (this->Token != NUMBER) {
- // try { // try from 0804fa18 to 0804fa1c has its CatchHandler @ 0804fa4f
- error(this,"number expected");
- }
- return iVar2 * this->Number;
-}
-
-
-
-// DWARF original prototype: char * readString(TReadScriptFile * this)
-
-char * __thiscall TReadScriptFile::readString(TReadScriptFile *this)
-
-{
- // try { // try from 0804fa8f to 0804faa8 has its CatchHandler @ 0804fae0
- nextToken(this);
- if (this->Token != STRING) {
- error(this,"string expected");
- }
- if (this->Token != STRING) {
- // try { // try from 0804fab9 to 0804fabd has its CatchHandler @ 0804facb
- error(this,"string expected");
- }
- return this->String;
-}
-
-
-
-// DWARF original prototype: uchar * readBytesequence(TReadScriptFile * this)
-
-uchar * __thiscall TReadScriptFile::readBytesequence(TReadScriptFile *this)
-
-{
- // try { // try from 0804faff to 0804fb18 has its CatchHandler @ 0804fb50
- nextToken(this);
- if (this->Token != BYTES) {
- error(this,"byte-sequence expected");
- }
- if (this->Token != BYTES) {
- // try { // try from 0804fb29 to 0804fb2d has its CatchHandler @ 0804fb3b
- error(this,"byte-sequence expected");
- }
- return this->Bytes;
-}
-
-
-
-// DWARF original prototype: void readCoordinate(TReadScriptFile * this, int * x, int * y, int * z)
-
-void __thiscall TReadScriptFile::readCoordinate(TReadScriptFile *this)
-
-{
- int *in_stack_00000008;
- int *in_stack_0000000c;
- int *in_stack_00000010;
-
- // try { // try from 0804fb6f to 0804fb88 has its CatchHandler @ 0804fbe0
- nextToken(this);
- if (this->Token != COORDINATE) {
- error(this,"coordinates expected");
- }
- if (this->Token != COORDINATE) {
- // try { // try from 0804fb99 to 0804fb9d has its CatchHandler @ 0804fbc6
- error(this,"coordinates expected");
- }
- *in_stack_00000008 = this->CoordX;
- *in_stack_0000000c = this->CoordY;
- *in_stack_00000010 = this->CoordZ;
- return;
-}
-
-
-
-// DWARF original prototype: char readSpecial(TReadScriptFile * this)
-
-char __thiscall TReadScriptFile::readSpecial(TReadScriptFile *this)
-
-{
- // try { // try from 0804fbff to 0804fc18 has its CatchHandler @ 0804fc50
- nextToken(this);
- if (this->Token != SPECIAL) {
- error(this,"special-char expected");
- }
- if (this->Token != SPECIAL) {
- // try { // try from 0804fc29 to 0804fc2d has its CatchHandler @ 0804fc3c
- error(this,"special-char expected");
- }
- return this->Special;
-}
-
-
-
-// WARNING: Variable defined which should be unmapped: Symbol_local
-// DWARF original prototype: void readSymbol(TReadScriptFile * this, char Symbol)
-
-void __thiscall TReadScriptFile::readSymbol(TReadScriptFile *this,char Symbol)
-
-{
- char Symbol_local;
-
- // try { // try from 0804fc76 to 0804fc8f has its CatchHandler @ 0804fcf0
- nextToken(this);
- if (this->Token != SPECIAL) {
- error(this,"special-char expected");
- if (this->Token != SPECIAL) {
- // try { // try from 0804fca0 to 0804fca4 has its CatchHandler @ 0804fcd3
- error(this,"special-char expected");
- }
- }
- if (Symbol != this->Special) {
- // try { // try from 0804fcc7 to 0804fccb has its CatchHandler @ 0804fcf0
- error(this,"special-char expected");
- }
- return;
-}
-
-
-
-// DWARF original prototype: char * getIdentifier(TReadScriptFile * this)
-
-char * __thiscall TReadScriptFile::getIdentifier(TReadScriptFile *this)
-
-{
- if (this->Token != IDENTIFIER) {
- // try { // try from 0804fd1c to 0804fd2e has its CatchHandler @ 0804fd38
- error(this,"identifier expected");
- }
- strLower(this->String);
- return this->String;
-}
-
-
-
-// DWARF original prototype: int getNumber(TReadScriptFile * this)
-
-int __thiscall TReadScriptFile::getNumber(TReadScriptFile *this)
-
-{
- if (this->Token != NUMBER) {
- // try { // try from 0804fd6c to 0804fd70 has its CatchHandler @ 0804fd80
- error(this,"number expected");
- }
- return this->Number;
-}
-
-
-
-// DWARF original prototype: char * getString(TReadScriptFile * this)
-
-char * __thiscall TReadScriptFile::getString(TReadScriptFile *this)
-
-{
- if (this->Token != STRING) {
- // try { // try from 0804fdbc to 0804fdc0 has its CatchHandler @ 0804fdd0
- error(this,"string expected");
- }
- return this->String;
-}
-
-
-
-// DWARF original prototype: uchar * getBytesequence(TReadScriptFile * this)
-
-uchar * __thiscall TReadScriptFile::getBytesequence(TReadScriptFile *this)
-
-{
- if (this->Token != BYTES) {
- // try { // try from 0804fe0c to 0804fe10 has its CatchHandler @ 0804fe20
- error(this,"byte-sequence expected");
- }
- return this->Bytes;
-}
-
-
-
-// DWARF original prototype: void getCoordinate(TReadScriptFile * this, int * x, int * y, int * z)
-
-void __thiscall TReadScriptFile::getCoordinate(TReadScriptFile *this)
-
-{
- int *in_stack_00000008;
- int *in_stack_0000000c;
- int *in_stack_00000010;
-
- if (this->Token != COORDINATE) {
- // try { // try from 0804fe5c to 0804fe60 has its CatchHandler @ 0804fe90
- error(this,"coordinates expected");
- }
- *in_stack_00000008 = this->CoordX;
- *in_stack_0000000c = this->CoordY;
- *in_stack_00000010 = this->CoordZ;
- return;
-}
-
-
-
-// DWARF original prototype: char getSpecial(TReadScriptFile * this)
-
-char __thiscall TReadScriptFile::getSpecial(TReadScriptFile *this)
-
-{
- if (this->Token != SPECIAL) {
- // try { // try from 0804fecc to 0804fed0 has its CatchHandler @ 0804fee0
- error(this,"special-char expected");
- }
- return this->Special;
-}
-
-
-
// DWARF original prototype: void TWriteScriptFile(TWriteScriptFile * this)
void __thiscall TWriteScriptFile::TWriteScriptFile(TWriteScriptFile *this)
diff --git a/reference/types.hh b/reference/types.hh index e66ea72..5bfc014 100644 --- a/reference/types.hh +++ b/reference/types.hh @@ -96,15 +96,6 @@ struct TDatabasePoolConnection { bool TransactionRunning; }; -struct TDatabaseSettings { - char Product[30]; - char Database[30]; - char Login[30]; - char Password[30]; - char Host[30]; - char Port[6]; -}; - struct Semaphore { int value; struct pthread_mutex_t mutex; @@ -828,12 +819,6 @@ struct TWriteBinaryFile { char Filename[4096]; }; -struct TWriteScriptFile { - FILE *File; - char Filename[4096]; - int Line; -}; - struct TReadBinaryFile { struct TReadStream super_TReadStream; undefined field1_0x1; @@ -844,21 +829,6 @@ struct TReadBinaryFile { int FileSize; }; -struct TReadScriptFile { - enum TOKEN Token; - FILE *File[3]; - char Filename[3][4096]; - int Line[3]; - char String[4000]; - int RecursionDepth; - uchar *Bytes; - int Number; - int CoordX; - int CoordY; - int CoordZ; - char Special; -}; - struct TNPC { struct TNonplayer super_TNonplayer; ulong Interlocutor; @@ -891,12 +861,6 @@ struct TQueryManagerConnection { undefined field8_0x2f; }; -struct TQueryManagerSettings { - char Host[50]; - int Port; - char Password[30]; -}; - struct TQueryManagerPoolConnection { struct TQueryManagerConnectionPool *QueryManagerConnectionPool; struct TQueryManagerConnection *QueryManagerConnection; diff --git a/src/config.cc b/src/config.cc index f30b2b6..bae583a 100644 --- a/src/config.cc +++ b/src/config.cc @@ -1,4 +1,4 @@ -#include "main.hh" +#include "config.hh" char BINPATH[4096]; char DATAPATH[4096]; diff --git a/src/config.hh b/src/config.hh new file mode 100644 index 0000000..c2f1e14 --- /dev/null +++ b/src/config.hh @@ -0,0 +1,61 @@ +#ifndef TIBIA_CONFIG_HH_ +#define TIBIA_CONFIG_HH_ 1 + +#include "main.hh" +#include "enums.hh" + +struct TDatabaseSettings { + char Product[30]; + char Database[30]; + char Login[30]; + char Password[30]; + char Host[30]; + char Port[6]; +}; + +struct TQueryManagerSettings { + char Host[50]; + int Port; + char Password[30]; +}; + +extern char BINPATH[4096]; +extern char DATAPATH[4096]; +extern char LOGPATH[4096]; +extern char MAPPATH[4096]; +extern char MONSTERPATH[4096]; +extern char NPCPATH[4096]; +extern char ORIGMAPPATH[4096]; +extern char SAVEPATH[4096]; +extern char USERPATH[4096]; +extern int SHMKey; +extern int AdminPort; +extern int GamePort; +extern int QueryManagerPort; +extern char AdminAddress[16]; +extern char GameAddress[16]; +extern char QueryManagerAddress[16]; +extern char QueryManagerAdminPW[9]; +extern char QueryManagerGamePW[9]; +extern char QueryManagerWebPW[9]; +extern int DebugLevel; +extern bool PrivateWorld; +extern TWorldType WorldType; +extern char WorldName[30]; +extern int MaxPlayers; +extern int MaxNewbies; +extern int PremiumPlayerBuffer; +extern int PremiumNewbieBuffer; +extern int Beat; +extern int RebootTime; +extern TDatabaseSettings ADMIN_DATABASE; +extern TDatabaseSettings VOLATILE_DATABASE; +extern TDatabaseSettings WEB_DATABASE; +extern TDatabaseSettings FORUM_DATABASE; +extern TDatabaseSettings MANAGER_DATABASE; +extern int NumberOfQueryManagers; +extern TQueryManagerSettings QUERY_MANAGER[10]; + +void ReadConfig(void); + +#endif //TIBIA_CONFIG_HH_ diff --git a/src/main.cc b/src/main.cc index 0b6e8c0..a930aef 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,4 +1,5 @@ #include "main.hh" +#include "config.hh" #include <signal.h> #include <sys/time.h> diff --git a/src/main.hh b/src/main.hh index f888521..7aa329a 100644 --- a/src/main.hh +++ b/src/main.hh @@ -80,49 +80,11 @@ STATIC_ASSERT(sizeof(int) == 4); // be too difficult either. Overall this design is outdated and should be reviewed. // Nevertheless, we should focus on getting it working as intended, on the target // platform (which is Linux 32-bits) before attempting to refine it. -STATIC_ASSERT(OS_LINUX); -#include <sys/types.h> +//STATIC_ASSERT(OS_LINUX); +//#include <sys/types.h> +typedef int pid_t; // -// config.cc -extern char BINPATH[4096]; -extern char DATAPATH[4096]; -extern char LOGPATH[4096]; -extern char MAPPATH[4096]; -extern char MONSTERPATH[4096]; -extern char NPCPATH[4096]; -extern char ORIGMAPPATH[4096]; -extern char SAVEPATH[4096]; -extern char USERPATH[4096]; -extern int SHMKey; -extern int AdminPort; -extern int GamePort; -extern int QueryManagerPort; -extern char AdminAddress[16]; -extern char GameAddress[16]; -extern char QueryManagerAddress[16]; -extern char QueryManagerAdminPW[9]; -extern char QueryManagerGamePW[9]; -extern char QueryManagerWebPW[9]; -extern int DebugLevel; -extern bool PrivateWorld; -extern TWorldType WorldType; -extern char WorldName[30]; -extern int MaxPlayers; -extern int MaxNewbies; -extern int PremiumPlayerBuffer; -extern int PremiumNewbieBuffer; -extern int Beat; -extern int RebootTime; -extern TDatabaseSettings ADMIN_DATABASE; -extern TDatabaseSettings VOLATILE_DATABASE; -extern TDatabaseSettings WEB_DATABASE; -extern TDatabaseSettings FORUM_DATABASE; -extern TDatabaseSettings MANAGER_DATABASE; -extern int NumberOfQueryManagers; -extern TQueryManagerSettings QUERY_MANAGER[10]; -void ReadConfig(void); - // shm.cc void StartGame(void); void CloseGame(void); diff --git a/src/script.cc b/src/script.cc new file mode 100644 index 0000000..d3e8134 --- /dev/null +++ b/src/script.cc @@ -0,0 +1,617 @@ +#include "script.hh" + +// ============================================================================= +// ============================================================================= +// TODO(fusion): Move to `util.cc`? + +static bool isSpace(int c){ + return c == ' ' + || c == '\t' + || c == '\n' + || c == '\r' + || c == '\v' + || c == '\f'; +} + +static bool isAlpha(int c){ + // TODO(fusion): This is most likely wrong! We're assuming a direct conversion + // from `char` to `int` which will cause sign extension for negative values. This + // wouldn't be a problem if we expected to parse only streams of `char[]` but can + // be problematic for the output of `getc` which returns bytes as `unsigned char` + // converted to `int`. + // TLDR: The parameter `c` should be `uint8`. + return ('A' <= c && c <= 'Z') + || ('a' <= c && c <= 'z') + || c == -0x1C // E4 => ä + || c == -0x0A // F6 => ö + || c == -0x04 // FC => ü + || c == -0x3C // C4 => Ä + || c == -0x2A // D6 => Ö + || c == -0x24 // DC => Ü + || c == -0x21; // DF => ß +} + +static bool isEngAlpha(int c){ + return ('A' <= c && c <= 'Z') + || ('a' <= c && c <= 'z'); +} + +static bool isDigit(int c){ + return ('0' <= c && c <= '9'); +} + +static int toLower(int c){ + // TODO(fusion): Same problem as `isAlpha`. + if(('A' <= c && c <= 'Z') || (0xC0 <= c && c <= 0xDE && c != 0xD7)){ + c += 32; + } + return c; +} + +static int toUpper(int c){ + // TODO(fusion): Same problem as `isAlpha`. + if(('A' <= c && c <= 'Z') || (0xE0 <= c && c <= 0xFE && c != 0xF7)){ + c -= 32; + } + return c; +} + +static char *strLower(char *s){ + for(int i = 0; s[i] != 0; i += 1){ + s[i] = (char)toLower(s[i]); + } + return s; +} + +static char *strUpper(char *s){ + for(int i = 0; s[i] != 0; i += 1){ + s[i] = (char)toUpper(s[i]); + } + return s; +} + +static int stricmp(const char *s1, const char *s2, int Pos){ + for(int i = 0; i < Pos; i += 1){ + int c1 = toLower(s1[i]); + int c2 = toLower(s2[i]); + if(c1 > c2){ + return 1; + }else if(c1 < c2){ + return -1; + }else{ + ASSERT(c1 == c2); + if(c1 == 0){ + return 0; + } + } + } +} + +static char *findFirst(char *s, char c){ + return strchr(s, (int)c); +} + +static char *findLast(char *s, char c){ + char *Current = s; + char *Last = NULL; + while(true){ + Current = strchr(Current, (int)c); + if(Current == NULL) + break; + Last = Current; + Current += 1; // skip character + } + return Last; +} + +// ============================================================================= +// ============================================================================= + +TReadScriptFile::TReadScriptFile(void){ + this->RecursionDepth = -1; + this->Bytes = (uint8*)this->String; +} + +TReadScriptFile::~TReadScriptFile(void){ + if(this->RecursionDepth != -1){ + ::error("TReadScriptFile::~TReadScriptFile: Datei ist noch offen.\n"); + for(int Depth = this->RecursionDepth; Depth >= 0; Depth -= 1){ + // TODO(fusion): Probably `TReadScriptFile::close` inlined? + if(fclose(this->File[Depth]) != 0){ + ::error("TReadScriptFile::close: Fehler %d beim Schließen der Datei.\n", errno); + } + } + this->RecursionDepth = -1; + } +} + +void TReadScriptFile::open(const char *FileName){ + int Depth = this->RecursionDepth + 1; + if((Depth + 1) >= NARRAY(this->File)){ + ::error("TReadScriptFile::open: Rekursionstiefe zu groß.\n"); + throw "Recursion depth too high"; + } + + ASSERT(Depth >= 0); + + // TODO(fusion): More `strcpy`s... + if(Depth > 0 && FileName[0] != '/'){ + strcpy(this->Filename[Depth], this->Filename[Depth - 1]); + if(char *Slash = findLast(this->Filename[Depth], '/')){ + strcpy(Slash + 1, FileName); + }else{ + strcpy(this->Filename[Depth], FileName); + } + }else{ + strcpy(this->Filename[Depth], FileName); + } + + this->File[Depth] = fopen(this->Filename[Depth], "rb"); + if(this->File[Depth] == NULL){ + ::error("TReadScriptFile::open: Kann Datei %s nicht öffnen.\n", this->Filename[Depth]); + ::error("Fehler %d: %s.\n", errno, strerror(errno)); + throw "Cannot open script-file"; + } + + this->Line[Depth] = 1; + this->RecursionDepth = Depth; +} + +void TReadScriptFile::close(void){ + int Depth = this->RecursionDepth; + if(Depth <= -1){ + ::error("TReadScriptFile::close: Keine Datei offen.\n"); + return; + } + + ASSERT(Depth < NARRAY(this->File)); + if(fclose(this->File[Depth]) != 0){ + ::error("TReadScriptFile::close: Fehler %d beim Schließen der Datei.\n", errno); + } + this->RecursionDepth -= 1; +} + +void TReadScriptFile::error(const char *Text){ + static char ErrorString[100]; + + int Depth = this->RecursionDepth; + ASSERT(Depth >= 0 && Depth <= NARRAY(this->File)); + + const char *Filename = this->Filename[Depth]; + if(const char *Slash = findLast(this->Filename[Depth], '/')){ + Filename = Slash + 1; + } + + snprintf(ErrorString, sizeof(ErrorString), + "error in script-file \"%s\", line %d: %s", + Filename, this->Line[Depth], Text); + + // TODO(fusion): Reset? Also seems like `TReadScriptFile::close` was inlined. + for(; Depth >= 0; Depth -= 1){ + if(fclose(this->File[Depth]) != 0){ + ::error("TReadScriptFile::close: Fehler %d beim Schließen der Datei.\n", errno); + } + } + this->RecursionDepth = -1; + + throw ErrorString; +} + +// NOTE(fusion): This function in particular was in pretty bad shape. It should +// be one of the few that doesn't roughly match the original version. Nevertheless, +// I think we should properly split this into a lexer and parser. We find ourselves +// parsing strings and numbers multiple times here, whereas a simple lexer and +// parser would simplify the work tremendously. +// TODO(fusion): This needs to be tested to make sure it behaves like the original. +void TReadScriptFile::nextToken(void){ + if(this->RecursionDepth == -1){ + ::error("TReadScriptFile::nextToken: Kein Skript zum Lesen geöffnet.\n"); + this->Token = ENDOFFILE; + return; + } + + // NOTE(fusion): Reset any previous token state. + memset(this->String, 0, sizeof(this->String)); + this->Number = 0; + this->CoordX = 0; + this->CoordY = 0; + this->CoordZ = 0; + this->Special = 0; + + // NOTE(fusion): `TReadScriptFile::error` will format and throw an error + // string that must be handled up the call stack. It must be considered + // an exit point for parsing errors in this function. + + while(true){ + int Depth = this->RecursionDepth; + ASSERT(Depth >= 0 && Depth < NARRAY(this->File)); + FILE *File = this->File[Depth]; + + int c; + do{ + c = getc(File); + if(c == '\n'){ + this->Line[Depth] += 1; + } + }while(isSpace(c)); + + if(c == EOF){ + if(Depth == 0){ + this->Token = ENDOFFILE; + return; + } + + this->close(); + continue; + } + + switch(c){ + case '#':{ // COMMENT + while(true){ + int next = getc(File); + if(next == '\n' || next == EOF){ + if(next == '\n'){ + this->Line[Depth] += 1; + } + break; + } + } + break; + } + + case '@':{ // INCLUDE + int next = getc(File); + if(next == EOF){ + this->error("unexpected end of file"); + }else if(next != '"'){ + this->error("syntax error"); + } + + int StringLength = 0; + while(true){ + // TODO(fusion): I don't think new lines are even allowed in + // file names but we should keep track of the line number even + // if they happen here. + next = getc(File); + if(next == EOF){ + this->error("unexpected end of file"); + }else if(next == '"'){ + break; + } + + if(StringLength >= (NARRAY(this->String) - 1)){ + this->error("string too long"); + } + this->String[StringLength] = (char)next; + StringLength += 1; + } + + // TODO(fusion): Maybe check if the path is empty? + this->open(this->String); + + // NOTE(fusion): This is the only place we parse a string without + // returning it as a token. We need to reset it to make sure any + // subsequent string-like token will be properly parsed. + memset(this->String, 0, sizeof(this->String)); + + break; + } + + case '"':{ // STRING + int StringLength = 0; + while(true){ + int next = getc(File); + if(next == EOF){ + this->error("unexpected end of file"); + }else if(next == '\\'){ + next = getc(File); + if(next == EOF){ + this->error("unexpected end of file"); + }else if(next == 'n'){ + next = '\n'; + } + }else if(next == '\n'){ + // TODO(fusion): Shouldn't we prevent non-escaped new + // lines inside strings? + this->Line[Depth] += 1; + }else if(next == '"'){ + break; + } + + if(StringLength >= (NARRAY(this->String) - 1)){ + this->error("string too long"); + } + this->String[StringLength] = (char)next; + StringLength += 1; + } + this->Token = STRING; + return; + } + + case '[':{ // COORDINATE + // NOTE(fusion): X-Coordinate or SPECIAL '['. + int Sign = -1; + int Coord = 0; + int next = getc(File); + if(isDigit(next)){ + Sign = 1; + Coord = next - '0'; + }else if(next != '-'){ + this->Token = SPECIAL; + this->Special = '['; + if(next != EOF){ + ungetc(next, File); + } + return; + } + + while(true){ + next = getc(File); + if(next == EOF){ + this->error("unexpected end of file"); + }else if(isDigit(next)){ + Coord *= 10; + Coord += next - '0'; + }else if(next == ','){ + break; + }else{ + this->error("syntax error"); + } + } + + this->CoordX = Sign * Coord; + + // NOTE(fusion): Y-Coordinate. + Sign = -1; + Coord = 0; + next = getc(File); + if(isDigit(next)){ + Sign = 1; + Coord = next - '0'; + }else if(next != '-'){ + this->error("syntax error"); + } + + while(true){ + next = getc(File); + if(next == EOF){ + this->error("unexpected end of file"); + }else if(isDigit(next)){ + Coord *= 10; + Coord += next - '0'; + }else if(next == ','){ + break; + }else{ + this->error("syntax error"); + } + } + + this->CoordY = Sign * Coord; + + // NOTE(fusion): Z-Coordinate. + Sign = -1; + Coord = 0; + next = getc(File); + if(isDigit(next)){ + Sign = 1; + Coord = next - '0'; + }else if(next != '-'){ + this->error("syntax error"); + } + + while(true){ + next = getc(File); + if(next == EOF){ + this->error("unexpected end of file"); + }else if(isDigit(next)){ + Coord *= 10; + Coord += next - '0'; + }else if(next == ']'){ + break; + }else{ + this->error("syntax error"); + } + } + + this->CoordZ = Sign * Coord; + this->Token = COORDINATE; + return; + } + + case '<':{ + int next = getc(File); + if(next == '='){ + this->Special = 'L'; + }else if(next == '>'){ + this->Special = 'N'; + }else{ + this->Special = '<'; + if(next != EOF){ + ungetc(next, File); + } + } + this->Token = SPECIAL; + return; + } + + case '>':{ + int next = getc(File); + if(next == '='){ + this->Special = 'G'; + }else{ + this->Special = '>'; + if(next != EOF){ + ungetc(next, File); + } + } + this->Token = SPECIAL; + return; + } + + case '-':{ + int next = getc(File); + if(next == '>'){ + this->Special = 'I'; + }else{ + this->Special = '-'; + if(next != EOF){ + ungetc(next, File); + } + } + this->Token = SPECIAL; + return; + } + + default:{ + if(isAlpha(c)){ + int IdentLength = 1; + this->String[0] = (char)c; + while(true){ + int next = getc(File); + if(isAlpha(next) || isDigit(next) || next == '_'){ + if(IdentLength >= 30){ + this->error("identifier too long"); + } + this->String[IdentLength] = (char)next; + IdentLength += 1; + }else{ + if(next != EOF){ + ungetc(next, File); + } + break; + } + } + this->Token = IDENTIFIER; + }else if(isDigit(c)){ + // NOTE(fusion): `this->Bytes` points to `this->String` (set + // in the constructor) which is why we check against the size + // of the `this->String` array. It doesn't seem to store the + // number of bytes but I've only seen it used with fixed size + // arrays like outfit colors or sector offsets. + // Also, it doesn't make sense to have a null terminator in + // a byte string so the capacity check doesn't include it as + // in the case of regular strings. + + // TODO(fusion): We're not checking for integer overflow at all. + int BytesLength = 0; + int Number = c - '0'; + while(true){ + int next = getc(File); + if(isDigit(next)){ + Number *= 10; + Number += next - '0'; + }else if(next == '-'){ + if(BytesLength >= NARRAY(this->String)){ + this->error("too many bytes"); + } + this->Bytes[BytesLength] = (uint8)Number; + BytesLength += 1; + + // NOTE(fusion): If there is a '-' after a number, there + // better be a second number. + next = getc(File); + if(next == EOF){ + this->error("unexpected end of file"); + }else if(!isDigit(next)){ + this->error("syntax error"); + } + Number = next - '0'; + }else{ + if(next != EOF){ + ungetc(next, File); + } + + if(BytesLength <= 0){ + this->Token = NUMBER; + this->Number = Number; + }else{ + if(BytesLength >= NARRAY(this->String)){ + this->error("too many bytes"); + } + this->Token = BYTES; + this->Bytes[BytesLength] = (uint8)Number; + BytesLength += 1; + } + break; + } + } + }else{ + this->Token = SPECIAL; + this->Special = (char)c; + } + return; + } + } + } +} + +char *TReadScriptFile::getIdentifier(void){ + if(this->Token != IDENTIFIER){ + this->error("identifier expected"); + } + strLower(this->String); + return this->String; +} + +int TReadScriptFile::getNumber(void){ + if(this->Token != NUMBER){ + this->error("number expected"); + } + return this->Number; +} + +char *TReadScriptFile::getString(void){ + if(this->Token != STRING){ + this->error("string expected"); + } + return this->String; +} + +uint8 *TReadScriptFile::getBytesequence(void){ + if(this->Token != BYTES){ + this->error("byte-sequence expected"); + } + return this->Bytes; +} + +void TReadScriptFile::getCoordinate(int *x, int *y, int *z){ + if(this->Token != COORDINATE){ + this->error("coordinates expected"); + } + *x = this->CoordX; + *y = this->CoordY; + *z = this->CoordZ; +} + +char TReadScriptFile::getSpecial(void){ + if(this->Token != SPECIAL){ + this->error("special-char expected"); + } + return this->Special; +} + +// TODO(fusion): REMOVE. This is a snippet to test the script lexer. +int main(int argc, char **argv){ + TReadScriptFile Script; + Script.open("local/alexander.npc"); + while(true){ + Script.nextToken(); + if(Script.Token == ENDOFFILE){ + printf("EOF\n"); + Script.close(); + break; + } + + switch(Script.Token){ + case IDENTIFIER: printf("IDENTIFIER \"%s\"\n", Script.String); break; + case NUMBER: printf("NUMBER %d\n", Script.Number); break; + case STRING: printf("STRING \"%s\"\n", Script.String); break; + case BYTES: printf("BYTES %u-%u-%u-%u-%u\n", Script.Bytes[0], Script.Bytes[1], Script.Bytes[2], Script.Bytes[3], Script.Bytes[4]); break; + case COORDINATE: printf("COORD [%d,%d,%d]\n", Script.CoordX, Script.CoordY, Script.CoordZ); break; + case SPECIAL: printf("SPECIAL '%c'\n", Script.Special); break; + default: printf("UNKNOWN TOKEN %d\n", Script.Token); break; + } + } + + return EXIT_FAILURE; +} diff --git a/src/script.hh b/src/script.hh new file mode 100644 index 0000000..2825d6d --- /dev/null +++ b/src/script.hh @@ -0,0 +1,94 @@ +#ifndef TIBIA_SCRIPT_HH_ +#define TIBIA_SCRIPT_HH_ 1 + +#include "main.hh" +#include "enums.hh" + +struct TReadScriptFile { + // REGULAR FUNCTIONS + // ========================================================================= + TReadScriptFile(void); + ~TReadScriptFile(void); + void open(const char *FileName); + void close(void); + void error(const char *Text); + void nextToken(void); + char *getIdentifier(void); + int getNumber(void); + char *getString(void); + uint8 *getBytesequence(void); + void getCoordinate(int *x, int *y, int *z); + char getSpecial(void); + + char *readIdentifier(void){ + this->nextToken(); + return this->getIdentifier(); + } + + int readNumber(void){ + this->nextToken(); + + int Sign = 1; + if(this->Token == SPECIAL && this->Special == '-'){ + Sign = -1; + this->nextToken(); + } + + return Sign * this->getNumber(); + } + + char *readString(void){ + this->nextToken(); + return this->getString(); + } + + uint8 *readBytesequence(void){ + this->nextToken(); + return this->getBytesequence(); + } + + void readCoordinate(int *x, int *y, int *z){ + this->nextToken(); + this->getCoordinate(x, y, z); + } + + char readSpecial(void){ + this->nextToken(); + return this->getSpecial(); + } + + void TReadScriptFile::readSymbol(char Symbol){ + if(this->readSpecial() != Symbol){ + this->error("symbol mismatch"); + } + } + + // DATA + // ========================================================================= + TOKEN Token; + FILE *File[3]; + char Filename[3][4096]; + int Line[3]; + char String[4000]; + int RecursionDepth; + uint8 *Bytes; + int Number; + int CoordX; + int CoordY; + int CoordZ; + char Special; +}; + +struct TWriteScriptFile { + // REGULAR FUNCTIONS + // ========================================================================= + //TWriteScriptFile(void); + + // DATA + // ========================================================================= + FILE *File; + char Filename[4096]; + int Line; +}; + +#endif //TIBIA_SCRIPT_HH_ diff --git a/src/util.cc b/src/util.cc index f2ebc48..4f74c80 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1,4 +1,4 @@ -#include "util.hh" +#include "main.hh" static void (*ErrorFunction)(const char *Text) = NULL; static void (*PrintFunction)(int Level, const char *Text) = NULL; |
