Skip to content

Commit

Permalink
Export minimap as .otmm (otclient format) or .png (#413)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mignari authored Mar 27, 2023
1 parent 2057c52 commit fd77aa4
Show file tree
Hide file tree
Showing 16 changed files with 601 additions and 162 deletions.
65 changes: 23 additions & 42 deletions source/common_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
#include "common_windows.h"
#include "positionctrl.h"

#include "iominimap.h"

#ifdef _MSC_VER
#pragma warning(disable:4018) // signed/unsigned mismatch
#endif
Expand Down Expand Up @@ -491,6 +493,15 @@ ExportMiniMapWindow::ExportMiniMapWindow(wxWindow* parent, Editor& editor) :
tmpsizer->Add(file_name_text_field, 1, wxALL, 5);
sizer->Add(tmpsizer, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5);

// Format options
wxArrayString format_choices;
format_choices.Add(".otmm (Client Minimap)");
format_choices.Add(".png (PNG Image)");
format_choices.Add(".bmp (Bitmap Image)");
format_options = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, format_choices);
format_options->SetSelection(0);
tmpsizer->Add(format_options, 1, wxALL, 5);

// Export options
wxArrayString choices;
choices.Add("All Floors");
Expand Down Expand Up @@ -553,59 +564,29 @@ void ExportMiniMapWindow::OnFileNameChanged(wxKeyEvent& event)

void ExportMiniMapWindow::OnClickOK(wxCommandEvent& WXUNUSED(event))
{
g_gui.CreateLoadBar("Exporting minimap");
g_gui.CreateLoadBar("Exporting minimap...");

try
{
FileName directory(directory_text_field->GetValue());
g_settings.setString(Config::MINIMAP_EXPORT_DIR, directory_text_field->GetValue().ToStdString());

switch(floor_options->GetSelection())
{
case 0: { // All floors
for(int floor = 0; floor < rme::MapLayers; ++floor) {
g_gui.SetLoadScale(int(floor*(100.f/16.f)), int((floor+1)*(100.f/16.f)));
FileName file(file_name_text_field->GetValue() + "_" + i2ws(floor) + ".bmp");
file.Normalize(wxPATH_NORM_ALL, directory.GetFullPath());
editor.exportMiniMap(file, floor, true);
}
break;
}

case 1: { // Ground floor
FileName file(file_name_text_field->GetValue() + "_" + i2ws(rme::MapGroundLayer) + ".bmp");
file.Normalize(wxPATH_NORM_ALL, directory.GetFullPath());
editor.exportMiniMap(file, rme::MapGroundLayer, true);
break;
}
auto format = static_cast<MinimapExportFormat>(format_options->GetSelection());
auto mode = static_cast<MinimapExportMode>(floor_options->GetSelection());
std::string directory = directory_text_field->GetValue().ToStdString();
std::string file_name = file_name_text_field->GetValue().ToStdString();
int floor = floor_number->GetValue();

case 2: { // Specific floors
int floor = floor_number->GetValue();
FileName file(file_name_text_field->GetValue() + "_" + i2ws(floor) + ".bmp");
file.Normalize(wxPATH_NORM_ALL, directory.GetFullPath());
editor.exportMiniMap(file, floor, true);
break;
}
g_settings.setString(Config::MINIMAP_EXPORT_DIR, directory);

case 3: { // Selected area
editor.exportSelectionAsMiniMap(directory, file_name_text_field->GetValue());
break;
}
}
}
catch(std::bad_alloc&)
{
g_gui.PopupDialog("Error", "There is not enough memory available to complete the operation.", wxOK);
IOMinimap io(&editor, format, mode, true);
if (!io.saveMinimap(directory, file_name, floor)) {
g_gui.PopupDialog("Error", io.getError(), wxOK);
}

g_gui.DestroyLoadBar();
EndModal(1);
EndModal(wxID_OK);
}

void ExportMiniMapWindow::OnClickCancel(wxCommandEvent& WXUNUSED(event))
{
// Just close this window
EndModal(0);
EndModal(wxID_CANCEL);
}

void ExportMiniMapWindow::CheckValues()
Expand Down
1 change: 1 addition & 0 deletions source/common_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class ExportMiniMapWindow : public wxDialog

Editor& editor;

wxChoice* format_options;
wxStaticText* error_field;
wxTextCtrl* directory_text_field;
wxTextCtrl* file_name_text_field;
Expand Down
95 changes: 0 additions & 95 deletions source/editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,101 +440,6 @@ bool Editor::importMiniMap(FileName filename, int import, int import_x_offset, i
return false;
}

bool Editor::exportMiniMap(FileName filename, int floor /*= rme::MapGroundLayer*/, bool displaydialog)
{
return map.exportMinimap(filename, floor, displaydialog);
}

bool Editor::exportSelectionAsMiniMap(FileName directory, wxString fileName)
{
if(!directory.Exists() || !directory.IsDirWritable())
return false;

int min_x = rme::MapMaxWidth + 1, min_y = rme::MapMaxHeight + 1, min_z = rme::MapMaxLayer + 1;
int max_x = 0, max_y = 0, max_z = 0;

const TileSet& tiles = selection.getTiles();
for(Tile* tile : tiles) {
if(tile->empty())
continue;

const Position& pos = tile->getPosition();

if(pos.x < min_x)
min_x = pos.x;
if(pos.x > max_x)
max_x = pos.x;

if(pos.y < min_y)
min_y = pos.y;
if(pos.y > max_y)
max_y = pos.y;

if(pos.z < min_z)
min_z = pos.z;
if(pos.z > max_z)
max_z = pos.z;
}

int numtiles = (max_x - min_x) * (max_y - min_y);
int minimap_width = max_x - min_x + 1;
int minimap_height = max_y - min_y + 1;

if(numtiles == 0)
return false;

if(minimap_width > 2048 || minimap_height > 2048) {
g_gui.PopupDialog("Error", "Minimap size greater than 2048px.", wxOK);
return false;
}

int tiles_iterated = 0;

for(int z = min_z; z <= max_z; z++) {
uint8_t* pixels = newd uint8_t[minimap_width * minimap_height * 3]; // 3 bytes per pixel
memset(pixels, 0, minimap_width * minimap_height * 3);

for(Tile* tile : tiles) {
if(tile->getZ() != z)
continue;

++tiles_iterated;
if(tiles_iterated % 8192 == 0)
g_gui.SetLoadDone(int(tiles_iterated / double(tiles.size()) * 90.0));

if(tile->empty())
continue;

uint8_t color = 0;

for(Item* item : tile->items) {
if(item->getMiniMapColor() != 0) {
color = item->getMiniMapColor();
break;
}
}

if(color == 0 && tile->hasGround())
color = tile->ground->getMiniMapColor();

uint32_t index = ((tile->getY() - min_y) * minimap_width + (tile->getX() - min_x)) * 3;

pixels[index] = (uint8_t)(int(color / 36) % 6 * 51); // red
pixels[index + 1] = (uint8_t)(int(color / 6) % 6 * 51); // green
pixels[index + 2] = (uint8_t)(color % 6 * 51); // blue
}

FileName file(fileName + "_" + i2ws(z) + ".png");
file.Normalize(wxPATH_NORM_ALL, directory.GetFullPath());
wxImage* image = newd wxImage(minimap_width, minimap_height, pixels, true);
image->SaveFile(file.GetFullPath(), wxBITMAP_TYPE_PNG);
image->Destroy();
delete[] pixels;
}

return true;
}

bool Editor::importMap(FileName filename, int import_x_offset, int import_y_offset, int import_z_offset, ImportType house_import_type, ImportType spawn_import_type)
{
selection.clear();
Expand Down
2 changes: 0 additions & 2 deletions source/editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ class Editor
wxString getLoaderError() const { return map.getError(); }
bool importMap(FileName filename, int import_x_offset, int import_y_offset, int import_z_offset, ImportType house_import_type, ImportType spawn_import_type);
bool importMiniMap(FileName filename, int import, int import_x_offset, int import_y_offset, int import_z_offset);
bool exportMiniMap(FileName filename, int floor /*= rme::MapGroundLayer*/, bool displaydialog);
bool exportSelectionAsMiniMap(FileName directory, wxString fileName);

ActionQueue* getHistoryActions() const noexcept { return actionQueue; }
Action* createAction(ActionIdentifier type);
Expand Down
33 changes: 23 additions & 10 deletions source/filehandle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ uint8_t NodeFileWriteHandle::NODE_START = ::NODE_START;
uint8_t NodeFileWriteHandle::NODE_END = ::NODE_END;
uint8_t NodeFileWriteHandle::ESCAPE_CHAR = ::ESCAPE_CHAR;

bool FileHandle::seek(size_t offset, int origin)
{
if(file) {
return fseek(file, static_cast<long>(offset), origin) == 0;
}
return false;
}

size_t FileHandle::tell()
{
if(file) {
return ftell(file);
}
return 0;
}

void FileHandle::close()
{
if(file) {
Expand Down Expand Up @@ -126,16 +142,6 @@ bool FileReadHandle::getLongString(std::string& str)
return getRAW(str, sz);
}

bool FileReadHandle::seek(size_t offset)
{
return fseek(file, long(offset), SEEK_SET) == 0;
}

bool FileReadHandle::seekRelative(size_t offset)
{
return fseek(file, long(offset), SEEK_CUR) == 0;
}

//=============================================================================
// Node file read handle

Expand Down Expand Up @@ -553,6 +559,13 @@ bool FileWriteHandle::addRAW(const uint8_t* ptr, size_t sz)
return ferror(file) == 0;
}

void FileWriteHandle::flush()
{
if(file) {
fflush(file);
}
}

//=============================================================================
// Disk based node file write handle

Expand Down
19 changes: 13 additions & 6 deletions source/filehandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ class FileHandle
FileHandle() : error_code(FILE_NO_ERROR), file(nullptr) {}
virtual ~FileHandle() { close(); }

bool seek(size_t offset, int origin = SEEK_SET);
size_t tell();
FORCEINLINE void skip(size_t offset) { seek(offset, SEEK_CUR); }

// Ensures we don't accidentally copy it.
FileHandle(const FileHandle &) = delete;
FileHandle &operator=(const FileHandle &) = delete;
Expand All @@ -64,6 +68,7 @@ class FileHandle
virtual bool isOpen() { return file != nullptr; }
virtual bool isOk() { return isOpen() && error_code == FILE_NO_ERROR && ferror(file) == 0; }
std::string getErrorMessage();

public:
FileHandleError error_code;
FILE* file;
Expand All @@ -87,11 +92,8 @@ class FileReadHandle : public FileHandle
bool getLongString(std::string& str);

virtual void close();
bool seek(size_t offset);
bool seekRelative(size_t offset);
FORCEINLINE void skip(size_t offset) { seekRelative(offset); }
size_t size() { return file_size; }
size_t tell() { if(file) return ftell(file); return 0; }

protected:
size_t file_size;

Expand Down Expand Up @@ -241,6 +243,7 @@ class FileWriteHandle : public FileHandle
bool addRAW(const std::string& str);
bool addRAW(const uint8_t* ptr, size_t sz);
bool addRAW(const char* c) { return addRAW(reinterpret_cast<const uint8_t*>(c), strlen(c)); }
void flush();

protected:
template<class T>
Expand Down Expand Up @@ -301,17 +304,20 @@ class NodeFileWriteHandle : public FileHandle
}
};

class DiskNodeFileWriteHandle : public NodeFileWriteHandle {
class DiskNodeFileWriteHandle : public NodeFileWriteHandle
{
public:
DiskNodeFileWriteHandle(const std::string& name, const std::string& identifier);
virtual ~DiskNodeFileWriteHandle();

virtual void close();

protected:
virtual void renewCache();
};

class MemoryNodeFileWriteHandle : public NodeFileWriteHandle {
class MemoryNodeFileWriteHandle : public NodeFileWriteHandle
{
public:
MemoryNodeFileWriteHandle();
virtual ~MemoryNodeFileWriteHandle();
Expand All @@ -321,6 +327,7 @@ class MemoryNodeFileWriteHandle : public NodeFileWriteHandle {

uint8_t* getMemory();
size_t getSize();

protected:
virtual void renewCache();
};
Expand Down
12 changes: 9 additions & 3 deletions source/graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,6 @@ bool GraphicManager::loadSpriteMetadataFlags(FileReadHandle& file, GameSprite* s
case DatFlagChargeable:
break;

case DatFlagGround:
case DatFlagWritable:
case DatFlagWritableOnce:
case DatFlagCloth:
Expand All @@ -643,6 +642,12 @@ bool GraphicManager::loadSpriteMetadataFlags(FileReadHandle& file, GameSprite* s
file.skip(2);
break;

case DatFlagGround:
uint16_t speed;
file.getU16(speed);
sType->ground_speed = speed;
break;

case DatFlagLight: {
SpriteLight light;
uint16_t intensity;
Expand Down Expand Up @@ -759,7 +764,7 @@ bool GraphicManager::loadSpriteData(const FileName& datafile, wxString& error, w
wxString ss;
ss << "items.spr: Duplicate GameSprite id " << id;
warnings.push_back(ss);
fh.seekRelative(size);
fh.skip(size);
} else {
spr->id = id;
spr->size = size;
Expand All @@ -771,7 +776,7 @@ bool GraphicManager::loadSpriteData(const FileName& datafile, wxString& error, w
}
}
} else {
fh.seekRelative(size);
fh.skip(size);
}
}
#undef safe_get
Expand Down Expand Up @@ -887,6 +892,7 @@ GameSprite::GameSprite() :
frames(0),
numsprites(0),
animator(nullptr),
ground_speed(0),
draw_height(0),
minimap_color(0)
{
Expand Down
1 change: 1 addition & 0 deletions source/graphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ class GameSprite : public Sprite

Animator* animator;

uint16_t ground_speed;
uint16_t draw_height;
wxPoint draw_offset;
uint16_t minimap_color;
Expand Down
Loading

0 comments on commit fd77aa4

Please sign in to comment.