2017-06-12 19:00:13 +00:00
|
|
|
// Adapted from TWMapImagesRecovery by Tardo: https://github.com/Tardo/TWMapImagesRecovery
|
2022-04-22 23:04:48 +00:00
|
|
|
#include <base/logger.h>
|
2017-06-12 19:00:13 +00:00
|
|
|
#include <base/system.h>
|
2022-06-21 13:26:23 +00:00
|
|
|
#include <engine/gfx/image_loader.h>
|
2023-04-27 15:13:35 +00:00
|
|
|
#include <engine/graphics.h>
|
2017-06-12 19:00:13 +00:00
|
|
|
#include <engine/shared/datafile.h>
|
|
|
|
#include <engine/storage.h>
|
2020-09-26 19:41:58 +00:00
|
|
|
#include <game/mapitems.h>
|
2017-06-12 19:00:13 +00:00
|
|
|
|
|
|
|
bool Process(IStorage *pStorage, const char *pMapName, const char *pPathSave)
|
|
|
|
{
|
2022-06-04 08:13:35 +00:00
|
|
|
CDataFileReader Reader;
|
|
|
|
if(!Reader.Open(pStorage, pMapName, IStorage::TYPE_ABSOLUTE))
|
2017-06-12 19:00:13 +00:00
|
|
|
{
|
|
|
|
dbg_msg("map_extract", "error opening map '%s'", pMapName);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-10-12 18:54:17 +00:00
|
|
|
const CMapItemVersion *pVersion = static_cast<CMapItemVersion *>(Reader.FindItem(MAPITEMTYPE_VERSION, 0));
|
|
|
|
if(pVersion == nullptr || pVersion->m_Version != CMapItemVersion::CURRENT_VERSION)
|
|
|
|
{
|
|
|
|
dbg_msg("map_extract", "unsupported map version '%s'", pMapName);
|
2017-06-12 19:00:13 +00:00
|
|
|
return false;
|
2023-10-12 18:54:17 +00:00
|
|
|
}
|
2017-06-12 19:00:13 +00:00
|
|
|
|
|
|
|
dbg_msg("map_extract", "Make sure you have the permission to use these images and sounds in your own maps");
|
|
|
|
|
2022-06-04 08:13:35 +00:00
|
|
|
CMapItemInfo *pInfo = (CMapItemInfo *)Reader.FindItem(MAPITEMTYPE_INFO, 0);
|
2017-06-12 19:00:13 +00:00
|
|
|
|
2020-06-20 22:57:11 +00:00
|
|
|
if(pInfo)
|
|
|
|
{
|
2023-08-13 09:50:35 +00:00
|
|
|
const char *pAuthor = Reader.GetDataString(pInfo->m_Author);
|
|
|
|
dbg_msg("map_extract", "author: %s", pAuthor == nullptr ? "(error)" : pAuthor);
|
|
|
|
const char *pMapVersion = Reader.GetDataString(pInfo->m_MapVersion);
|
|
|
|
dbg_msg("map_extract", "version: %s", pMapVersion == nullptr ? "(error)" : pMapVersion);
|
|
|
|
const char *pCredits = Reader.GetDataString(pInfo->m_Credits);
|
|
|
|
dbg_msg("map_extract", "credits: %s", pCredits == nullptr ? "(error)" : pCredits);
|
|
|
|
const char *pLicense = Reader.GetDataString(pInfo->m_License);
|
|
|
|
dbg_msg("map_extract", "license: %s", pLicense == nullptr ? "(error)" : pLicense);
|
2020-06-20 22:57:11 +00:00
|
|
|
}
|
2017-06-12 19:00:13 +00:00
|
|
|
|
|
|
|
int Start, Num;
|
|
|
|
|
|
|
|
// load images
|
2022-06-04 08:13:35 +00:00
|
|
|
Reader.GetType(MAPITEMTYPE_IMAGE, &Start, &Num);
|
2017-06-12 19:00:13 +00:00
|
|
|
|
|
|
|
for(int i = 0; i < Num; i++)
|
|
|
|
{
|
2023-04-27 15:13:35 +00:00
|
|
|
CMapItemImage_v2 *pItem = (CMapItemImage_v2 *)Reader.GetItem(Start + i);
|
2017-06-12 19:00:13 +00:00
|
|
|
if(pItem->m_External)
|
|
|
|
continue;
|
|
|
|
|
2023-08-13 09:50:35 +00:00
|
|
|
const char *pName = Reader.GetDataString(pItem->m_ImageName);
|
|
|
|
if(pName == nullptr || pName[0] == '\0')
|
|
|
|
{
|
|
|
|
dbg_msg("map_extract", "failed to load name of image %d", i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
char aBuf[IO_MAX_PATH_LENGTH];
|
2017-06-12 19:00:13 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%s/%s.png", pPathSave, pName);
|
|
|
|
dbg_msg("map_extract", "writing image: %s (%dx%d)", aBuf, pItem->m_Width, pItem->m_Height);
|
|
|
|
|
2023-04-27 15:13:35 +00:00
|
|
|
const int Format = pItem->m_Version < CMapItemImage_v2::CURRENT_VERSION ? CImageInfo::FORMAT_RGBA : pItem->m_Format;
|
|
|
|
EImageFormat OutputFormat;
|
|
|
|
if(Format == CImageInfo::FORMAT_RGBA)
|
|
|
|
OutputFormat = IMAGE_FORMAT_RGBA;
|
|
|
|
else if(Format == CImageInfo::FORMAT_RGB)
|
|
|
|
OutputFormat = IMAGE_FORMAT_RGB;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_msg("map_extract", "ignoring image '%s' with unknown format %d", aBuf, Format);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-06-12 19:00:13 +00:00
|
|
|
// copy image data
|
2021-11-13 20:07:03 +00:00
|
|
|
IOHANDLE File = io_open(aBuf, IOFLAG_WRITE);
|
2022-06-11 07:09:42 +00:00
|
|
|
if(File)
|
2021-11-05 16:21:41 +00:00
|
|
|
{
|
2022-06-11 07:09:42 +00:00
|
|
|
TImageByteBuffer ByteBuffer;
|
|
|
|
SImageByteBuffer ImageByteBuffer(&ByteBuffer);
|
|
|
|
|
2023-04-27 15:13:35 +00:00
|
|
|
if(SavePNG(OutputFormat, (const uint8_t *)Reader.GetData(pItem->m_ImageData), ImageByteBuffer, pItem->m_Width, pItem->m_Height))
|
2022-06-11 07:09:42 +00:00
|
|
|
io_write(File, &ByteBuffer.front(), ByteBuffer.size());
|
|
|
|
io_close(File);
|
2021-11-05 16:21:41 +00:00
|
|
|
}
|
2017-06-12 19:00:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// load sounds
|
2022-06-04 08:13:35 +00:00
|
|
|
Reader.GetType(MAPITEMTYPE_SOUND, &Start, &Num);
|
2017-06-12 19:00:13 +00:00
|
|
|
|
|
|
|
for(int i = 0; i < Num; i++)
|
|
|
|
{
|
2023-02-20 22:38:54 +00:00
|
|
|
CMapItemSound *pItem = (CMapItemSound *)Reader.GetItem(Start + i);
|
2017-06-12 19:00:13 +00:00
|
|
|
if(pItem->m_External)
|
|
|
|
continue;
|
|
|
|
|
2023-08-13 09:50:35 +00:00
|
|
|
const char *pName = Reader.GetDataString(pItem->m_SoundName);
|
|
|
|
if(pName == nullptr || pName[0] == '\0')
|
|
|
|
{
|
|
|
|
dbg_msg("map_extract", "failed to load name of sound %d", i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
char aBuf[IO_MAX_PATH_LENGTH];
|
2017-06-12 19:00:13 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%s/%s.opus", pPathSave, pName);
|
|
|
|
dbg_msg("map_extract", "writing sound: %s (%d B)", aBuf, pItem->m_SoundDataSize);
|
|
|
|
|
|
|
|
IOHANDLE Opus = io_open(aBuf, IOFLAG_WRITE);
|
2022-06-04 08:13:35 +00:00
|
|
|
io_write(Opus, (unsigned char *)Reader.GetData(pItem->m_SoundData), pItem->m_SoundDataSize);
|
2017-06-12 19:00:13 +00:00
|
|
|
io_close(Opus);
|
|
|
|
}
|
|
|
|
|
2022-06-04 08:13:35 +00:00
|
|
|
return Reader.Close();
|
2017-06-12 19:00:13 +00:00
|
|
|
}
|
|
|
|
|
2021-11-21 17:16:14 +00:00
|
|
|
int main(int argc, const char *argv[])
|
2017-06-12 19:00:13 +00:00
|
|
|
{
|
2022-06-13 16:07:29 +00:00
|
|
|
CCmdlineFix CmdlineFix(&argc, &argv);
|
2022-04-22 23:04:48 +00:00
|
|
|
log_set_global_logger_default();
|
2017-06-12 19:00:13 +00:00
|
|
|
|
|
|
|
IStorage *pStorage = CreateLocalStorage();
|
2022-06-04 08:13:35 +00:00
|
|
|
if(!pStorage)
|
|
|
|
return -1;
|
2017-06-12 19:00:13 +00:00
|
|
|
|
2022-06-04 08:13:35 +00:00
|
|
|
const char *pDir;
|
2020-09-26 19:41:58 +00:00
|
|
|
if(argc == 2)
|
2017-06-12 19:00:13 +00:00
|
|
|
{
|
2022-06-04 08:13:35 +00:00
|
|
|
pDir = ".";
|
2017-06-12 19:00:13 +00:00
|
|
|
}
|
2020-09-26 19:41:58 +00:00
|
|
|
else if(argc == 3)
|
2017-06-12 19:00:13 +00:00
|
|
|
{
|
2022-06-04 08:13:35 +00:00
|
|
|
pDir = argv[2];
|
2017-06-12 19:00:13 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-06-12 19:18:21 +00:00
|
|
|
dbg_msg("usage", "%s map [directory]", argv[0]);
|
2017-06-12 19:00:13 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-06-04 08:13:35 +00:00
|
|
|
if(!fs_is_dir(pDir))
|
2017-06-12 19:00:13 +00:00
|
|
|
{
|
2022-06-04 08:13:35 +00:00
|
|
|
dbg_msg("usage", "directory '%s' does not exist", pDir);
|
2017-06-12 19:00:13 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-06-04 08:13:35 +00:00
|
|
|
int Result = Process(pStorage, argv[1], pDir) ? 0 : 1;
|
2021-11-21 17:16:14 +00:00
|
|
|
return Result;
|
2017-06-12 19:00:13 +00:00
|
|
|
}
|