1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include "map.h"
4
5#include <base/log.h>
6#include <base/system.h>
7
8#include <engine/storage.h>
9
10#include <game/mapitems.h>
11
12CMap::CMap() = default;
13
14CMap::~CMap()
15{
16 Unload();
17}
18
19int CMap::GetDataSize(int Index) const
20{
21 return m_DataFile.GetDataSize(Index);
22}
23
24void *CMap::GetData(int Index)
25{
26 return m_DataFile.GetData(Index);
27}
28
29void *CMap::GetDataSwapped(int Index)
30{
31 return m_DataFile.GetDataSwapped(Index);
32}
33
34const char *CMap::GetDataString(int Index)
35{
36 return m_DataFile.GetDataString(Index);
37}
38
39void CMap::UnloadData(int Index)
40{
41 m_DataFile.UnloadData(Index);
42}
43
44int CMap::NumData() const
45{
46 return m_DataFile.NumData();
47}
48
49int CMap::GetItemSize(int Index)
50{
51 return m_DataFile.GetItemSize(Index);
52}
53
54void *CMap::GetItem(int Index, int *pType, int *pId)
55{
56 return m_DataFile.GetItem(Index, pType, pId);
57}
58
59void CMap::GetType(int Type, int *pStart, int *pNum)
60{
61 m_DataFile.GetType(Type, pStart, pNum);
62}
63
64int CMap::FindItemIndex(int Type, int Id)
65{
66 return m_DataFile.FindItemIndex(Type, Id);
67}
68
69void *CMap::FindItem(int Type, int Id)
70{
71 return m_DataFile.FindItem(Type, Id);
72}
73
74int CMap::NumItems() const
75{
76 return m_DataFile.NumItems();
77}
78
79bool CMap::Load(const char *pFullName, IStorage *pStorage, const char *pPath, int StorageType)
80{
81 // Ensure current datafile is not left in an inconsistent state if loading fails,
82 // by loading the new datafile separately first.
83 CDataFileReader NewDataFile;
84 if(!NewDataFile.Open(pFullName, pStorage, pPath, StorageType))
85 return false;
86
87 // Check version
88 const CMapItemVersion *pItem = (CMapItemVersion *)NewDataFile.FindItem(Type: MAPITEMTYPE_VERSION, Id: 0);
89 if(pItem == nullptr || pItem->m_Version != 1)
90 {
91 log_error("map/load", "Error: map version not supported.");
92 NewDataFile.Close();
93 return false;
94 }
95
96 // Replace compressed tile layers with uncompressed ones
97 int GroupsStart, GroupsNum, LayersStart, LayersNum;
98 NewDataFile.GetType(Type: MAPITEMTYPE_GROUP, pStart: &GroupsStart, pNum: &GroupsNum);
99 NewDataFile.GetType(Type: MAPITEMTYPE_LAYER, pStart: &LayersStart, pNum: &LayersNum);
100 for(int g = 0; g < GroupsNum; g++)
101 {
102 const CMapItemGroup *pGroup = static_cast<CMapItemGroup *>(NewDataFile.GetItem(Index: GroupsStart + g));
103 for(int l = 0; l < pGroup->m_NumLayers; l++)
104 {
105 CMapItemLayer *pLayer = static_cast<CMapItemLayer *>(NewDataFile.GetItem(Index: LayersStart + pGroup->m_StartLayer + l));
106 if(pLayer->m_Type == LAYERTYPE_TILES)
107 {
108 CMapItemLayerTilemap *pTilemap = reinterpret_cast<CMapItemLayerTilemap *>(pLayer);
109 if(pTilemap->m_Version >= CMapItemLayerTilemap::VERSION_TEEWORLDS_TILESKIP)
110 {
111 const size_t TilemapCount = (size_t)pTilemap->m_Width * pTilemap->m_Height;
112 const size_t TilemapSize = TilemapCount * sizeof(CTile);
113
114 if(((int)TilemapCount / pTilemap->m_Width != pTilemap->m_Height) || (TilemapSize / sizeof(CTile) != TilemapCount))
115 {
116 log_error("map/load", "map layer too big (%d * %d * %d causes an integer overflow)", pTilemap->m_Width, pTilemap->m_Height, (int)sizeof(CTile));
117 return false;
118 }
119 CTile *pTiles = static_cast<CTile *>(malloc(size: TilemapSize));
120 if(!pTiles)
121 return false;
122 ExtractTiles(pDest: pTiles, DestSize: (size_t)pTilemap->m_Width * pTilemap->m_Height, pSrc: static_cast<CTile *>(NewDataFile.GetData(Index: pTilemap->m_Data)), SrcSize: NewDataFile.GetDataSize(Index: pTilemap->m_Data) / sizeof(CTile));
123 NewDataFile.ReplaceData(Index: pTilemap->m_Data, pData: reinterpret_cast<char *>(pTiles), Size: TilemapSize);
124 }
125 }
126 }
127 }
128
129 // Replace existing datafile with new datafile
130 m_DataFile.Close();
131 m_DataFile = std::move(NewDataFile);
132 return true;
133}
134
135bool CMap::Load(IStorage *pStorage, const char *pPath, int StorageType)
136{
137 char aFilename[IO_MAX_PATH_LENGTH];
138 fs_split_file_extension(filename: fs_filename(path: pPath), name: aFilename, name_size: sizeof(aFilename));
139 return Load(pFullName: aFilename, pStorage, pPath, StorageType);
140}
141
142void CMap::Unload()
143{
144 m_DataFile.Close();
145}
146
147bool CMap::IsLoaded() const
148{
149 return m_DataFile.IsOpen();
150}
151
152IOHANDLE CMap::File() const
153{
154 return m_DataFile.File();
155}
156
157const char *CMap::FullName() const
158{
159 return m_DataFile.FullName();
160}
161
162const char *CMap::BaseName() const
163{
164 return m_DataFile.BaseName();
165}
166
167const char *CMap::Path() const
168{
169 return m_DataFile.Path();
170}
171
172SHA256_DIGEST CMap::Sha256() const
173{
174 return m_DataFile.Sha256();
175}
176
177unsigned CMap::Crc() const
178{
179 return m_DataFile.Crc();
180}
181
182int CMap::Size() const
183{
184 return m_DataFile.Size();
185}
186
187void CMap::ExtractTiles(CTile *pDest, size_t DestSize, const CTile *pSrc, size_t SrcSize)
188{
189 size_t DestIndex = 0;
190 size_t SrcIndex = 0;
191 while(DestIndex < DestSize && SrcIndex < SrcSize)
192 {
193 for(unsigned Counter = 0; Counter <= pSrc[SrcIndex].m_Skip && DestIndex < DestSize; Counter++)
194 {
195 pDest[DestIndex].m_Index = pSrc[SrcIndex].m_Index;
196 pDest[DestIndex].m_Flags = pSrc[SrcIndex].m_Flags;
197 pDest[DestIndex].m_Skip = 0;
198 pDest[DestIndex].m_Reserved = 0;
199 DestIndex++;
200 }
201 SrcIndex++;
202 }
203}
204
205extern std::unique_ptr<IMap> CreateMap()
206{
207 return std::make_unique<CMap>();
208}
209