/* * Descent 3 * Copyright (C) 2024 Parallax Software * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . --- HISTORICAL COMMENTS FOLLOW --- * $Logfile: /DescentIII/Main/editor/ScriptCompilerAPI.cpp $ * $Revision: 1.1.1.1 $ * $Date: 2003-08-26 03:57:38 $ * $Author: kevinb $ * * Implementation files for for the script compiler API * * $Log: not supported by cvs2svn $ * * 16 6/17/99 10:24a Kevin * Made things work in a release build * * 15 6/08/99 4:14p Jeff * added some #ifdefs for new editor, so it can be used in the new editor * project * * 14 2/23/99 11:52p Jeff * new script dll sync dialog * * 13 2/17/99 6:54p Jeff * added check box for auto check in/out scripts with levels...fix * checksum mprintf bug * * 12 2/17/99 3:24a Jeff * added checksum parameter * * 11 1/15/99 5:27p Jeff * possible long filename fix * * 10 1/13/99 5:00p Jeff * auto generate string table stuff * * 9 1/05/99 4:33p Jeff * oops, forgot STDCALL when making SaveRestoreState * * 8 12/31/98 2:37p Jeff * added SaveRestoreState import function * * 7 12/21/98 6:21p Jeff * added comments to generated blank scripts * * 6 12/18/98 4:32p Jeff * fixed code that creates empty script * * 5 12/18/98 2:37p Jeff * updated the create new script file * * 4 12/14/98 11:32a Jeff * started work on osiris load and bind functions * * 3 12/13/98 7:50p Jeff * automatically add new DLLs that aren't in the manage * system...implemented create new script function, even though it's still * in infancy. * * 2 12/13/98 3:09a Jeff * Initial creation, added ScriptCompile() function * * $NoKeywords: $ */ #include "stdafx.h" #include "ScriptCompilerAPI.h" #include "appdatabase.h" #include "descent.h" #include "cfile.h" #include "manage.h" #include "pserror.h" #include "mem.h" #include "ddio.h" #include "osiris_dll.h" #include #ifdef NEWEDITOR #include "../neweditor/globals.h" #endif int ScriptCompile(tCompilerInfo *ci) { char Compiler_path[_MAX_PATH]; int Warning_level = -1; int Debug_type = -1; int len = _MAX_PATH; if (!cfexist(ci->source_filename)) return CERR_SOURCENOEXIST; #ifndef NEWEDITOR // make sure there is a compiler defined if (Database->read("EditorCompiler", Compiler_path, &len)) { if (!cfexist(Compiler_path)) { OutrageMessageBox( "The configured virtual compiler (%s)\ncannot be found. Please make sure\nyou have specified a proper path " "and\nthe file exists. Go into the Script and\nLevel dialog to configure your compiler.", Compiler_path); return CERR_COMPILERMISSING; } } else { OutrageMessageBox( "You haven't configured a virtual compiler to use.\nPlease go into the Script and Level Interface\nof the " "editor and select the configure button\nto configure a virtual compiler to use.\nFind Jeff for help on this."); return CERR_NOCOMPILERDEFINED; } Database->read_int("EditorVCWarningLevel", &Warning_level); Database->read_int("EditorVCDebugLevel", &Debug_type); #else // New Editor setup // Determine the compiler CString cs_Compiler_Path = AfxGetApp()->GetProfileString("settings", "EditorCompiler", ""); if (cs_Compiler_Path.GetLength() > 1) { if (!cfexist(cs_Compiler_Path.GetBuffer(0))) { OutrageMessageBox( "The configured virtual compiler (%s)\ncannot be found. Please make sure\nyou have specified a proper path " "and\nthe file exists. Select the configure button\nto configure your virtual compiler.", cs_Compiler_Path.GetBuffer(0)); return CERR_COMPILERMISSING; } else { // valid compiler strcpy(Compiler_path, cs_Compiler_Path.GetBuffer(0)); } } else { OutrageMessageBox("You haven't configured a virtual compiler to use.\nPlease select the configure option\nto " "configure a virtual compiler to use."); return CERR_NOCOMPILERDEFINED; } // Determine the Warning level Warning_level = AfxGetApp()->GetProfileInt("settings", "EditorVCWarningLevel", 3); ASSERT(Warning_level >= 0 && Warning_level <= 4); if (Warning_level < 0 || Warning_level > 4) Warning_level = 3; // Determine the Debug type Debug_type = AfxGetApp()->GetProfileInt("settings", "EditorVCDebugLevel", 2); ASSERT(Debug_type >= 0 && Debug_type <= 2); if (Debug_type < 0 || Debug_type > 2) Debug_type = 2; #endif SECURITY_ATTRIBUTES sa = {0}; STARTUPINFO si = {0}; PROCESS_INFORMATION pi = {0}; HANDLE hPipeOutputRead = NULL; HANDLE hPipeOutputWrite = NULL; HANDLE hPipeInputRead = NULL; HANDLE hPipeInputWrite = NULL; BOOL bTest = 0; DWORD dwNumberOfBytesRead = 0; CHAR szBuffer[256]; sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; // Create pipe for standard output redirection. CreatePipe(&hPipeOutputRead, // read handle &hPipeOutputWrite, // write handle &sa, // security attributes 0 // number of bytes reserved for pipe - 0 default ); // Create pipe for standard input redirection. CreatePipe(&hPipeInputRead, // read handle &hPipeInputWrite, // write handle &sa, // security attributes 0 // number of bytes reserved for pipe - 0 default ); // Make child process use hPipeOutputWrite as standard out, // and make sure it does not show on screen. si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; si.wShowWindow = SW_HIDE; si.hStdInput = hPipeInputRead; si.hStdOutput = hPipeOutputWrite; si.hStdError = hPipeOutputWrite; CString cline; cline = Compiler_path; cline += " -f "; cline += "\""; cline += ci->source_filename; cline += "\""; cline += " -dir "; cline += LocalScriptDir; if (ci->script_type == ST_LEVEL) cline += " -level"; switch (Warning_level) { case 0: cline += " -w 0"; break; case 1: cline += " -w 1"; break; case 2: cline += " -w 2"; break; case 3: cline += " -w 3"; break; case 4: cline += " -w 4"; break; default: cline += " -w 3"; break; } switch (Debug_type) { case 1: cline += " -d coff"; break; case 2: cline += " -d c7"; break; } // Game structure checksum #ifndef NEWEDITOR char temp_c[128]; sprintf(temp_c, " -checksum %lu", Osiris_game_checksum); cline += temp_c; #endif CreateProcess(NULL, cline.GetBuffer(0), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); // Now that handles have been inherited, close it to be safe. // You don't want to read or write to them accidentally. CloseHandle(hPipeOutputWrite); CloseHandle(hPipeInputRead); // Now test to capture DOS application output by reading // hPipeOutputRead. Could also write to DOS application // standard input by writing to hPipeInputWrite. CString temp; while (TRUE) { bTest = ReadFile(hPipeOutputRead, // handle of the read end of our pipe &szBuffer, // address of buffer that receives data 256, // number of bytes to read &dwNumberOfBytesRead, // address of number of bytes read NULL // non-overlapped. ); if (!bTest) { break; } // do something with data. szBuffer[dwNumberOfBytesRead] = 0; // null terminate if (ci->callback) { int new_line_count = 0; char *ptr = szBuffer; while (*ptr) { if (*ptr == '\n') new_line_count++; ptr++; } char *data, *d_ptr; d_ptr = data = (char *)mem_malloc(dwNumberOfBytesRead + 1 + new_line_count); if (data) { ptr = (char *)szBuffer; while (*ptr) { if (*ptr == '\n') { *d_ptr = '\r'; d_ptr++; *d_ptr = '\n'; d_ptr++; } else if (*ptr != '\r') { *d_ptr = *ptr; d_ptr++; } ptr++; } *d_ptr = '\0'; (*ci->callback)(data); mem_free(data); } } } // Wait for spawn to finish. WaitForSingleObject(pi.hProcess, INFINITE); // Close all remaining handles CloseHandle(pi.hProcess); CloseHandle(hPipeOutputRead); CloseHandle(hPipeInputWrite); return CERR_NOERR; } bool ScriptCreateEmptyLevelScript(char *filename); bool ScriptCreateEmptyGameScript(char *filename); bool ScriptCreateEmptyScript(char *filename, uint8_t script_type) { if (cfexist(filename)) { char buffer[512]; sprintf(buffer, "A script by the name of %s\nalready exists.", filename); AfxMessageBox(buffer, MB_OK); return false; } switch (script_type) { case ST_GAME: return ScriptCreateEmptyGameScript(filename); break; case ST_LEVEL: return ScriptCreateEmptyLevelScript(filename); break; } return false; } #define VERSION "0.1" #define O(x) outputtofile x CFILE *CurrentFile; void outputtofile(char *format, ...) { char buffer[1024]; va_list marker; va_start(marker, format); std::vsprintf(buffer, format, marker); cf_WriteString(CurrentFile, buffer); va_end(marker); } bool ScriptCreateEmptyLevelScript(char *filename) { char fullpath[_MAX_PATH]; ddio_MakePath(fullpath, LocalScriptDir, filename, NULL); CurrentFile = cfopen(fullpath, "wt"); if (!CurrentFile) return false; O(("// %s", filename)); O(("// %s", VERSION)); O(("#include ")); O(("#include ")); O(("#include ")); O(("#include \"osiris_import.h\"")); O(("#include \"osiris_common.h\"")); O(("")); O(("#ifdef _MSC_VER //Visual C++ Build")); O(("#define STDCALL __stdcall")); O(("#define STDCALLPTR *STDCALL")); O(("#else //Non-Visual C++ Build")); O(("#define STDCALL __attribute__((stdcall))")); O(("#define STDCALLPTR STDCALL*")); O(("#endif")); O(("")); O(("#ifdef __cplusplus")); O(("extern \"C\"{")); O(("#endif")); O(("char STDCALL InitializeDLL(tOSIRISModuleInit *func_list);")); O(("void STDCALL ShutdownDLL(void);")); O(("int STDCALL GetGOScriptID(const char *name,uint8_t is_door);")); O(("void STDCALLPTR CreateInstance(int id);")); O(("void STDCALL DestroyInstance(int id,void *ptr);")); O(("int16_t STDCALL CallInstanceEvent(int id,void *ptr,int event,tOSIRISEventInfo *data);")); O(("int STDCALL GetTriggerScriptID(int trigger_room, int trigger_face );")); O(("int STDCALL GetCOScriptList( int **list, int **id_list );")); O(("int STDCALL SaveRestoreState( void *file_ptr, uint8_t saving_state );")); O(("#ifdef __cplusplus")); O(("}")); O(("#endif")); O(("")); O(("int String_table_size = 0;")); O(("char **String_table = NULL;")); O(("static const char *_Error_string = \"!!ERROR MISSING STRING!!\";")); O(("static const char *_Empty_string = \"\";")); O(("const char *GetStringFromTable(int index)")); O(("{")); O((" if( (index<0) || (index>=String_table_size) )")); O((" return _Error_string;")); O((" if(!String_table[index])")); O((" return _Empty_string;")); O((" return String_table[index];")); O(("}")); O(("#define TXT(x) GetStringFromTable(x)")); O(("// InitializeDLL")); O(("// Purpose:")); O(("// This function gets called when the DLL first gets loaded. It will only be called once (until " "the")); O(("// DLL is unloaded). Passed in is a struct of data passed from the game needed for the DLL to interact")); O(("// with D3. Usually this function will just call osicommon_Initialize(), which sets up the imported")); O(("// functions. However, you can alloc some memory or whatever in this function, and free it in " "ShutdownDLL().")); O(("// Note: You cannot call any imported functions until osicommon_Initialize() is called.")); O(("// Returns 1 if initialization went ok, 0 if there was an error and the DLL should not be loaded.")); O(("char STDCALL InitializeDLL(tOSIRISModuleInit *func_list)")); O(("{")); O((" osicommon_Initialize(func_list);")); O((" String_table_size = func_list->string_count;")); O((" String_table = func_list->string_table;")); O((" if(func_list->game_checksum!=CHECKSUM)")); O((" { ")); O((" mprintf(0,\"Game-Checksum FAIL!!! (%%ul!=%%ul)\\n\",func_list->game_checksum,CHECKSUM);")); O((" mprintf(0,\"RECOMPILE YOUR SCRIPTS!!!\\n\");")); O((" return 0;")); O((" }")); O((" return 1;")); O(("}")); O(("")); O(("// ShutdownDLL")); O(("// Purpose:")); O(("// This function is called right before a DLL is about to be unloaded from memory. You can " "free")); O(("// any unfree'd memory, or anything else you need to do. Don't worry about destroying any instances")); O(("// of scripts, as they will all be automatically destroyed before this function is called. The")); O(("// same goes for any memory allocated with Scrpt_MemAlloc(), as this will automatically be freed")); O(("// when a scripts instance is destroyed.")); O(("void STDCALL ShutdownDLL(void)")); O(("{")); O(("}")); O(("")); O(("// GetGOScriptID")); O(("// Purpose:")); O(("// Given the name of the object (from its pagename), this function will search through its")); O(("// list of General Object Scripts for a script with a matching name (to see if there is a script")); O(("// for that type/id of object within this DLL). If a matching scriptname is found, a UNIQUE ID")); O(("// is to be returned back to Descent 3. This ID will be used from here on out for all future")); O(("// interaction with the DLL. Since doors are not part of the generic object's, it's possible")); O(("// for a door to have the same name as a generic object (OBJ_POWERUP, OBJ_BUILDING, OBJ_CLUTTER")); O(("// or OBJ_ROBOT), therefore, a 1 is passed in for isdoor if the given object name refers to a")); O(("// door, else it is a 0. The return value is the unique identifier, else -1 if the script")); O(("// does not exist in the DLL.")); O(("// The only reserved ID is 0, which must be used for the level script")); O(("int STDCALL GetGOScriptID(const char *name,uint8_t isdoor)")); O(("{")); O((" return -1;")); O(("}")); O(("")); O(("// CreateInstance")); O(("// Purpose:")); O(("// Given an ID from a call to GetGOScriptID(), GetTriggerScriptID() or GetCOScriptList(), this")); O(("// function will create a new instance for that particular script (by allocating and initializing")); O(("// memory, etc.). A pointer to this instance is to be returned back to Descent 3. This pointer will")); O(("// be passed around, along with the ID for CallInstanceEvent() and DestroyInstance(). Return NULL")); O(("// if there was an error.")); O(("// The only reserved ID is 0, which must be used for the level script")); O(("void STDCALLPTR CreateInstance(int id)")); O(("{")); O((" return NULL;")); O(("}")); O(("")); O(("// DestroyInstance")); O(("// Purpose:")); O(("// Given an ID, and a pointer to a particular instance of a script, this function will delete " "and")); O(("// destruct all information associated with that script, so it will no longer exist.")); O(("void STDCALL DestroyInstance(int id,void *ptr)")); O(("{")); O(("}")); O(("")); O(("// CallInstanceEvent")); O(("// Purpose:")); O(("// Given an ID, a pointer to a script instance, an event and a pointer to the struct of")); O(("// information about the event, this function will translate who this event belongs to and")); O(("// passes the event to that instance of the script to be handled. Return a combination of")); O(("// CONTINUE_CHAIN and CONTINUE_DEFAULT, to give instructions on what to do based on the")); O(("// event. CONTINUE_CHAIN means to continue through the chain of scripts (custom script, level")); O(("// script, mission script, and finally default script). If CONTINUE_CHAIN is not specified,")); O(("// than the chain is broken and those scripts of lower priority will never get the event. Return")); O(("// CONTINUE_DEFAULT in order to tell D3 if you want process the normal action that is built into")); O(("// the game for that event. This only pertains to certain events. If the chain continues")); O(("// after this script, than the CONTINUE_DEFAULT setting will be overridden by lower priority")); O(("// scripts return value.")); O(("// The only reserved ID is 0, which must be used for the level script")); O(("int16_t STDCALL CallInstanceEvent(int id,void *ptr,int event,tOSIRISEventInfo *data)")); O(("{")); O((" return CONTINUE_CHAIN|CONTINUE_DEFAULT;")); O(("}")); O(("")); O(("// GetTriggerScriptID")); O(("// Purpose:")); O(("// Given a room and face number, this function will return a unique ID (global DLL unique)")); O(("// in which Descent 3 should use for all future interaction will the DLL when refering to")); O(("// this trigger. Return -1 if there is no trigger script available for the specified trigger.")); O(("// The only reserved ID is 0, which must be used for the level script")); O(("int STDCALL GetTriggerScriptID(int trigger_room,int trigger_face)")); O(("{")); O((" return -1;")); O(("}")); O(("")); O(("// GetCOScriptList")); O(("// Purpose:")); O(("// This function returns the pointers to the 2 arrays that Descent 3 should use to determine")); O(("// what object's have custom scripts in this level DLL. list should be set to point to the array")); O(("// of object handles that have custom scripts in this DLL. id_list should be set to point to the")); O(("// corresponding array of unique IDs that match with the object handle list. This function should")); O(("// return the size of the arrays returned...if no custom scripts are available, than this function")); O(("// returns 0.")); O(("// The only reserved ID is 0, which must be used for the level script")); O(("int STDCALL GetCOScriptList( int **list, int **id_list )")); O(("{")); O((" return 0;")); O(("}")); O(("// SaveRestoreState")); O(("// Purpose:")); O(("// This function is called when Descent 3 is saving or restoring the game state. In this " "function")); O(("// you should save/restore any global data that you want preserved through load/save (which includes")); O(("// demos). You must be very careful with this function, corrupting the file (reading or writing too")); O(("// much or too little) may be hazardous to the game (possibly making it impossible to restore the")); O(("// state). It would be best to use version information to keep older versions of saved states still")); O(("// able to be used. IT IS VERY IMPORTANT WHEN SAVING THE STATE TO RETURN THE NUMBER OF _BYTES_ WROTE")); O(("// TO THE FILE. When restoring the data, the return value is ignored. saving_state is 1 when you " "should")); O(("// write data to the file_ptr, 0 when you should read in the data.")); O(("int STDCALL SaveRestoreState( void *file_ptr, uint8_t saving_state )")); O(("{")); O((" return 0;")); O(("}")); O(("")); cfclose(CurrentFile); return true; } bool ScriptCreateEmptyGameScript(char *filename) { char fullpath[_MAX_PATH]; ddio_MakePath(fullpath, LocalScriptDir, filename, NULL); CurrentFile = cfopen(fullpath, "wt"); if (!CurrentFile) return false; O(("// %s", filename)); O(("// %s", VERSION)); O(("#include ")); O(("#include ")); O(("#include ")); O(("#include \"osiris_import.h\"")); O(("#include \"osiris_common.h\"")); O(("")); O(("#ifdef _MSC_VER //Visual C++ Build")); O(("#define STDCALL __stdcall")); O(("#define STDCALLPTR *STDCALL")); O(("#else //Non-Visual C++ Build")); O(("#define STDCALL __attribute__((stdcall))")); O(("#define STDCALLPTR STDCALL*")); O(("#endif")); O(("")); O(("#ifdef __cplusplus")); O(("extern \"C\"{")); O(("#endif")); O(("char STDCALL InitializeDLL(tOSIRISModuleInit *func_list);")); O(("void STDCALL ShutdownDLL(void);")); O(("int STDCALL GetGOScriptID(const char *name,uint8_t isdoor);")); O(("void STDCALLPTR CreateInstance(int id);")); O(("void STDCALL DestroyInstance(int id,void *ptr);")); O(("int16_t STDCALL CallInstanceEvent(int id,void *ptr,int event,tOSIRISEventInfo *data);")); O(("int STDCALL SaveRestoreState( void *file_ptr, uint8_t saving_state );")); O(("#ifdef __cplusplus")); O(("}")); O(("#endif")); O(("")); O(("int String_table_size = 0;")); O(("char **String_table = NULL;")); O(("static char *_Error_string = \"!!ERROR MISSING STRING!!\";")); O(("static char *_Empty_string = \"\";")); O(("const char *GetStringFromTable(int index)")); O(("{")); O((" if( (index<0) || (index>=String_table_size) )")); O((" return _Error_string;")); O((" if(!String_table[index])")); O((" return _Empty_string;")); O((" return String_table[index];")); O(("}")); O(("#define TXT(x) GetStringFromTable(x)")); O(("// InitializeDLL")); O(("// Purpose:")); O(("// This function gets called when the DLL first gets loaded. It will only be called once (until " "the")); O(("// DLL is unloaded). Passed in is a struct of data passed from the game needed for the DLL to interact")); O(("// with D3. Usually this function will just call osicommon_Initialize(), which sets up the imported")); O(("// functions. However, you can alloc some memory or whatever in this function, and free it in " "ShutdownDLL().")); O(("// Note: You cannot call any imported functions until osicommon_Initialize() is called.")); O(("// Returns 1 if initialization went ok, 0 if there was an error and the DLL should not be loaded.")); O(("char STDCALL InitializeDLL(tOSIRISModuleInit *func_list)")); O(("{")); O((" osicommon_Initialize((tOSIRISModuleInit *)func_list);")); O((" String_table_size = func_list->string_count;")); O((" String_table = func_list->string_table;")); O((" if(func_list->game_checksum!=CHECKSUM)")); O((" { ")); O((" mprintf(0,\"Game-Checksum FAIL!!! (%%ul!=%%ul)\\n\",func_list->game_checksum,CHECKSUM);")); O((" mprintf(0,\"RECOMPILE YOUR SCRIPTS!!!\\n\");")); O((" return 0;")); O((" }")); O((" return 1;")); O(("}")); O(("")); O(("// ShutdownDLL")); O(("// Purpose:")); O(("// This function is called right before a DLL is about to be unloaded from memory. You can " "free")); O(("// any unfree'd memory, or anything else you need to do. Don't worry about destroying any instances")); O(("// of scripts, as they will all be automatically destroyed before this function is called. The")); O(("// same goes for any memory allocated with Scrpt_MemAlloc(), as this will automatically be freed")); O(("// when a scripts instance is destroyed.")); O(("void STDCALL ShutdownDLL(void)")); O(("{")); O(("}")); O(("")); O(("// GetGOScriptID")); O(("// Purpose:")); O(("// Given the name of the object (from its pagename), this function will search through its")); O(("// list of General Object Scripts for a script with a matching name (to see if there is a script")); O(("// for that type/id of object within this DLL). If a matching scriptname is found, a UNIQUE ID")); O(("// is to be returned back to Descent 3. This ID will be used from here on out for all future")); O(("// interaction with the DLL. Since doors are not part of the generic object's, it's possible")); O(("// for a door to have the same name as a generic object (OBJ_POWERUP, OBJ_BUILDING, OBJ_CLUTTER")); O(("// or OBJ_ROBOT), therefore, a 1 is passed in for isdoor if the given object name refers to a")); O(("// door, else it is a 0. The return value is the unique identifier, else -1 if the script")); O(("// does not exist in the DLL.")); O(("int STDCALL GetGOScriptID(const char *name,uint8_t isdoor)")); O(("{")); O((" return -1;")); O(("}")); O(("")); O(("// CreateInstance")); O(("// Purpose:")); O(("// Given an ID from a call to GetGOScriptID(), this function will create a new instance for " "that")); O(("// particular script (by allocating and initializing memory, etc.). A pointer to this instance")); O(("// is to be returned back to Descent 3. This pointer will be passed around, along with the ID")); O(("// for CallInstanceEvent() and DestroyInstance(). Return NULL if there was an error.")); O(("void STDCALLPTR CreateInstance(int id)")); O(("{")); O((" return NULL;")); O(("}")); O(("")); O(("// DestroyInstance")); O(("// Purpose:")); O(("// Given an ID, and a pointer to a particular instance of a script, this function will delete " "and")); O(("// destruct all information associated with that script, so it will no longer exist.")); O(("void STDCALL DestroyInstance(int id,void *ptr)")); O(("{")); O(("}")); O(("")); O(("// CallInstanceEvent")); O(("// Purpose:")); O(("// Given an ID, a pointer to a script instance, an event and a pointer to the struct of")); O(("// information about the event, this function will translate who this event belongs to and")); O(("// passes the event to that instance of the script to be handled. Return a combination of")); O(("// CONTINUE_CHAIN and CONTINUE_DEFAULT, to give instructions on what to do based on the")); O(("// event. CONTINUE_CHAIN means to continue through the chain of scripts (custom script, level")); O(("// script, mission script, and finally default script). If CONTINUE_CHAIN is not specified,")); O(("// than the chain is broken and those scripts of lower priority will never get the event. Return")); O(("// CONTINUE_DEFAULT in order to tell D3 if you want process the normal action that is built into")); O(("// the game for that event. This only pertains to certain events. If the chain continues")); O(("// after this script, than the CONTINUE_DEFAULT setting will be overridden by lower priority")); O(("// scripts return value.")); O(("int16_t STDCALL CallInstanceEvent(int id,void *ptr,int event,tOSIRISEventInfo *data)")); O(("{")); O((" return CONTINUE_CHAIN|CONTINUE_DEFAULT;")); O(("}")); O(("")); O(("// SaveRestoreState")); O(("// Purpose:")); O(("// This function is called when Descent 3 is saving or restoring the game state. In this " "function")); O(("// you should save/restore any global data that you want preserved through load/save (which includes")); O(("// demos). You must be very careful with this function, corrupting the file (reading or writing too")); O(("// much or too little) may be hazardous to the game (possibly making it impossible to restore the")); O(("// state). It would be best to use version information to keep older versions of saved states still")); O(("// able to be used. IT IS VERY IMPORTANT WHEN SAVING THE STATE TO RETURN THE NUMBER OF _BYTES_ WROTE")); O(("// TO THE FILE. When restoring the data, the return value is ignored. saving_state is 1 when you " "should")); O(("// write data to the file_ptr, 0 when you should read in the data.")); O(("int STDCALL SaveRestoreState( void *file_ptr, uint8_t saving_state )")); O(("{")); O((" return 0;")); O(("}")); O(("")); cfclose(CurrentFile); return true; }