Skip to content

Commit 89eaddc

Browse files
authored
Merge pull request fallahn#128 from mpartel/tileset-without-map
Allow loading a Tileset without a Map
2 parents fcef1a2 + 831fb4d commit 89eaddc

15 files changed

Lines changed: 426 additions & 163 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<tileset name="platform" class="Level" tilewidth="64" tileheight="64" tilecount="42" columns="6">
3+
<properties>
4+
<property name="bool property" type="bool" value="false"/>
5+
<property name="float property" type="float" value="56.770000000000003"/>
6+
<property name="int property" type="int" value="12"/>
7+
<property name="string property" value="shoes"/>
8+
</properties>
9+
<image source="tileset.png" trans="ff00ff" width="384" height="448"/>
10+
<terraintypes>
11+
<terrain name="brown" tile="-1"/>
12+
<terrain name="green" tile="-1"/>
13+
</terraintypes>
14+
<tile id="0" terrain=",0,,0"/>
15+
<tile id="22" terrain="1,1,1,"/>
16+
<tile id="25" terrain=",1,,1"/>
17+
<tile id="26" terrain="0,,0,"/>
18+
</tileset>
29 KB
Loading
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
tileset by Alucard
2+
http://opengameart.org/content/pixel-art-platformer-complete-pack
6.33 KB
Loading

ParseTest/src/main.cpp

Lines changed: 122 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,27 +25,29 @@ and must not be misrepresented as being the original software.
2525
source distribution.
2626
*********************************************************************/
2727

28+
#include <tmxlite/FreeFuncs.hpp>
2829
#include <tmxlite/Map.hpp>
2930
#include <tmxlite/ObjectGroup.hpp>
3031
#include <tmxlite/LayerGroup.hpp>
3132
#include <tmxlite/TileLayer.hpp>
33+
#include <tmxlite/Tileset.hpp>
3234

3335
#include <iostream>
3436
#include <array>
3537
#include <string>
3638

3739
namespace
3840
{
39-
const std::array<std::string, 4u> LayerStrings =
40-
{
41-
std::string("Tile"),
42-
std::string("Object"),
43-
std::string("Image"),
44-
std::string("Group"),
45-
};
46-
}
4741

48-
int main()
42+
const std::array<std::string, 4u> LayerStrings =
43+
{
44+
std::string("Tile"),
45+
std::string("Object"),
46+
std::string("Image"),
47+
std::string("Group"),
48+
};
49+
50+
void testLoadMap()
4951
{
5052
tmx::Map map;
5153

@@ -69,6 +71,7 @@ int main()
6971
{
7072
std::cout << "Tileset: " << tileset.getName() << std::endl;
7173
std::cout << "Tileset class: " << tileset.getClass() << std::endl;
74+
std::cout << "Tileset first GID: " << tileset.getFirstGID() << std::endl;
7275
}
7376

7477
std::cout << "Map has " << mapProperties.size() << " properties" << std::endl;
@@ -169,11 +172,121 @@ int main()
169172
{
170173
std::cout << "Failed loading map" << std::endl;
171174
}
175+
}
176+
177+
void testLoadTilesetWithoutMap()
178+
{
179+
tmx::Tileset ts;
180+
if (!ts.loadWithoutMap("images/tilemap/platform.tsx"))
181+
{
182+
std::cout << "Failed to load tileset" << std::endl;
183+
return;
184+
}
185+
186+
std::cout << "Loaded tileset without map: " << ts.getName() << std::endl;
187+
std::cout << "Tileset class: " << ts.getClass() << std::endl;
188+
std::cout << "Tileset image: " << ts.getImagePath() << std::endl;
189+
std::cout << "Tileset first GID: " << ts.getFirstGID() << std::endl;
190+
if (ts.getColumnCount() > 0)
191+
{
192+
std::cout << "Tiles in tileset: " << ts.getTileCount() << std::endl;
193+
}
194+
}
195+
196+
class TestFailure {};
197+
198+
template <typename T1, typename T2>
199+
void checkEqImpl(const char* expr1, T1&& v1, const char* expr2, T2&& v2, const char* file, ssize_t line)
200+
{
201+
if (v1 != v2)
202+
{
203+
std::cout << "FAIL: " << expr1 << " = " << expr2
204+
<< "\n Left: " << v1 << "\n Right: " << v2
205+
<< "\nat " << file << ":" << line
206+
<< std::endl;
207+
throw TestFailure();
208+
}
209+
else
210+
{
211+
std::cout << "OK: " << expr1 << " = " << expr2 << " = " << v1 << std::endl;
212+
}
213+
}
214+
215+
#define CHECK_EQ(a, b) checkEqImpl(#a, (a), #b, (b), __FILE__, __LINE__)
216+
217+
struct RevertWindowsPathHandling
218+
{
219+
bool oldValue = tmx::enableWindowsPathHandling;
220+
~RevertWindowsPathHandling()
221+
{
222+
tmx::enableWindowsPathHandling = oldValue;
223+
}
224+
};
225+
226+
void testResolvingPaths()
227+
{
228+
RevertWindowsPathHandling revertWindowsPathHandling;
229+
tmx::enableWindowsPathHandling = true;
230+
231+
CHECK_EQ(tmx::resolveFilePath("a/b/c", "A/B/C"), "A/B/C/a/b/c");
232+
CHECK_EQ(tmx::resolveFilePath("a/b/c/", "A/B/C"), "A/B/C/a/b/c");
233+
CHECK_EQ(tmx::resolveFilePath("a/b/c", "A/B/C/"), "A/B/C/a/b/c");
234+
235+
CHECK_EQ(tmx::resolveFilePath("a/b/c", ""), "a/b/c");
236+
CHECK_EQ(tmx::resolveFilePath("", "A/B/C"), "A/B/C");
237+
238+
CHECK_EQ(tmx::resolveFilePath("a///b//c", "A//B///C"), "A/B/C/a/b/c");
239+
240+
CHECK_EQ(tmx::resolveFilePath("/a/b/c", "A/B/C"), "/a/b/c");
241+
CHECK_EQ(tmx::resolveFilePath("a/b/c", "/A/B/C"), "/A/B/C/a/b/c");
242+
243+
CHECK_EQ(tmx::resolveFilePath("./a/b/c", "A/B/C"), "A/B/C/a/b/c");
244+
CHECK_EQ(tmx::resolveFilePath("../a/b/c", "A/B/C"), "A/B/a/b/c");
245+
CHECK_EQ(tmx::resolveFilePath("../../a/b/c", "A/B/C"), "A/a/b/c");
246+
CHECK_EQ(tmx::resolveFilePath("../../../a/b/c", "A/B/C"), "a/b/c");
247+
CHECK_EQ(tmx::resolveFilePath("../../../../a/b/c", "A/B/C"), "../a/b/c");
248+
CHECK_EQ(tmx::resolveFilePath("../../../../../a/b/c", "A/B/C"), "../../a/b/c");
249+
250+
CHECK_EQ(tmx::resolveFilePath("a/b/c", "./A/B/C"), "A/B/C/a/b/c");
251+
CHECK_EQ(tmx::resolveFilePath("a/b/c", "../A/B/C"), "../A/B/C/a/b/c");
252+
CHECK_EQ(tmx::resolveFilePath("a/b/c", "../../A/B/C"), "../../A/B/C/a/b/c");
253+
254+
CHECK_EQ(tmx::resolveFilePath("a/../b/c", "A/B/C"), "A/B/C/b/c");
255+
CHECK_EQ(tmx::resolveFilePath("a/b/c", "A/../B/C"), "B/C/a/b/c");
256+
CHECK_EQ(tmx::resolveFilePath("a/../b/../c", "A/B/C"), "A/B/C/c");
257+
CHECK_EQ(tmx::resolveFilePath("a/b/c", "A/../B/../C"), "C/a/b/c");
258+
CHECK_EQ(tmx::resolveFilePath("a/../b/../c", "A/../B/../C"), "C/c");
259+
260+
CHECK_EQ(tmx::resolveFilePath("a/b/c/../..", "A/B/C"), "A/B/C/a");
261+
CHECK_EQ(tmx::resolveFilePath("a/b/c/../../..", "A/B/C"), "A/B/C");
262+
CHECK_EQ(tmx::resolveFilePath("a/b/c/../../../..", "A/B/C"), "A/B");
263+
264+
CHECK_EQ(tmx::resolveFilePath("/a/../b/c", "A/B/C"), "/b/c");
265+
CHECK_EQ(tmx::resolveFilePath("/a/../../b/c", "A/B/C"), "/../b/c"); // "/b/c" would also be valid but not worth handling this weird case IMO
266+
267+
CHECK_EQ(tmx::resolveFilePath("C:/a/../b/c", "A/B/C"), "C:/b/c");
268+
CHECK_EQ(tmx::resolveFilePath("a/../b/c", "C:/A/B/C"), "C:/A/B/C/b/c");
269+
CHECK_EQ(tmx::resolveFilePath("C:/a/../b/c", "C:/A/B/C"), "C:/b/c");
270+
271+
CHECK_EQ(tmx::resolveFilePath("a\\..\\b\\c", "C:\\A\\B\\..\\C"), "C:/A/C/b/c");
272+
}
273+
274+
} // namespace
275+
276+
int main()
277+
{
278+
testLoadMap();
279+
std::cout << std::endl << "------------------------------" << std::endl << std::endl;
280+
testLoadTilesetWithoutMap();
281+
std::cout << std::endl << "------------------------------" << std::endl << std::endl;
282+
testResolvingPaths();
283+
std::cout << std::endl << "------------------------------" << std::endl << std::endl;
172284

173285
#if defined(PAUSE_AT_END)
174286
std::cout << std::endl << "Press return to quit..." <<std::endl;
175287
std::cin.get();
176288
#endif
177289

290+
std::cout << "Test complete" << std::endl;
178291
return 0;
179292
}

tmxlite/CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ else()
3434
endif()
3535
endif()
3636

37-
set(CMAKE_CXX_STANDARD 14)
38-
set(CMAKE_CXX_STANDARD_REQUIRED ON)
37+
if(NOT DEFINED CMAKE_CXX_STANDARD)
38+
set(CMAKE_CXX_STANDARD 14)
39+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
40+
endif()
3941

4042
if(TMXLITE_STATIC_LIB)
4143
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -D_DEBUG_ -DTMXLITE_STATIC")

tmxlite/include/tmxlite/Config.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,10 @@ source distribution.
6161
//static build doesn't need import/export macros
6262
#define TMXLITE_EXPORT_API
6363

64-
#endif //TMXLITE_STATIC
64+
#endif //TMXLITE_STATIC
65+
66+
#if __cpp_constinit >= 201907
67+
#define TMXLITE_CONSTINIT constinit
68+
#else
69+
#define TMXLITE_CONSTINIT
70+
#endif

tmxlite/include/tmxlite/FreeFuncs.hpp

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ René Nyffenegger rene.nyffenegger@adp-gmbh.ch
5353

5454
#include <tmxlite/detail/Android.hpp>
5555
#include <tmxlite/detail/Log.hpp>
56+
#include <tmxlite/Config.hpp>
5657
#include <tmxlite/Types.hpp>
5758

5859
#include <string>
@@ -63,7 +64,7 @@ René Nyffenegger rene.nyffenegger@adp-gmbh.ch
6364

6465
namespace tmx
6566
{
66-
//using inline here just to supress unused warnings on gcc
67+
//using inline here just to supress unused warnings on gcc (TODO: can say "(void)x" instead)
6768
bool decompress(const char* source, std::vector<unsigned char>& dest, std::size_t inSize, std::size_t expectedSize);
6869

6970
static inline std::string base64_decode(std::string const& encoded_string)
@@ -163,41 +164,34 @@ namespace tmx
163164
return{};
164165
}
165166

166-
static inline std::string resolveFilePath(std::string path, const std::string& workingDir)
167-
{
168-
static const std::string match("../");
169-
std::size_t result = path.find(match);
170-
std::size_t count = 0;
171-
while (result != std::string::npos)
172-
{
173-
count++;
174-
path = path.substr(result + match.size());
175-
result = path.find(match);
176-
}
177-
178-
if (workingDir.empty()) return path;
179-
180-
std::string outPath = workingDir;
181-
for (auto i = 0u; i < count; ++i)
182-
{
183-
result = outPath.find_last_of('/');
184-
if (result != std::string::npos)
185-
{
186-
outPath = outPath.substr(0, result);
187-
}
188-
}
189-
// this does only work on windows
190-
#ifndef __ANDROID__
191-
return outPath + '/' + path;
192-
#endif
193-
194-
// todo: make resolveFilePath work with subfolders on
195-
// android - currently only the root folder is working
196-
197-
#ifdef __ANDROID__
198-
return path;
199-
#endif
200-
}
167+
/*!
168+
\brief Splits 's' at each 'sep' and appends the parts to 'out'.
169+
*/
170+
void splitStringInto(const std::string& s, char sep, std::vector<std::string>* out);
171+
172+
/*!
173+
\brief Joins 'parts' with 'sep' and appends the result to 'out'.
174+
*/
175+
void joinStringInto(const std::vector<std::string>& parts, char sep, std::string* out);
176+
177+
/*!
178+
\brief Defaults to true only on Windows. May be changed for unit testing.
179+
*/
180+
extern TMXLITE_CONSTINIT bool enableWindowsPathHandling;
181+
182+
/*!
183+
\brief Returns whether 'path' is absolute, and optionally writes its
184+
prefix (e.g. '/' or 'c:\\') in 'prefix'.
185+
*/
186+
bool isAbsoluteFilePath(const std::string& path, std::string* prefix = nullptr);
187+
188+
/*!
189+
\brief Returns a path that refers to what 'path' would refer to if
190+
it were evaluated in 'workingDir'. Simplifies away '.' and '..'
191+
in most cases where it's possible.
192+
Both 'path' and 'workingDir' may be relative or absolute.
193+
*/
194+
std::string resolveFilePath(std::string path, std::string workingDir);
201195

202196
static inline std::string getFilePath(const std::string& path)
203197
{
@@ -216,11 +210,17 @@ namespace tmx
216210
};
217211

218212

219-
#ifdef _WIN32 //try windows formatted paths first
220-
std::string retVal = searchFunc('\\', path);
221-
if (!retVal.empty()) return retVal;
222-
#endif
213+
if (enableWindowsPathHandling)
214+
{
215+
std::string retVal = searchFunc('\\', path);
216+
if (!retVal.empty()) return retVal;
217+
}
223218

224219
return searchFunc('/', path);
225220
}
221+
222+
/*!
223+
\brief Appends the contents of a file into the given string.
224+
*/
225+
bool readFileIntoString(const std::string& path, std::string* out);
226226
} //namespacec tmx

tmxlite/include/tmxlite/Tileset.hpp

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ namespace tmx
5252
class TMXLITE_EXPORT_API Tileset final
5353
{
5454
public:
55-
explicit Tileset(const std::string& workingDir);
55+
explicit Tileset(const std::string& workingDir = "");
5656

5757
/*!
5858
\brief Any tiles within a tile set which have special
@@ -130,12 +130,28 @@ namespace tmx
130130
BottomRight
131131
};
132132

133+
/**
134+
\brief Loads the tilemap from the given location.
135+
This does not set the first GID.
136+
This does not support templates.
137+
Usually tilemaps are loaded automatically as part of a Map instead.
138+
*/
139+
bool loadWithoutMap(const std::string& path);
140+
141+
/**
142+
\brief Loads the tilemap from the given XML string.
143+
This does not set the first GID.
144+
This does not support templates.
145+
Usually tilemaps are loaded automatically as part of a Map instead.
146+
*/
147+
bool loadWithoutMapFromString(const std::string& xmlStr);
148+
133149
/*!
134-
\brief Attempts to parse the given xml node.
135-
If node parsing fails an error is printed in the console
150+
\brief Attempts to parse the given xml node as part of a map.
151+
If node parsing fails, an error is printed in the console
136152
and the Tileset remains in an uninitialised state.
137153
*/
138-
void parse(pugi::xml_node, Map*);
154+
bool parse(pugi::xml_node, Map*);
139155

140156
/*!
141157
\brief Returns the first GID of this tile set.
@@ -144,6 +160,12 @@ namespace tmx
144160
*/
145161
std::uint32_t getFirstGID() const { return m_firstGID; }
146162

163+
/*!
164+
\brief Sets the first GID of this tile set.
165+
This is set automatically if the tileset is loaded as part of a Map.
166+
*/
167+
void setFirstGID(std::uint32_t firstGID) { m_firstGID = firstGID; }
168+
147169
/*!
148170
\brief Returns the last GID of this tile set.
149171
This is the ID of the last tile in the tile set.
@@ -284,7 +306,9 @@ namespace tmx
284306
std::vector<std::uint32_t> m_tileIndex;
285307
std::vector<Tile> m_tiles;
286308

287-
void reset();
309+
//always returns false so we can return this
310+
//on load failure
311+
bool reset();
288312

289313
void parseOffsetNode(const pugi::xml_node&);
290314
void parsePropertyNode(const pugi::xml_node&);

0 commit comments

Comments
 (0)