Sun, 15 Jul 2012 18:56:26 +0300
Mainloop and onenter definitions are now written into separate buffers first and only written to file after state ends. Why? Zandronum seems to demand that mainloop definitions MUST be written right after any onenter one. This way, mainloop and onenter definitions can be written without this restriction in the script. Also now I have a cool uchar-buffer class :)
common.h | file | annotate | diff | comparison | revisions | |
objwriter.cxx | file | annotate | diff | comparison | revisions | |
objwriter.h | file | annotate | diff | comparison | revisions | |
parser.cxx | file | annotate | diff | comparison | revisions | |
scriptreader.h | file | annotate | diff | comparison | revisions |
--- a/common.h Sun Jul 15 03:31:35 2012 +0300 +++ b/common.h Sun Jul 15 18:56:26 2012 +0300 @@ -41,7 +41,9 @@ #ifndef __COMMON_H__ #define __COMMON_H__ +#include <stdio.h> #include <stdarg.h> +#include <typeinfo> #include "bots.h" #include "str.h" @@ -50,7 +52,16 @@ #define VERSION_MINOR 0 #define VERSION_REVISION 999 -typedef unsigned long qbyte; +// Where is the parser at? +enum parsermode { + MODE_TOPLEVEL, // at top level + MODE_EVENT, // inside event definition + MODE_MAINLOOP, // inside mainloop + MODE_ONENTER, // inside onenter + MODE_ONEXIT, // inside onexit +}; + +typedef long qbyte; #define CHECK_FILE(pointer,path,action) \ if (!pointer) { \ @@ -75,4 +86,36 @@ extern str g_CurState; #endif +// Power function +template<class T> T pow (T a, T b) { + if (!b) + return 1; + + T r = a; + while (b > 1) { + b--; + + // r *= a fails here with large numbers + r += a * a; + } + + return r; +} + +template <class T> unsigned char CharByte (T a, unsigned int b) { + if (b >= sizeof (T)) + error ("CharByte: tried to get byte %u out of a %u-byte %s\n", + b, sizeof (T), typeid(T).name()); + + unsigned long p1 = pow<unsigned long> (256, b); + unsigned long p2 = pow<unsigned long> (256, b+1); + unsigned long r = (a % p2) / p1; + + if (r > 256) + error ("result %lu too big!", r); + + unsigned char ur = static_cast<unsigned char> (r); + return ur; +} + #endif // __COMMON_H__ \ No newline at end of file
--- a/objwriter.cxx Sun Jul 15 03:31:35 2012 +0300 +++ b/objwriter.cxx Sun Jul 15 18:56:26 2012 +0300 @@ -48,7 +48,12 @@ #include "bots.h" +extern bool g_GotMainLoop; + ObjWriter::ObjWriter (str path) { + MainLoopBuffer = new DataBuffer; + OnEnterBuffer = new DataBuffer; + numWrittenBytes = 0; fp = fopen (path, "w"); CHECK_FILE (fp, path, "writing"); @@ -56,6 +61,8 @@ ObjWriter::~ObjWriter () { fclose (fp); + delete MainLoopBuffer; + delete OnEnterBuffer; } void ObjWriter::WriteString (char* s) { @@ -70,4 +77,28 @@ void ObjWriter::WriteString (str s) { WriteString (s.chars()); +} + +void ObjWriter::WriteBuffers () { + // If there was no mainloop defined, write a dummy one now. + if (!g_GotMainLoop) { + MainLoopBuffer->Write<long> (DH_MAINLOOP); + MainLoopBuffer->Write<long> (DH_ENDMAINLOOP); + } + + // Write the onenter and mainloop buffers, IN THAT ORDER + for (int i = 0; i < 2; i++) { + DataBuffer* buf = (!i) ? OnEnterBuffer : MainLoopBuffer; + for (unsigned int x = 0; x < buf->writepos; x++) { + unsigned char c = *(buf->buffer+x); + Write<unsigned char> (c); + } + + // Clear the buffer afterwards for potential next state + delete buf; + buf = new DataBuffer; + } + + // Next state definitely has no mainloop yet + g_GotMainLoop = false; } \ No newline at end of file
--- a/objwriter.h Sun Jul 15 03:31:35 2012 +0300 +++ b/objwriter.h Sun Jul 15 18:56:26 2012 +0300 @@ -42,14 +42,88 @@ #define __OBJWRITER_H__ #include <stdio.h> +#include <typeinfo> +#include <string.h> #include "common.h" #include "str.h" +extern int g_CurMode; + +// ============================================================================ +// DataBuffer: A dynamic data buffer. +class DataBuffer { +public: + // The actual buffer + unsigned char* buffer; + + // How big is the buffer? + unsigned int bufsize; + + // Position in the buffer + unsigned int writepos; + unsigned int readpos; + + // METHODS + DataBuffer (unsigned int size=128) { + writepos = 0; + readpos = 0; + + buffer = new unsigned char[size]; + bufsize = size; + } + + ~DataBuffer () { + delete buffer; + } + + template<class T> void Write(T stuff) { + if (sizeof (char) != 1) { + error ("DataBuffer: sizeof(char) must be 1!\n"); + } + + // Out of space, must resize + if (writepos + sizeof(T) >= bufsize) { + // First, store the old buffer temporarily + char* copy = new char[bufsize]; + printf ("Resizing buffer: copy buffer to safety. %u bytes to copy\n", bufsize); + memcpy (copy, buffer, bufsize); + + // Remake the buffer with the new size. + // Have a bit of leeway so we don't have to + // resize immediately again. + size_t newsize = bufsize + sizeof (T) + 128; + delete buffer; + buffer = new unsigned char[newsize]; + bufsize = newsize; + + // Now, copy the new stuff over. + memcpy (buffer, copy, bufsize); + + // Nuke the copy now as it's no longer needed + delete copy; + } + + // Write the new stuff one byte at a time + for (unsigned int x = 0; x < sizeof (T); x++) { + buffer[writepos] = CharByte<T> (stuff, x); + writepos++; + } + } + + template<class T> T Read() { + T result = buffer[readpos]; + readpos += sizeof (T); + return result; + } +}; + class ObjWriter { public: // ==================================================================== // MEMBERS FILE* fp; + DataBuffer* OnEnterBuffer; + DataBuffer* MainLoopBuffer; unsigned int numWrittenBytes; // ==================================================================== @@ -59,12 +133,19 @@ void WriteString (char* s); void WriteString (const char* s); void WriteString (str s); + void WriteBuffers (); template <class T> void Write (T stuff) { + // Mainloop and onenter are written into a separate buffer. + if (g_CurMode == MODE_MAINLOOP || g_CurMode == MODE_ONENTER) { + DataBuffer* buffer = (g_CurMode == MODE_MAINLOOP) ? MainLoopBuffer : OnEnterBuffer; + buffer->Write<T> (stuff); + return; + } + fwrite (&stuff, sizeof (T), 1, fp); numWrittenBytes += sizeof (T); } - // Cannot use default arguments in function templates.. void Write (qbyte stuff) {Write<qbyte> (stuff);} };
--- a/parser.cxx Sun Jul 15 03:31:35 2012 +0300 +++ b/parser.cxx Sun Jul 15 18:56:26 2012 +0300 @@ -59,9 +59,9 @@ int g_CurMode = MODE_TOPLEVEL; str g_CurState = ""; bool g_stateSpawnDefined = false; +bool g_GotMainLoop = false; void ScriptReader::BeginParse (ObjWriter* w) { - bool gotMainLoop = false; while (Next()) { // printf ("got token %s\n", token.chars()); if (!token.icompare ("#include")) { @@ -95,12 +95,10 @@ // Must end in a colon MustNext (":"); - // If the previous state did not define a mainloop, - // define a dummy one now, since one has to be present. - if (g_CurState.len() && !gotMainLoop) { - w->Write (DH_MAINLOOP); - w->Write (DH_ENDMAINLOOP); - } + // Write the previous state's onenter and + // mainloop buffers to file now + if (g_CurState.len()) + w->WriteBuffers(); w->Write (DH_STATENAME); w->WriteString (statename); @@ -109,7 +107,7 @@ g_NumStates++; g_CurState = token; - gotMainLoop = false; + g_GotMainLoop = false; continue; } @@ -136,17 +134,20 @@ if (!token.icompare ("mainloop")) { MUST_TOPLEVEL MustNext ("{"); + + // Mode must be set before dataheader is written here! g_CurMode = MODE_MAINLOOP; w->Write (DH_MAINLOOP); - gotMainLoop = true; continue; } if (!token.icompare ("onenter") || !token.icompare ("onexit")) { MUST_TOPLEVEL bool onenter = !token.compare ("onenter"); + MustNext ("{"); - MustNext ("{"); + // Mode must be set before dataheader is written here, + // because onenter goes to a separate buffer. g_CurMode = onenter ? MODE_ONENTER : MODE_ONEXIT; w->Write (onenter ? DH_ONENTER : DH_ONEXIT); continue; @@ -184,7 +185,9 @@ if (dataheader == -1) ParserError ("unexpected `}`"); - // Closing brace.. + // Data header must be written before mode is changed because + // onenter and mainloop go into buffers, and we want the closing + // data headers into said buffers too. w->Write (dataheader); g_CurMode = MODE_TOPLEVEL; @@ -198,11 +201,9 @@ if (g_CurMode == MODE_TOPLEVEL) ParserError ("can't alter variables at top level"); - // Only addition for now.. - MustNext (); - // Build operator string. Only '=' is one // character, others are two. + MustNext (); str oper = token; if (token.compare ("=") != 0) { MustNext (); @@ -261,11 +262,10 @@ if (!g_stateSpawnDefined) ParserError ("script must have a state named `stateSpawn`!"); - // If the last state did not have a main loop, define a dummy one now. - if (!gotMainLoop) { - w->Write (DH_MAINLOOP); - w->Write (DH_ENDMAINLOOP); - } + + + // Dump the last state's onenter and mainloop + w->WriteBuffers(); // If we added strings here, we need to write a list of them. unsigned int stringcount = CountStringTable ();
--- a/scriptreader.h Sun Jul 15 03:31:35 2012 +0300 +++ b/scriptreader.h Sun Jul 15 18:56:26 2012 +0300 @@ -46,15 +46,6 @@ #include "objwriter.h" #include "commands.h" -// Where is the parser at? -enum parsermode { - MODE_TOPLEVEL, // at top level - MODE_EVENT, // inside event definition - MODE_MAINLOOP, // inside mainloop - MODE_ONENTER, // inside onenter - MODE_ONEXIT, // inside onexit -}; - class ScriptReader { public: // ====================================================================