1#include <base/logger.h>
2#include <base/system.h>
3#include <engine/shared/datafile.h>
4#include <engine/storage.h>
5#include <game/gamecore.h>
6#include <game/mapitems.h>
7
8bool Process(IStorage *pStorage, const char **pMapNames)
9{
10 CDataFileReader aMaps[2];
11
12 for(int i = 0; i < 2; ++i)
13 {
14 if(!aMaps[i].Open(pStorage, pFilename: pMapNames[i], StorageType: IStorage::TYPE_ABSOLUTE))
15 {
16 dbg_msg(sys: "map_compare", fmt: "error opening map '%s'", pMapNames[i]);
17 return false;
18 }
19
20 const CMapItemVersion *pVersion = static_cast<CMapItemVersion *>(aMaps[i].FindItem(Type: MAPITEMTYPE_VERSION, Id: 0));
21 if(pVersion == nullptr || pVersion->m_Version != CMapItemVersion::CURRENT_VERSION)
22 {
23 dbg_msg(sys: "map_compare", fmt: "unsupported map version '%s'", pMapNames[i]);
24 return false;
25 }
26 }
27
28 int aStart[2], aNum[2];
29 for(int i = 0; i < 2; ++i)
30 aMaps[i].GetType(Type: MAPITEMTYPE_LAYER, pStart: &aStart[i], pNum: &aNum[i]);
31
32 // ensure basic layout
33 if(aNum[0] != aNum[1])
34 {
35 dbg_msg(sys: "map_compare", fmt: "different layer numbers:");
36 for(int i = 0; i < 2; ++i)
37 dbg_msg(sys: "map_compare", fmt: " \"%s\": %d layers", pMapNames[i], aNum[i]);
38 return false;
39 }
40
41 // preload data
42 for(int j = 0; j < aNum[0]; ++j)
43 {
44 for(int i = 0; i < 2; ++i)
45 {
46 CMapItemLayer *pItem = (CMapItemLayer *)aMaps[i].GetItem(Index: aStart[i] + j);
47 if(pItem->m_Type == LAYERTYPE_TILES)
48 (void)aMaps[i].GetData(Index: ((CMapItemLayerTilemap *)pItem)->m_Data);
49 }
50 }
51
52 // compare
53 for(int j = 0; j < aNum[0]; ++j)
54 {
55 CMapItemLayer *apItem[2];
56 for(int i = 0; i < 2; ++i)
57 apItem[i] = (CMapItemLayer *)aMaps[i].GetItem(Index: aStart[i] + j);
58
59 if(apItem[0]->m_Type != LAYERTYPE_TILES || apItem[1]->m_Type != LAYERTYPE_TILES)
60 continue;
61
62 CMapItemLayerTilemap *apTilemap[2];
63 char aaName[2][12];
64
65 for(int i = 0; i < 2; ++i)
66 {
67 apTilemap[i] = (CMapItemLayerTilemap *)apItem[i];
68 IntsToStr(pInts: apTilemap[i]->m_aName, NumInts: std::size(apTilemap[i]->m_aName), pStr: aaName[i], StrSize: std::size(aaName[i]));
69 }
70
71 if(str_comp(a: aaName[0], b: aaName[1]) != 0 || apTilemap[0]->m_Width != apTilemap[1]->m_Width || apTilemap[0]->m_Height != apTilemap[1]->m_Height)
72 {
73 dbg_msg(sys: "map_compare", fmt: "different tile layers:");
74 for(int i = 0; i < 2; ++i)
75 dbg_msg(sys: "map_compare", fmt: " \"%s\" (%dx%d)", aaName[i], apTilemap[i]->m_Width, apTilemap[i]->m_Height);
76 return false;
77 }
78 CTile *apTile[2];
79 for(int i = 0; i < 2; ++i)
80 apTile[i] = (CTile *)aMaps[i].GetData(Index: apTilemap[i]->m_Data);
81
82 for(int y = 0; y < apTilemap[0]->m_Height; y++)
83 {
84 for(int x = 0; x < apTilemap[0]->m_Width; x++)
85 {
86 int Pos = y * apTilemap[0]->m_Width + x;
87 if(apTile[0][Pos].m_Index != apTile[1][Pos].m_Index || apTile[0][Pos].m_Flags != apTile[1][Pos].m_Flags)
88 {
89 dbg_msg(sys: "map_compare", fmt: "[%d:%s] %dx%d: (index: %d, flags: %d) != (index: %d, flags: %d)", aNum[0], aaName[0], x, y, apTile[0][Pos].m_Index, apTile[0][Pos].m_Flags, apTile[1][Pos].m_Index, apTile[0][Pos].m_Flags);
90 }
91 }
92 }
93 }
94
95 return true;
96}
97
98int main(int argc, const char *argv[])
99{
100 CCmdlineFix CmdlineFix(&argc, &argv);
101 std::vector<std::shared_ptr<ILogger>> vpLoggers;
102 std::shared_ptr<ILogger> pStdoutLogger = std::shared_ptr<ILogger>(log_logger_stdout());
103 if(pStdoutLogger)
104 {
105 vpLoggers.push_back(x: pStdoutLogger);
106 }
107 IOHANDLE LogFile = io_open(filename: "map_diff.txt", flags: IOFLAG_WRITE);
108 if(LogFile)
109 {
110 vpLoggers.push_back(x: std::shared_ptr<ILogger>(log_logger_file(file: LogFile)));
111 }
112 log_set_global_logger(logger: log_logger_collection(vpLoggers: std::move(vpLoggers)).release());
113
114 if(argc != 3)
115 {
116 dbg_msg(sys: "usage", fmt: "%s map1 map2", argv[0]);
117 return -1;
118 }
119
120 IStorage *pStorage = CreateLocalStorage();
121 if(!pStorage)
122 return -1;
123
124 return Process(pStorage, pMapNames: &argv[1]) ? 0 : 1;
125}
126