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