From 958e939492dfbef54c5de08bd773f3986e5e92b9 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sun, 19 Jul 2009 12:28:32 +0100 Subject: [PATCH] Loading and editing maps in resources now works correctly --- include/GameScreens.hpp | 2 +- include/IMap.hpp | 18 +++++---- include/IResource.hpp | 14 +++++-- src/Editor.cpp | 16 ++++---- src/Main.cpp | 10 ++--- src/Map.cpp | 56 ++++++++++++++------------- src/Model.cpp | 7 ++-- src/Resource.cpp | 84 +++++++++++++++++++++++++++++++++-------- 8 files changed, 137 insertions(+), 70 deletions(-) diff --git a/include/GameScreens.hpp b/include/GameScreens.hpp index 16eab80..45cb244 100644 --- a/include/GameScreens.hpp +++ b/include/GameScreens.hpp @@ -24,7 +24,7 @@ // Create the various screens // These may be called multiple times -IScreenPtr makeEditorScreen(IMapPtr aMap, const std::string& aFileName); +IScreenPtr makeEditorScreen(IMapPtr aMap); IScreenPtr makeGameScreen(IMapPtr aMap); // Access to the window the game is running in diff --git a/include/IMap.hpp b/include/IMap.hpp index 2eef127..1de99c9 100644 --- a/include/IMap.hpp +++ b/include/IMap.hpp @@ -21,6 +21,7 @@ #include "IGraphics.hpp" #include "ITrackSegment.hpp" #include "IStation.hpp" +#include "IResource.hpp" #include #include @@ -72,8 +73,11 @@ public: // True if this names a valid tile virtual bool isValidTileName(unsigned aName) const = 0; - // Save the map to the given file - virtual void save(const std::string& aFileName) = 0; + // Save the map to its resource + virtual void save() = 0; + + // Return the name of the map resource + virtual string name() const = 0; // Change the start location // The second variant allows setting the direction as well @@ -103,12 +107,12 @@ public: }; -typedef std::tr1::shared_ptr IMapPtr; +typedef shared_ptr IMapPtr; -// Make an empty map -IMapPtr makeEmptyMap(int aWidth, int aHeight); +// Make an empty map inside a resource +IMapPtr makeEmptyMap(const string& aResId, int aWidth, int aHeight); -// Load a map from an XML file -IMapPtr loadMap(const std::string& aFileName); +// Load a map from a resource +IMapPtr loadMap(const string& aResId); #endif diff --git a/include/IResource.hpp b/include/IResource.hpp index a5d3e76..99ac516 100644 --- a/include/IResource.hpp +++ b/include/IResource.hpp @@ -38,16 +38,22 @@ struct IResource { // A handle for reading data out of files in resources class Handle { public: - explicit Handle(const string& aFileName); + enum Mode { READ, WRITE }; + + explicit Handle(const string& aFileName, Mode aMode = READ); + + ifstream& rstream() { return *myReadStream; } + ofstream& wstream() { return *myWriteStream; } - ifstream& stream() { return *myStream; } string fileName() const { return myFileName; } private: - shared_ptr myStream; + shared_ptr myReadStream; + shared_ptr myWriteStream; const string myFileName; }; virtual Handle openFile(const string& aName) = 0; + virtual Handle writeFile(const string& aName) = 0; }; typedef shared_ptr IResourcePtr; @@ -59,5 +65,7 @@ typedef ResourceList::iterator ResourceListIt; void initResources(); void enumResources(const string& aClass, ResourceList& aList); IResourcePtr findResource(const string& aResId, const string& aClass); +IResourcePtr makeNewResource(const string& aResId, const string& aClass); +bool resourceExists(const string& aResId, const string& aClass); #endif diff --git a/src/Editor.cpp b/src/Editor.cpp index eec9d74..890dc79 100644 --- a/src/Editor.cpp +++ b/src/Editor.cpp @@ -37,7 +37,7 @@ // Concrete editor class class Editor : public IScreen { public: - Editor(IMapPtr aMap, const string& aFileName); + Editor(IMapPtr aMap); ~Editor(); void display(IGraphicsPtr aContext) const; @@ -75,7 +75,6 @@ private: Vector myPosition; Tool myTool; - string myFileName; bool amScrolling; // Variables for dragging track segments @@ -124,10 +123,9 @@ void addEditorGUI() theModelViewer->setModel(loadBuilding("white_house")->model()); } -Editor::Editor(IMapPtr aMap, const string& aFileName) +Editor::Editor(IMapPtr aMap) : myMap(aMap), myPosition(4.5, -17.5, -21.5), - myTool(TRACK_TOOL), myFileName(aFileName), - amScrolling(false), amDragging(false) + myTool(TRACK_TOOL), amScrolling(false), amDragging(false) { mySun = makeSunLight(); @@ -135,7 +133,7 @@ Editor::Editor(IMapPtr aMap, const string& aFileName) myMap->setGrid(true); - log() << "Editing " << aFileName; + log() << "Editing " << aMap->name(); } Editor::~Editor() @@ -548,7 +546,7 @@ void Editor::onKeyDown(SDLKey aKey) switch (aKey) { case SDLK_x: // Write out to disk - myMap->save(myFileName); + myMap->save(); break; case SDLK_g: // Toggle grid @@ -567,7 +565,7 @@ void Editor::onKeyDown(SDLKey aKey) } // Create an instance of the editor screen -IScreenPtr makeEditorScreen(IMapPtr aMap, const string& aFileName) +IScreenPtr makeEditorScreen(IMapPtr aMap) { - return IScreenPtr(new Editor(aMap, aFileName)); + return IScreenPtr(new Editor(aMap)); } diff --git a/src/Main.cpp b/src/Main.cpp index 6fc5739..5e91c60 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -18,7 +18,7 @@ #include "IWindow.hpp" #include "ILogger.hpp" #include "GameScreens.hpp" -#include "Resources.hpp" +#include "IResource.hpp" #include @@ -50,16 +50,16 @@ int main(int argc, char** argv) const string cmd(argv[1]); #else // For ease of debugging, specify a default map - const string mapFile("maps\\figure8.xml"); + const string mapFile("figure8"); const string cmd("play"); #endif // #ifndef WIN32 IScreenPtr screen; if (cmd == "edit") { theWindow = makeFLTKWindow("Train Game Editor", addEditorGUI); - if (exists(mapFile)) - screen = makeEditorScreen(loadMap(mapFile), mapFile); + if (resourceExists(mapFile, "maps")) + screen = makeEditorScreen(loadMap(mapFile)); else - screen = makeEditorScreen(makeEmptyMap(64, 64), mapFile); + screen = makeEditorScreen(makeEmptyMap(mapFile, 64, 64)); } else if (cmd == "play") { theWindow = makeSDLWindow(); diff --git a/src/Map.cpp b/src/Map.cpp index a546a3d..0bdba24 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -70,7 +70,7 @@ class Map : public IMap, public ISectorRenderable, public enable_shared_from_this { friend class MapLoader; public: - Map(); + Map(IResourcePtr aRes); ~Map(); // IMap interface @@ -78,6 +78,8 @@ public: int depth() const { return myDepth; } double heightAt() const { return 0.0; } + string name() const { return myResource->name(); } + void setStart(int x, int y); void setStart(int x, int y, int dirX, int dirY); void setGrid(bool onOff); @@ -100,7 +102,7 @@ public: const Point& aFinishPos); void levelArea(Point aStartPos, Point aFinishPos); - void save(const string& aFileName); + void save(); IStationPtr extendStation(Point aStartPos, Point aFinishPos); @@ -169,7 +171,7 @@ private: } void resetMarks() const; - void writeHeightMap(const string& aFileName) const; + void writeHeightMap() const; void readHeightMap(IResource::Handle aHandle); void tileVertices(int x, int y, int* indexes) const; void renderPickSector(Point botLeft, Point topRight); @@ -195,15 +197,17 @@ private: IFogPtr myFog; bool shouldDrawGridLines, inPickMode; list > myDirtyTiles; + IResourcePtr myResource; }; const float Map::TILE_HEIGHT(0.2f); -Map::Map() +Map::Map(IResourcePtr aRes) : myTiles(NULL), myHeightMap(NULL), myWidth(0), myDepth(0), myStartLocation(makePoint(1, 1)), myStartDirection(axis::X), - shouldDrawGridLines(false), inPickMode(false) + shouldDrawGridLines(false), inPickMode(false), + myResource(aRes) { myFog = makeFog(0.25f, // Density 60.0f, 70.0f); // Start and end distance @@ -1024,16 +1028,16 @@ IStationPtr Map::extendStation(Point aStartPos, Point aFinishPos) // Bytes 0-3 Width of map // Bytes 4-7 Depth of map // Bytes 8+ Raw height data -void Map::writeHeightMap(const string& aFileName) const +void Map::writeHeightMap() const { using namespace boost; - log() << "Writing terrain height map to " << aFileName; + IResource::Handle h = myResource->writeFile(myResource->name() + ".bin"); - ofstream of(aFileName.c_str(), ios::binary); - if (!of.good()) - throw runtime_error("Failed to open " + aFileName + " for writing"); + log() << "Writing terrain height map to " << h.fileName(); + ofstream& of = h.wstream(); + const int32_t wl = static_cast(myWidth); const int32_t dl = static_cast(myDepth); of.write(reinterpret_cast(&wl), sizeof(int32_t)); @@ -1051,7 +1055,7 @@ void Map::readHeightMap(IResource::Handle aHandle) log() << "Reading height map from " << aHandle.fileName(); - istream& is = aHandle.stream(); + istream& is = aHandle.rstream(); // Check the dimensions of the binary file match the XML file int32_t wl, dl; @@ -1076,16 +1080,16 @@ void Map::readHeightMap(IResource::Handle aHandle) } // Turn the map into XML -void Map::save(const string& aFileName) +void Map::save() { using namespace boost::filesystem; - - log() << "Saving map to " << aFileName; - ofstream of(aFileName.c_str()); - if (!of.good()) - throw runtime_error("Failed to open " + aFileName + " for writing"); + IResource::Handle h = myResource->writeFile(myResource->name() + ".xml"); + + log() << "Saving map to " << h.fileName(); + ofstream& of = h.wstream(); + xml::element root("map"); root.addAttribute("width", myWidth); root.addAttribute("height", myDepth); @@ -1119,14 +1123,11 @@ void Map::save(const string& aFileName) } // Generate the height map - // Note: change_extension is deprecated (use .replace_extension() instead - // when boost is updated in Debian) - const string binFile(change_extension(path(aFileName), ".bin").file_string()); - writeHeightMap(binFile); + writeHeightMap(); root.addChild (xml::element("heightmap") - .addText(binFile)); + .addText(myResource->name() + ".bin")); xml::element tileset("tileset"); @@ -1165,10 +1166,13 @@ void Map::save(const string& aFileName) of << xml::document(root); } -IMapPtr makeEmptyMap(int aWidth, int aDepth) +IMapPtr makeEmptyMap(const string& aResId, int aWidth, int aDepth) { - shared_ptr ptr(new Map); + IResourcePtr res = makeNewResource(aResId, "maps"); + + shared_ptr ptr(new Map(res)); ptr->resetMap(aWidth, aDepth); + ptr->save(); return IMapPtr(ptr); } @@ -1326,10 +1330,10 @@ private: IMapPtr loadMap(const string& aResId) { - shared_ptr map(new Map); - IResourcePtr res = findResource(aResId, "maps"); + shared_ptr map(new Map(res)); + log() << "Loading map from file " << res->xmlFileName(); static IXMLParserPtr xmlParser = makeXMLParser("schemas/map.xsd"); diff --git a/src/Model.cpp b/src/Model.cpp index 8770f99..c604611 100644 --- a/src/Model.cpp +++ b/src/Model.cpp @@ -62,7 +62,7 @@ MaterialFile::MaterialFile(IResource::Handle aHandle) { log() << "Loading materials from " << aHandle.fileName(); - istream& is = aHandle.stream(); + istream& is = aHandle.rstream(); string activeMaterial; while (!is.eof()) { @@ -156,10 +156,9 @@ IModelPtr loadModel(IResourcePtr aRes, const string& aFileName, float aScale) if (it != theCache.end()) return (*it).second; - log() << "Loading model " << cacheName; - // Not in the cache, load it from the resource IResource::Handle h = aRes->openFile(aFileName); + log() << "Loading model " << h.fileName(); vector vertices; vector normals; @@ -175,7 +174,7 @@ IModelPtr loadModel(IResourcePtr aRes, const string& aFileName, float aScale) MaterialFilePtr materialFile; - ifstream& f = h.stream(); + ifstream& f = h.rstream(); while (!f.eof()) { string first; diff --git a/src/Resource.cpp b/src/Resource.cpp index 13f4b46..db8e1f2 100644 --- a/src/Resource.cpp +++ b/src/Resource.cpp @@ -43,18 +43,34 @@ public: Handle openFile(const string& aFileName) { - return Handle((myPath / aFileName).file_string()); + return Handle((myPath / aFileName).file_string(), Handle::READ); + } + + Handle writeFile(const string& aFileName) + { + return Handle((myPath / aFileName).file_string(), Handle::WRITE); } private: const path myPath; }; -IResource::Handle::Handle(const string& aFileName) - : myStream(new ifstream(aFileName.c_str())), - myFileName(aFileName) +IResource::Handle::Handle(const string& aFileName, Mode aMode) + : myFileName(aFileName) { - if (!myStream->good()) - throw runtime_error("Failed to open resource file " + aFileName); + if (aMode == READ) { + myReadStream = shared_ptr(new ifstream(aFileName.c_str())); + + if (!myReadStream->good()) + throw runtime_error("Failed to open resource file " + aFileName); + } + else if (aMode == WRITE) { + myWriteStream = shared_ptr(new ofstream(aFileName.c_str())); + + if (!myWriteStream->good()) + throw runtime_error("Failed to open resource file " + aFileName); + } + else + throw runtime_error("Bad mode for Handle"); } namespace { @@ -73,7 +89,7 @@ namespace { return theResources[aClass]; } - void addResource(const char* aClass, IResourcePtr aRes) + void addResource(const string& aClass, IResourcePtr aRes) { resClassList(aClass).push_back(aRes); } @@ -119,15 +135,53 @@ void enumResources(const string& aClass, ResourceList& aList) } -// Find a resource of a particular type +namespace { + // Find a resource of a particular type + // Returns null pointer on failure + IResourcePtr maybeFindResource(const string& aResId, const string& aClass) + { + ResourceList& rlist = resClassList(aClass); + for (ResourceListIt it = rlist.begin(); it != rlist.end(); ++it) { + if ((*it)->name() == aResId) + return *it; + } + + return IResourcePtr(); + } +} + +// Find a resource or throw an exception on failure IResourcePtr findResource(const string& aResId, const string& aClass) { - ResourceList& rlist = resClassList(aClass); - for (ResourceListIt it = rlist.begin(); it != rlist.end(); ++it) { - if ((*it)->name() == aResId) - return *it; - } + IResourcePtr r = maybeFindResource(aResId, aClass); + if (r) + return r; + else + throw runtime_error("Failed to find resource " + aResId + + " in class " + aClass); +} + +// True if the given resource exists +bool resourceExists(const string& aResId, const string& aClass) +{ + return maybeFindResource(aResId, aClass); +} + +// Create an empty resource directory +IResourcePtr makeNewResource(const string& aResId, const string& aClass) +{ + const path p = path(aClass) / aResId; + + if (exists(p)) + throw runtime_error("Cannot create resource " + aResId + + "in class " + aClass + ": already exists!"); + + if (!create_directories(p)) + throw runtime_error("Failed to create resource directory " + + p.file_string()); + + IResourcePtr r = IResourcePtr(new FilesystemResource(p)); + addResource(aClass, r); - throw runtime_error("Failed to find resource " + aResId - + " in class " + aClass); + return r; } -- 2.39.2