diff --git a/CMakeLists.txt b/CMakeLists.txt index db8b519db..ef980c629 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -819,6 +819,7 @@ set_glob(TOOLS GLOB src/tools fake_server.cpp map_diff.cpp map_extract.cpp + map_replace_image.cpp map_resave.cpp map_version.cpp packetgen.cpp @@ -833,7 +834,7 @@ foreach(ABS_T ${TOOLS}) if(T MATCHES "\\.cpp$") string(REGEX REPLACE "\\.cpp$" "" TOOL "${T}") set(EXTRA_TOOL_SRC) - if(TOOL MATCHES "^(tileset_|dilate|map_extract$)") + if(TOOL MATCHES "^(tileset_|dilate|map_extract|map_replace_image$)") list(APPEND EXTRA_TOOL_SRC ${DEP_PNG}) endif() if(TOOL MATCHES "^config_") diff --git a/src/engine/shared/datafile.cpp b/src/engine/shared/datafile.cpp index d48b3f1ad..61ed34db6 100644 --- a/src/engine/shared/datafile.cpp +++ b/src/engine/shared/datafile.cpp @@ -356,10 +356,11 @@ void CDataFileReader::UnloadData(int Index) int CDataFileReader::GetItemSize(int Index) { - if(!m_pDataFile) { return 0; } + if(!m_pDataFile) + return 0; if(Index == m_pDataFile->m_Header.m_NumItems-1) - return m_pDataFile->m_Header.m_ItemSize-m_pDataFile->m_Info.m_pItemOffsets[Index]; - return m_pDataFile->m_Info.m_pItemOffsets[Index+1]-m_pDataFile->m_Info.m_pItemOffsets[Index]; + return m_pDataFile->m_Header.m_ItemSize-m_pDataFile->m_Info.m_pItemOffsets[Index] - sizeof(CDatafileItem); + return m_pDataFile->m_Info.m_pItemOffsets[Index+1]-m_pDataFile->m_Info.m_pItemOffsets[Index] - sizeof(CDatafileItem); } void *CDataFileReader::GetItem(int Index, int *pType, int *pID) diff --git a/src/game/editor/io.cpp b/src/game/editor/io.cpp index b798acc40..ab52d59c8 100644 --- a/src/game/editor/io.cpp +++ b/src/game/editor/io.cpp @@ -612,7 +612,7 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag DataFile.GetType(MAPITEMTYPE_INFO, &Start, &Num); for(int i = Start; i < Start + Num; i++) { - int ItemSize = DataFile.GetItemSize(Start) - 8; + int ItemSize = DataFile.GetItemSize(Start); int ItemID; CMapItemInfoSettings *pItem = (CMapItemInfoSettings *)DataFile.GetItem(i, 0, &ItemID); if(!pItem || ItemID != 0) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 289fe51c7..b83c27eb9 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -2708,8 +2708,7 @@ void CGameContext::OnMapChange(char *pNewMapName, int MapNameSize) int TypeID; int ItemID; int *pData = (int *)Reader.GetItem(i, &TypeID, &ItemID); - // GetItemSize returns item size including header, remove that. - int Size = Reader.GetItemSize(i) - sizeof(int) * 2; + int Size = Reader.GetItemSize(i); CMapItemInfoSettings MapInfo; if(TypeID == MAPITEMTYPE_INFO && ItemID == 0) { @@ -2804,7 +2803,7 @@ void CGameContext::LoadMapSettings() { int ItemID; CMapItemInfoSettings *pItem = (CMapItemInfoSettings *)pMap->GetItem(i, 0, &ItemID); - int ItemSize = pMap->GetItemSize(i) - 8; + int ItemSize = pMap->GetItemSize(i); if(!pItem || ItemID != 0) continue; diff --git a/src/tools/config_retrieve.cpp b/src/tools/config_retrieve.cpp index abdbe68aa..76c4eace0 100644 --- a/src/tools/config_retrieve.cpp +++ b/src/tools/config_retrieve.cpp @@ -18,7 +18,7 @@ void Process(IStorage *pStorage, const char *pMapName, const char *pConfigName) { int ItemID; CMapItemInfoSettings *pItem = (CMapItemInfoSettings *)Map.GetItem(i, 0, &ItemID); - int ItemSize = Map.GetItemSize(i) - 8; + int ItemSize = Map.GetItemSize(i); if(!pItem || ItemID != 0) continue; diff --git a/src/tools/config_store.cpp b/src/tools/config_store.cpp index be88e7caa..e80d06e72 100644 --- a/src/tools/config_store.cpp +++ b/src/tools/config_store.cpp @@ -54,8 +54,7 @@ void Process(IStorage *pStorage, const char *pMapName, const char *pConfigName) int TypeID; int ItemID; int *pData = (int *)Reader.GetItem(i, &TypeID, &ItemID); - // GetItemSize returns item size including header, remove that. - int Size = Reader.GetItemSize(i) - sizeof(int) * 2; + int Size = Reader.GetItemSize(i); CMapItemInfoSettings MapInfo; if(TypeID == MAPITEMTYPE_INFO && ItemID == 0) { diff --git a/src/tools/map_replace_image.cpp b/src/tools/map_replace_image.cpp new file mode 100644 index 000000000..4361a18f4 --- /dev/null +++ b/src/tools/map_replace_image.cpp @@ -0,0 +1,165 @@ +/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a +complete release at teeworlds.com. */ + +#include +#include +#include +#include +#include +#include +#include + +/* + Usage: map_replace_image + Notes: map filepath must be relative to user default teeworlds folder + new image filepath must be absolute or relative to the current position +*/ + +// global datafile reader and writer +CDataFileReader DataFile; +CDataFileWriter df; + +// global new image data (set by ReplaceImageItem) +int NewNameID = -1; +char aNewName[128]; +int NewDataID = -1; +int NewDataSize = 0; +void* pNewData = 0; + +int LoadPNG(CImageInfo* pImg, const char *pFilename) +{ + unsigned char *pBuffer; + png_t Png; + + png_init(0,0); + int Error = png_open_file(&Png, pFilename); + if(Error != PNG_NO_ERROR) + { + dbg_msg("game/png", "failed to open file. filename='%s'", pFilename); + if(Error != PNG_FILE_ERROR) + png_close_file(&Png); + return 0; + } + + if(Png.depth != 8 || (Png.color_type != PNG_TRUECOLOR && Png.color_type != PNG_TRUECOLOR_ALPHA) || Png.width > (2<<12) || Png.height > (2<<12)) + { + dbg_msg("game/png", "invalid format. filename='%s'", pFilename); + png_close_file(&Png); + return 0; + } + + pBuffer = (unsigned char *)mem_alloc(Png.width * Png.height * Png.bpp, 1); + png_get_data(&Png, pBuffer); + png_close_file(&Png); + + pImg->m_Width = Png.width; + pImg->m_Height = Png.height; + if(Png.color_type == PNG_TRUECOLOR) + pImg->m_Format = CImageInfo::FORMAT_RGB; + else if(Png.color_type == PNG_TRUECOLOR_ALPHA) + pImg->m_Format = CImageInfo::FORMAT_RGBA; + pImg->m_pData = pBuffer; + return 1; +} + +void ExtractName(const char *pFileName, char *pName, int BufferSize) +{ + const char *pExtractedName = pFileName; + const char *pEnd = 0; + for(; *pFileName; ++pFileName) + { + if(*pFileName == '/' || *pFileName == '\\') + pExtractedName = pFileName+1; + else if(*pFileName == '.') + pEnd = pFileName; + } + + int Length = pEnd > pExtractedName ? min(BufferSize, (int)(pEnd-pExtractedName+1)) : BufferSize; + str_copy(pName, pExtractedName, Length); +} + +void* ReplaceImageItem(void *pItem, int Type, char *aImgName, char *aImgFile) +{ + if(Type != MAPITEMTYPE_IMAGE) + return pItem; + + CMapItemImage *pImgItem = (CMapItemImage*) pItem; + char *pName = (char *)DataFile.GetData(pImgItem->m_ImageName); + + if(str_comp(aImgName, pName)) + return pItem; + + CImageInfo* pImgInfo = new CImageInfo; + if(!LoadPNG(pImgInfo, aImgFile)) + return pItem; + + CMapItemImage *pNewImgItem = new CMapItemImage; + *pNewImgItem = *pImgItem; + + pNewImgItem->m_Width = pImgInfo->m_Width; + pNewImgItem->m_Height = pImgInfo->m_Height; + int PixelSize = pImgInfo->m_Format == CImageInfo::FORMAT_RGB ? 3 : 4; + + NewNameID = pImgItem->m_ImageName; + ExtractName(aImgFile, aNewName, sizeof(aNewName)); + NewDataID = pImgItem->m_ImageData; + pNewData = pImgInfo->m_pData; + NewDataSize = pImgInfo->m_Width*pImgInfo->m_Height*PixelSize; + + return (void *)pNewImgItem; +} + +int main(int argc, const char **argv) +{ + IStorage *pStorage = CreateStorage("Teeworlds", IStorage::STORAGETYPE_BASIC, argc, argv); + int Index, ID = 0, Type = 0, Size; + void *pPtr; + char aFileName[1024], aImageName[1024], aImageFile[1024]; + + if(!pStorage || argc != 5) { + return -1; + } + + str_format(aFileName, sizeof(aFileName), "%s", argv[2]); + str_format(aImageName, sizeof(aImageName), "%s", argv[3]); + str_format(aImageFile, sizeof(aImageFile), "%s", argv[4]); + + if(!DataFile.Open(pStorage, argv[1], IStorage::TYPE_ALL)) { + return -1; + } + if(!df.Open(pStorage, aFileName)) { + return -1; + } + + // add all items + for(Index = 0; Index < DataFile.NumItems(); Index++) + { + pPtr = DataFile.GetItem(Index, &Type, &ID); + Size = DataFile.GetItemSize(Index); + pPtr = ReplaceImageItem(pPtr, Type, aImageName, aImageFile); + df.AddItem(Type, ID, Size, pPtr); + } + + // add all data + for(Index = 0; Index < DataFile.NumItems(); Index++) + { + if(Index == NewDataID) { + pPtr = pNewData; + Size = NewDataSize; + } + else if (Index == NewNameID) { + pPtr = (void *)aNewName; + Size = str_length(aNewName)+1; + } + else { + pPtr = DataFile.GetData(Index); + Size = DataFile.GetUncompressedDataSize(Index); + } + + df.AddData(Size, pPtr); + } + + DataFile.Close(); + df.Finish(); + return 0; +} diff --git a/src/tools/map_resave.cpp b/src/tools/map_resave.cpp index d4303506e..0882242c7 100644 --- a/src/tools/map_resave.cpp +++ b/src/tools/map_resave.cpp @@ -35,7 +35,7 @@ int main(int argc, const char **argv) for(Index = 0; Index < DataFile.NumData(); Index++) { pPtr = DataFile.GetData(Index); - Size = DataFile.GetDataSize(Index); + Size = DataFile.GetUncompressedDataSize(Index); df.AddData(Size, pPtr); }