Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export minimap as .otmm (otclient format) or .png #413

Merged
merged 5 commits into from
Mar 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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