diff --git a/source/lib/time.cpp b/source/lib/time.cpp index 1c3ab35098..4c9edcd624 100755 --- a/source/lib/time.cpp +++ b/source/lib/time.cpp @@ -19,6 +19,8 @@ #include #include +#include "win.h" + #include "ia32.h" #include "posix.h" #include "detect.h" @@ -26,10 +28,6 @@ #include "types.h" #include "misc.h" -#ifdef _WIN32 -#include "win.h" -#endif - // high resolution (> 1 µs) timestamp [s], starting at or near 0 s. diff --git a/source/lib/unzip.cpp b/source/lib/unzip.cpp index 0986b26548..ce8f9fbb4e 100755 --- a/source/lib/unzip.cpp +++ b/source/lib/unzip.cpp @@ -221,6 +221,29 @@ Handle zopen(Handle hz, const char* fn) return hf; } +int zaccess(Handle hz, const char* fn) +{ + HDATA* hzd = h_data(hz, H_ZFILE); + if(!hzd) + return -1; + ZARCHIVE* za = (ZARCHIVE*)hzd->user; + + // find its File descriptor + const u32 fn_hash = fnv_hash(fn, strlen(fn)); + uint i = za->last_file+1; + if(i >= za->num_files || za->fn_hashs[i] != fn_hash) + { + for(i = 0; i < za->num_files; i++) + { + if(za->fn_hashs[i] == fn_hash) + break; + } + if(i == za->num_files) + return -1; + } + za->last_file = i; + return( 0 ); +} int zread(Handle hf, void*& p, size_t& size, size_t ofs) { diff --git a/source/lib/unzip.h b/source/lib/unzip.h index 0aa916d5f4..04d543078d 100755 --- a/source/lib/unzip.h +++ b/source/lib/unzip.h @@ -28,6 +28,8 @@ extern Handle zopen(const char* fn); // open and return a handle to file in the zip archive extern Handle zopen(Handle hz, const char* fn); +extern int zaccess( Handle hz, const char* fn ); + extern void zclose(Handle hz); extern int zread(Handle hf, void*& p, size_t& size, size_t ofs); diff --git a/source/lib/vfs.cpp b/source/lib/vfs.cpp index ce8a899365..94e7b75e86 100755 --- a/source/lib/vfs.cpp +++ b/source/lib/vfs.cpp @@ -13,7 +13,6 @@ #include "vfs.h" #include "mem.h" - // We need to be able to unmount specific paths (e.g. when switching mods). // Don't want to remount everything (slow), or specify a mod tag when mounting // (not this module's job). Instead, we include all archives in one path entry; @@ -67,7 +66,7 @@ int vfs_set_root(const char* argv0, const char* root) return -1; *fn = 0; - chdir(path); + chdir( fn + 1 ); chdir(root); return vfs_mount("."); @@ -181,11 +180,111 @@ Handle vfs_map(const char* fn) return 0; } +int vfs_access( char* fn ) +{ + // Alters 'fn' to the path of the file, if we find it... + + // Mostly identical to vfs_stat, below. + struct stat dy; + + // Strim out the path, if provided. + char* name = strrchr( fn, '/' ); + if( name ) { name++; } else name = fn; + + char buf[PATH_MAX+1]; buf[PATH_MAX] = 0; + + // for each search path: + for(PATH* entry = path_list; entry; entry = entry->next) + { + // dir + const char* path = name; + if(entry->dir[0] != '.' || entry->dir[1] != '\0') + { + // only prepend dir if not "." (root) - "./" isn't portable + snprintf(buf, PATH_MAX, "%s/%s", entry->dir, name); + path = buf; + } + if( !_stat( path, &dy ) ) + { + strcpy( fn, path ); + return( 0 ); + } + + // archive + for(int i = 0; i < entry->num_archives; i++) + { + if( !zaccess(entry->archives[i], name) ) + return( 0 ); + } + + } + + // not found + return -1; +} + +int vfs_stat( const char* fn, struct stat *buffer ) +{ + // Mostly identical to vfs_open, below. + + // Note: vfs_stat redefines the nlink member of stat for its own + // nefarious ends. + + // Check the specific location 'fn' first. + // If the file's there, it's faster. + + if( !_stat( fn, buffer ) ) + { + buffer->st_nlink = 0; + return( 0 ); + } + + // Not there? Drop the old path. + + char* name = strrchr( fn, '/' ); + if( name ) + fn = name + 1; + + char buf[PATH_MAX+1]; buf[PATH_MAX] = 0; + + // for each search path: + for(PATH* entry = path_list; entry; entry = entry->next) + { + // dir + const char* path = fn; + if(entry->dir[0] != '.' || entry->dir[1] != '\0') + { + // only prepend dir if not "." (root) - "./" isn't portable + snprintf(buf, PATH_MAX, "%s/%s", entry->dir, fn); + path = buf; + } + if( !_stat( path, buffer ) ) + { + buffer->st_nlink = 1; + return( 0 ); + } + + // Ignore files in an archive. + } + + // not found + return -1; +} + Handle vfs_open(const char* fn) { char buf[PATH_MAX+1]; buf[PATH_MAX] = 0; + // If a path to an existing file is given, use it. + + Handle h = vfs_map( fn ); + if( h ) + return( h ); + + char* name = strrchr( fn, '/' ); + if( name ) fn = name + 1; + // for each search path: for(PATH* entry = path_list; entry; entry = entry->next) { diff --git a/source/lib/vfs.h b/source/lib/vfs.h index b1173b3e94..84d6f098df 100755 --- a/source/lib/vfs.h +++ b/source/lib/vfs.h @@ -14,6 +14,9 @@ extern int vfs_umount(const char* path); extern Handle vfs_map(const char* fn); // actual path in real FS +extern int vfs_access( char* fn ); +extern int vfs_stat( const char* fn, struct stat *buffer ); + extern Handle vfs_open(const char* fn); extern int vfs_close(Handle h); diff --git a/source/lib/win.h b/source/lib/win.h index 036420d0b1..9cffef8c90 100755 --- a/source/lib/win.h +++ b/source/lib/win.h @@ -1,8 +1,11 @@ +#ifdef _WIN32 + #ifndef __WIN_H__ #define __WIN_H__ // Win32 socket decls aren't portable (e.g. problems with socklen_t) // => skip winsock.h; posix.h should be used instead + #define _WINSOCKAPI_ #define WIN32_LEAN_AND_MEAN @@ -79,4 +82,5 @@ extern __declspec(dllimport) int __stdcall WSAStartup(WORD, char*); #endif } -#endif // #ifndef __WIN_H__ \ No newline at end of file +#endif +#endif \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp index 3e21387877..9a1dfbb1ee 100755 --- a/source/main.cpp +++ b/source/main.cpp @@ -14,6 +14,7 @@ #include "tex.h" #include "vfs.h" #include "ia32.h" +#include "Config.h" #ifndef NO_GUI #include "gui/GUI.h" @@ -28,12 +29,9 @@ bool keys[256]; #include - - static Handle font; static Handle tex; - extern void terr_init(); extern void terr_update(); extern bool terr_handler(const SDL_Event& ev); @@ -167,9 +165,14 @@ glColor3f(1.0f, 1.0f, 1.0f); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); +#ifndef NO_GUI ////// janwas: I changed to some more for the GUI, we can talk about how to set this up glOrtho(0., xres, 0., yres, -1000., 1.); ////// +#else + // (MT) Above line hides the frame counter behind the terrain. + glOrtho( 0., xres, 0., yres, -1., 1. ); +#endif glMatrixMode(GL_MODELVIEW); glPushMatrix(); @@ -200,7 +203,6 @@ const float x = 600.0f, y = 512.0f; glLoadIdentity(); glTranslatef(10, 30, 0); font_bind(font); - glprintf("%d FPS", fps); #ifndef NO_GUI @@ -221,8 +223,6 @@ const float x = 600.0f, y = 512.0f; static void do_tick() { } - - int main(int argc, char* argv[]) { @@ -269,7 +269,7 @@ glEnable (GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); - + new CConfig; // TODO: all loading should go through the VFS; for now, we allow normal access // this has to come before VFS init, as it changes the current dir @@ -280,8 +280,8 @@ glEnable (GL_DEPTH_TEST); //g_GUI.LoadXMLFile("sprite1.xml"); #endif - vfs_set_root(argv[0], "data"); + // tex = tex_load("0adlogo2.bmp"); // tex_upload(tex); font = font_load("verdana.fnt"); @@ -295,12 +295,16 @@ terr_init(); in_add_handler(handler); in_add_handler(terr_handler); + // fixed timestep main loop const double TICK_TIME = 30e-3; // [s] double time0 = get_time(); + g_Config.Update(); while(!quit) { + g_Config.Update(); + // TODO: limiter in case simulation can't keep up? double time1 = get_time(); while((time1-time0) > TICK_TIME) @@ -315,6 +319,7 @@ in_add_handler(terr_handler); SDL_GL_SwapBuffers(); calc_fps(); + } #ifndef NO_GUI @@ -322,5 +327,7 @@ in_add_handler(terr_handler); delete CGUI::GetSingletonPtr(); // again, we should have all singleton deletes somewhere #endif + delete &g_Config; + return 0; } diff --git a/source/ps/CStr.h b/source/ps/CStr.h index 6e1324eb07..c45fad12f9 100755 --- a/source/ps/CStr.h +++ b/source/ps/CStr.h @@ -16,7 +16,8 @@ Example: MyString += _T(" I'm the number ") += CStr(MyNumber); // Prints "Hello, World. I'm the number 126" - _tcout << (LPCTSTR)MyString << endl; + + _tcout << (LPCTSTR)MyString << endl; MyString = _("2341"); MyNum = MyString.ToInt(); @@ -35,6 +36,9 @@ More Info: #include "Prometheus.h" #include // Used for basic string functionality #include + +#include "..\lib\posix.h" + #include using namespace std; @@ -56,7 +60,6 @@ typedef wchar_t TCHAR; #define _totlower towlower #define _istspace iswspace #define _tsprintf wsprintf -#define _ultot _ultow #define _ltot _ltow #else @@ -77,7 +80,6 @@ typedef char TCHAR; #endif - enum PS_TRIM_MODE {PS_TRIM_LEFT, PS_TRIM_RIGHT, PS_TRIM_BOTH}; diff --git a/source/ps/Config.cpp b/source/ps/Config.cpp new file mode 100755 index 0000000000..10100a3daa --- /dev/null +++ b/source/ps/Config.cpp @@ -0,0 +1,308 @@ +// Last modified: 6 November 2003 (Mark Thompson) + +// TODO: A few changes from VFS -> CFile usage if required. + +#include "Config.h" +#include "vfs.h" + +using namespace std; + +DEFINE_ERROR( PS_FILE_NOT_FOUND, "A data file required by the engine could \ +not be found. Check that it exists within the game directory or archive tree." ); +DEFINE_ERROR( PS_FILE_LOAD_FAILURE, "One or more data files required by the \ +engine could not be loaded. These files may have been deleted or corrupted." ); +DEFINE_ERROR( PS_FILE_NODYNAMIC, "A data file was modified during execution, \ +but the engine cannot make one or more of these alterations while the game is \ +running." ); + +//-------------------------------------------------------- +// SConfigData: Internal file representation +//-------------------------------------------------------- + +SConfigData::SConfigData( CStr _Filename, void* _Data, LoaderFunction _DynamicLoader ) +{ + Filename = _Filename; + Data = _Data; + DynamicLoader = _DynamicLoader; + Timestamp = TIME_UNREGISTERED; +} + +//-------------------------------------------------------- +// CConfig: Dynamic Data Manager (singleton) +//-------------------------------------------------------- + +//-------------------------------------------------------- +// CConfig::CConfig() +//-------------------------------------------------------- + +CConfig::CConfig() +{ + Clear(); + Attach( NULL ); + i = m_FileList.begin(); +} + +//-------------------------------------------------------- +// CConfig::Register() +// +// Add a file to the registered list. +//-------------------------------------------------------- + +PS_RESULT CConfig::Register( CStr Filename, void* Data, LoaderFunction DynamicLoader ) +{ + assert( DynamicLoader != NULL ); + + if( m_LogFile ) + { + CStr Report = _T( "Adding file: " ); + Report += Filename; + m_LogFile->WriteText( (const TCHAR*)Report ); + } + + // Might as well check we can find the thing. + char filepath[PATH_MAX]; + strcpy( filepath, Filename ); + + if( vfs_access( filepath ) ) // This changes filepath to the disk location + // of the file, if we know it, to speed up + // checks later. + { + if( m_LogFile ) + { + CStr Error = _T( "File not found on: " ); + Error += Filename; + m_LogFile->WriteError( (const TCHAR*)Error ); + } + return( PS_FILE_NOT_FOUND ); + } + + m_FileList.push_back( SConfigData( CStr( filepath ), Data, DynamicLoader ) ); + + i = m_FileList.begin(); + + if( m_LogFile ) + { + CStr Report = _T( "Found file: " ); + Report += CStr( filepath ); + m_LogFile->WriteText( (const TCHAR*)Report ); + } + return( PS_OK ); +} + +//-------------------------------------------------------- +// CConfig::Update() +// +// Check timestamps of files and reload as required. +//-------------------------------------------------------- + +PS_RESULT CConfig::Update() +{ + _int slice = 0; + _int failed = 0; + struct stat FileInfo; + + for( slice = 0; ( i != m_FileList.end() ) && ( slice < CONFIG_SLICE ); i++, slice++ ) + { + // TODO: CFile change on following line. + + if( vfs_stat( i->Filename, &FileInfo ) ) + { + // We can't find the file; if it exists, it's in an archive. + if( i->Timestamp ) + { + // And it's already been loaded once, don't do so again. + continue; + } + // == TIME_UNREGISTERED. Load it, and set the modified date + // to now so that if it does turn up later on with a time + // after the start of the program, it will get loaded. + i->Timestamp = time( NULL ); + } + else + { + if( FileInfo.st_nlink ) + { + // This flag is set by vfs_stat to indicate that the file's + // gone walkabouts since last we knew its location. + // Find its new path and copy it back to the data we maintain + // here to speed up future queries. + char filepath[PATH_MAX]; + strcpy( filepath, i->Filename ); + vfs_access( filepath ); + if( m_LogFile ) + { + CStr Report = _T( "File " ); + Report += i->Filename; + Report += CStr( _T( " moved to: " ) ); + Report += CStr( filepath ); + m_LogFile->WriteText( (const TCHAR*)Report ); + } + + i->Filename = CStr( filepath ); + } + if( i->Timestamp == FileInfo.st_mtime ) + { + // This file has the same modification time as it did last + // time we checked. + continue; + } + i->Timestamp = FileInfo.st_mtime; + } + // If we reach here, the file needs to be (re)loaded. + + // Note also that polling every frame via _stat() for a file which + // either does not exist or exists only in an archive could be a + // considerable waste of time, but if not done the game won't pick + // up on modified versions of archived files moved into the main + // directory trees. Also, alternatives to polling don't tend to be + // portable. + + slice--; + + // Reloaded files do not count against the slice quota. + + if( m_LogFile ) + { + CStr Report = _T( "Reloading file: " ); + Report += i->Filename; + m_LogFile->WriteText( (const TCHAR*)Report ); + } + + PS_RESULT Result; + if( ( Result = i->DynamicLoader( i->Filename, i->Data ) ) != PS_OK ) + { + if( m_LogFile ) + { + CStr Error = _T( "Load failed on: " ); + Error += CStr( i->Filename ); + Error += CStr( "Load function returned: " ); + Error += CStr( Result ); + m_LogFile->WriteError( (const TCHAR*)Error ); + } + failed++; + if( Result != PS_FILE_NODYNAMIC ) + return( PS_FILE_LOAD_FAILURE ); // Oops. Serious problem, bail. + } + } + if( i == m_FileList.end() ) i = m_FileList.begin(); + if( failed ) + return( PS_FILE_NODYNAMIC ); + return( PS_OK ); +} + +//-------------------------------------------------------- +// CConfig::ReloadAll() +// +// Reloads all files. +//-------------------------------------------------------- + +PS_RESULT CConfig::ReloadAll() +{ + // Mostly identical to Update(), above. + _int failed = 0; + _int notfound = 0; + struct stat FileInfo; + + for( i = m_FileList.begin(); i != m_FileList.end(); i++ ) + { + // TODO: CFile change on following line. + + if( vfs_stat( i->Filename, &FileInfo ) ) + { + // We can't find the file. Seeing as this should reload everything, + // check that it exists. + char filepath[PATH_MAX]; + strcpy( filepath, i->Filename ); + if( vfs_access( filepath ) ) + { + // Oops. + notfound++; + if( m_LogFile ) + { + CStr Error = _T( "File not found on: " ); + Error += i->Filename; + m_LogFile->WriteError( (const TCHAR*)Error ); + } + continue; + } + i->Filename = CStr( filepath ); + i->Timestamp = time( NULL ); + } + else + { + if( FileInfo.st_nlink ) + { + // This flag is set by vfs_stat to indicate that the file's + // gone walkabouts since last we knew its location. + // Find its new path and copy it back to the data we maintain + // here to speed up future queries. + char filepath[PATH_MAX]; + strcpy( filepath, i->Filename ); + vfs_access( filepath ); + if( m_LogFile ) + { + CStr Report = _T( "File " ); + Report += i->Filename; + Report += CStr( _T( " moved to: " ) ); + Report += CStr( filepath ); + m_LogFile->WriteText( (const TCHAR*)Report ); + } + + i->Filename = CStr( filepath ); + } + i->Timestamp = FileInfo.st_mtime; + } + + // And load them all again... + + if( m_LogFile ) + { + CStr Report = _T( "Reloading file: " ); + Report += i->Filename; + m_LogFile->WriteText( (const TCHAR*)Report ); + } + + PS_RESULT Result; + if( ( Result = i->DynamicLoader( i->Filename, i->Data ) ) != PS_OK ) + { + if( m_LogFile ) + { + CStr Error = _T( "Load failed on: " ); + Error += CStr( i->Filename ); + Error += CStr( "Load function returned: " ); + Error += CStr( Result ); + m_LogFile->WriteError( (const TCHAR*)Error ); + } + failed++; + if( Result != PS_FILE_NODYNAMIC ) + return( PS_FILE_LOAD_FAILURE ); // Oops. Serious problem, bail. + } + } + if( notfound ) + return( PS_FILE_NOT_FOUND ); + if( failed ) + return( PS_FILE_NODYNAMIC ); + return( PS_OK ); +} + +//-------------------------------------------------------- +// CConfig::Clear() +// +// Erases registered list. +//-------------------------------------------------------- + +void CConfig::Clear() +{ + m_FileList.clear(); +} + +//-------------------------------------------------------- +// CConfig::Attach() +// +// Attaches (or detaches, with a NULL argument) a logfile class. +//-------------------------------------------------------- + +void CConfig::Attach( CLogFile* LogFile ) +{ + m_LogFile = LogFile; +} diff --git a/source/ps/Config.h b/source/ps/Config.h new file mode 100755 index 0000000000..676b4c5ca6 --- /dev/null +++ b/source/ps/Config.h @@ -0,0 +1,116 @@ +/* +Config.h + +CConfig dynamic data file manager class +Mark Thompson (mot20@cam.ac.uk) + +Last modified: 6 November 2003 (Mark Thompson) + +--Overview-- + +Maintains a list of data files in use by the engine; reloads any data file +altered during execution when Update() is called. + +--Usage-- + +Load files by calling CConfig::Register() for each. The files are not loaded +until Update() is called; do so at the end of the block. + +Update() compares the 'last-modified' timestamps of all registered files, +reloading all that have been altered since the last call (also any newly +registered). + +Loader functions passed to Register() must take a CStr argument for a +filename, a void* for additional data, and return PS_OK if successful. +They must also handle the case where modified data is being reloaded. Ideally, +they should release resources allocated to the old version and load the new, at +least functions must leave the system in a predictable state. (e.g. if graphics +files are changed, but the loader functions cannot reload them, they should +do nothing but return PS_FILE_NODYNAMIC) + +--Examples-- + +g_Config.Register( "gameParameters.dat", NULL, paramLoader ); +g_Config.Register( "graphicsParameters7.dat", (void*)7, gfxParamLoader ); +g_Config.Update(); + +--More info-- + +TDD at http://forums.wildfiregames.com/0ad + +*/ + +#ifndef Config_H +#define Config_H + +//-------------------------------------------------------- +// Includes / Compiler directives +//-------------------------------------------------------- + +#include "stdlib.h" +#include "Prometheus.h" +#include "Singleton.h" +#include "CStr.h" +#include "LogFile.h" +#include "..\lib\posix.h" +#include "..\lib\unzip.h" +#include "..\lib\misc.h" + +#include + +//-------------------------------------------------------- +// Macros +//-------------------------------------------------------- + +// Get singleton +#define g_Config CConfig::GetSingleton() + +//Dummy timestamp value +#define TIME_UNREGISTERED 0 +#define ACCESS_EXISTS 0 +//The maximum number of files processed in one call to Update() +#define CONFIG_SLICE 100 + +//-------------------------------------------------------- +// Declarations +//-------------------------------------------------------- + +//Possible return codes +DECLARE_ERROR( PS_FILE_NOT_FOUND ); +DECLARE_ERROR( PS_FILE_LOAD_FAILURE ); +DECLARE_ERROR( PS_FILE_NODYNAMIC ); + +//Loader function +typedef PS_RESULT (*LoaderFunction)( CStr Filename, void* Data ); + +//Internal registration type +struct SConfigData +{ + CStr Filename; + LoaderFunction DynamicLoader; + void* Data; + time_t Timestamp; + SConfigData( CStr _Filename, void* _Data, LoaderFunction _DynamicLoader ); +}; + +class CConfig : public Singleton +{ +public: + CConfig(); + //Register a new file with it's associated loader function + PS_RESULT Register( CStr Filename, void* Data, LoaderFunction DynamicLoader ); + //Check all registered files, reload as neccessary + PS_RESULT Update(); + //Force an update of all files in the registered list. + PS_RESULT ReloadAll(); + //Erase the entire list of registered files + void Clear(); + //Attach or detach a logfile class. + void Attach( CLogFile* LogFile ); +private: + vector m_FileList; + vector::iterator i; + CLogFile* m_LogFile; +}; + +#endif diff --git a/source/terrain/Camera.cpp b/source/terrain/Camera.cpp index 63b97d69cf..54f7be10a9 100755 --- a/source/terrain/Camera.cpp +++ b/source/terrain/Camera.cpp @@ -16,8 +16,13 @@ extern int xres, yres; CCamera::CCamera () { - m_ViewPort.m_Width = 1280; - m_ViewPort.m_Height = 1024; +#ifdef WIDEASPECT + m_ViewPort.m_Width = 1440; + m_ViewPort.m_Height = 900; +#else + m_ViewPort.m_Width = 1600; + m_ViewPort.m_Height = 1200; +#endif m_ViewPort.m_X = 0; m_ViewPort.m_Y = 0; } diff --git a/source/terrain/terrainMain.cpp b/source/terrain/terrainMain.cpp index 2f4b1b39fc..6418bf944b 100755 --- a/source/terrain/terrainMain.cpp +++ b/source/terrain/terrainMain.cpp @@ -9,7 +9,6 @@ // TODO: fix scrolling hack - framerate independent, use SDL #include "win.h" // REMOVEME - void InitScene (); void InitResources (); void RenderScene (); @@ -46,7 +45,11 @@ extern int xres, yres; void terr_init() { - g_Renderer.Initialize (1280, 1024, 32); +#ifdef WIDEASPECT + g_Renderer.Initialize( 1440, 900, 32 ); +#else + g_Renderer.Initialize (1600, 1200, 32); +#endif InitResources (); InitScene (); @@ -298,6 +301,7 @@ int cnt; "blendtwooppositecorners.raw", "blendushape.raw" }; + /* //for(i = 0; i < NUM_ALPHA_MAPS;i++) i=5; @@ -328,7 +332,7 @@ cnt=5; for(i = 0; i < cnt; i++) { - AlphaMaps[i] = tex_load(fns[i]); + AlphaMaps[i] = tex_load(fns[i]); tex_upload(AlphaMaps[i], GL_LINEAR, GL_INTENSITY4); }