Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
50145dc
Fix the Y-sort rendering
dimspell Aug 21, 2026
e6cf3e3
Fix rendering order (pope on chair fix).
dimspell Aug 21, 2026
9175c07
Fix the y-sort render for the walls; recon the collision/access block
dimspell Aug 21, 2026
cda39b7
Object ID painter
dimspell Aug 21, 2026
cab895b
Update docs
dimspell Aug 21, 2026
3abd60c
Add Save.ifo file parser
dimspell Aug 21, 2026
ccd6949
Register the Save.ifo file parser and
dimspell Aug 21, 2026
09e9dbc
Register Save.ifo parser
dimspell Aug 21, 2026
f67c69b
Add Save.ifo docs
dimspell Aug 21, 2026
bcec0f0
Add some findings about the unknown bytes in the Map file
dimspell Aug 21, 2026
df3bf3d
Cleanup wording
dimspell Aug 21, 2026
442215d
Get rid of the foreign keys in the SQLite logic
dimspell Aug 21, 2026
72b35a1
Fix the EditItem SQL code
dimspell Aug 21, 2026
2e0b7e7
Recognise the game version in the docs.
dimspell Aug 21, 2026
96351f5
Add a popover to manage the layers visibility in map editor
dimspell Aug 21, 2026
39d8f49
Disable the foreign keys
dimspell Aug 21, 2026
47148c5
Fix popover display
dimspell Aug 21, 2026
c5f7088
Redesign the popover
dimspell Aug 21, 2026
d6c6642
Redesign the map toolbar
dimspell Aug 21, 2026
9a82d29
Make use of the toast instead of status bar in the map editor
dimspell Aug 21, 2026
6a9668d
Add Save.ifo gui editor
dimspell Aug 21, 2026
e046b96
Add some binary record layouts
dimspell Aug 21, 2026
6dc2950
Rename the columns in the database
dimspell Aug 21, 2026
cb13144
Update insert and create table SQL queries
dimspell Aug 22, 2026
250c009
Render shadows in the game; Add fogdata.dat reader (clean me)
dimspell Aug 22, 2026
07a6128
Remove map-from-db recipe and the Rust code
dimspell Aug 23, 2026
d8d9982
Update the event_items table structure
dimspell Aug 23, 2026
b0725db
Update the MagicType enum
dimspell Aug 25, 2026
964217c
Update map renderer
dimspell Aug 25, 2026
b41fb04
Add copy file path in the file tree
dimspell Aug 25, 2026
1355804
Export more map data
dimspell Aug 25, 2026
adc7488
Export trailing data in the tilesets
dimspell Aug 25, 2026
412a8de
Rename column name to "id"
dimspell Aug 25, 2026
5f189fe
Get rid of the shaky graphic in sprite exports
dimspell Aug 27, 2026
0fcc5d7
Recognize the NPC role type (if it is a dialog or a shop)
dimspell Aug 27, 2026
9ecea3a
Reconstruct map reader
dimspell Aug 27, 2026
06a4cd6
Add feather and rune stone teleport marks data in misc item
dimspell Aug 27, 2026
ec9dfa6
Add proc-macro extractor for a nested structure
dimspell Aug 28, 2026
d2a997d
Render sprite animation films with no origin offset
dimspell Aug 28, 2026
37d605a
Add multi file parsers
dimspell Aug 28, 2026
72710e1
Add multi file GUI editors
dimspell Aug 28, 2026
90d292a
Add tile-grid canvas editor
dimspell Aug 28, 2026
867ad73
Tile grid improvements
dimspell Aug 28, 2026
f8704cd
Remove not existing test
dimspell Aug 28, 2026
c921ef7
Format code
dimspell Aug 28, 2026
93347d6
Render the sprites with black color preservation
dimspell Sep 4, 2026
ae7c714
Export no combat zone
dimspell Sep 2, 2026
6d2097d
Show no combat zones
dimspell Sep 4, 2026
55c1de2
Reformat code
dimspell Sep 5, 2026
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
35 changes: 2 additions & 33 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@
extract-dialogue-text extract-weapons \
patch-dry-run patch-in-place patch validate validate-verbose \
list list-json list-filtered schema template template-pretty \
map-help map-tiles map-atlas map-render map-render-full \
map-help map-tiles map-atlas map-render \
map-render-transparent map-render-collisions map-render-events \
map-render-draw-items map-render-waypoints \
map-render-noground map-render-nobuildings map-render-noroofs \
map-render-nomonsters map-render-nonpcs map-render-noobjects \
map-extract-sprites map-to-json map-to-json-pretty map-to-db \
map-from-db map-from-db-with-sprite \
map-atlas-btl map-atlas-gtl \
map-all-gtl-tiles map-all-btl-tiles zip-map-tiles \
database-import database-dialog-texts database-maps \
Expand Down Expand Up @@ -62,7 +61,7 @@ cargo_test:
cargo test --workspace --all-features --quiet

iced_test:
ICED_TEST_BACKEND=software cargo test -p dispel-gui --features "iced_test app::tests"
ICED_TEST_BACKEND=software cargo test -p dispel-gui --features "iced_test"

# GUI simulations require Iced's CPU renderer when no GPU/window
# server is available (for example, in CI or headless development shells).
Expand Down Expand Up @@ -275,7 +274,6 @@ template-pretty:
# map tiles – Extract every tile as a separate image
# map atlas – Pack tiles into a single atlas PNG
# map render – Render map with layers, overlays, and sprites
# map from-db – Render map from SQLite + atlas PNGs
# map to-db – Import .MAP file into SQLite
# map sprites – Extract map-internal sprites to PNGs
# map to-json – Export map data as JSON
Expand All @@ -295,7 +293,6 @@ map-atlas:
# Optional flags:
# --game-path <DIR> Enable entity overlay (NPCs, monsters, extras)
# --save-sprites Export sub-sprites from the map file
# --full-map Render full canvas (no occlusion viewport)
# --transparent RGBA PNG with alpha channel
# --collisions Show collision overlay
# --events Show event overlay
Expand All @@ -317,15 +314,6 @@ map-render:
--output="map.png" \
--game-path="$(game_path)"

map-render-full:
cargo run -- map render \
--map="$(game_path)/Map/$(map_id).map" \
--btl="$(game_path)/Map/$(map_id).btl" \
--gtl="$(game_path)/Map/$(map_id).gtl" \
--output="map.png" \
--game-path="$(game_path)" \
--full-map

map-render-transparent:
cargo run -- map render \
--map="$(game_path)/Map/$(map_id).map" \
Expand Down Expand Up @@ -452,25 +440,6 @@ map-to-json-pretty:
map-to-db:
cargo run -- map to-db --database "$(db_path)" --map "$(INPUT)"

# map from-db --database <DB> --map-id <ID> --gtl-atlas <PNG> --btl-atlas <PNG> --output <PNG>
# [--game-path <DIR>] [--atlas-columns <NUM>]
map-from-db:
cargo run -- map from-db \
--database "$(db_path)" \
--map-id "$(map_id)" \
--gtl-atlas "$(map_id).gtl.png" \
--btl-atlas "$(map_id).btl.png" \
-o "$(out)_$(map_id).png"

map-from-db-with-sprite:
cargo run -- map from-db \
--database "$(db_path)" \
--map-id "$(map_id)" \
--gtl-atlas "$(map_id).gtl.png" \
--btl-atlas "$(map_id).btl.png" \
-o "$(out)_$(map_id).png" \
--game-path "$(game_path)"

# map atlas convenience (shortcut: make atlas from game path + map_id)
map-atlas-btl:
cargo run -- map atlas \
Expand Down
74 changes: 74 additions & 0 deletions dispel-gui/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::components::command_palette::CommandPalette;
use crate::components::edit_history::EditHistory;
use crate::components::file_tree::FileTree;
use crate::editors::fog_data::FogDataEditorState;
use crate::editors::snf_editor::SnfEditorState;
use crate::editors::sprite_editor::SpriteViewerState;
use crate::editors::tileset::TilesetEditorState;
Expand Down Expand Up @@ -314,6 +315,16 @@ impl App {
}
Task::none()
}
EditorType::FogDataEditor => {
if let Some(tab_id) = self.active_tab_id() {
self.state
.editors
.fog_editors
.entry(tab_id)
.or_insert_with(|| FogDataEditorState::load_from_path(path));
}
Task::none()
}
EditorType::HexEditor => {
let scripts_dir = self
.state
Expand Down Expand Up @@ -356,6 +367,69 @@ impl App {
),
))
}
EditorType::ChestSpawnEditor => {
let path_buf = path.to_path_buf();
Task::perform(
async move {
dispel_core::ChestSpawn::read_file(&path_buf).map_err(|e| e.to_string())
},
|result| {
crate::message::Message::chest_spawn(
crate::editors::chest_spawn::ChestSpawnEditorMessage::CatalogLoaded(
result,
),
)
},
)
}
EditorType::MonsterSpawnEditor => {
let path_buf = path.to_path_buf();
Task::perform(
async move {
dispel_core::MonsterSpawn::read_file(&path_buf).map_err(|e| e.to_string())
},
|result| {
crate::message::Message::monster_spawn(
crate::editors::monster_spawn::MonsterSpawnEditorMessage::CatalogLoaded(
result,
),
)
},
)
}
EditorType::MapInfoMultiEditor => {
let path_buf = path.to_path_buf();
Task::perform(
async move {
dispel_core::MapInfoMulti::read_file(&path_buf).map_err(|e| e.to_string())
},
|result| {
crate::message::Message::map_info_multi(
crate::editors::map_info_multi::MapInfoMultiEditorMessage::CatalogLoaded(result),
)
},
)
}
EditorType::TileGridEditor => {
let path_buf = path.to_path_buf();
// Chain: (1) load the file, (2) after the catalog is ready,
// dispatch FitToWindow so the grid auto-fits the canvas pane
// without requiring the user to click "Fit" or resize.
let load_task = Task::perform(
async move {
dispel_core::TileGrid::read_file(&path_buf).map_err(|e| e.to_string())
},
|result| {
crate::message::Message::tile_grid(
crate::editors::tile_grid::TileGridEditorMessage::CatalogLoaded(result),
)
},
);
let fit_task = Task::done(crate::message::Message::tile_grid_canvas(
crate::editors::tile_grid::TileGridCanvasMessage::FitToWindow,
));
load_task.chain(fit_task)
}
EditorType::SaveFileViewer => {
let Some(tab_id) = self.active_tab_id() else {
return Task::none();
Expand Down
29 changes: 29 additions & 0 deletions dispel-gui/src/components/editable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,25 @@ pub fn parse_hex_array<const N: usize>(s: &str) -> Option<[u8; N]> {
}
}

/// Serialize a `&[u32]` as a comma-separated string (e.g. `"0,1,1,0,..."`).
#[inline]
pub fn format_vec_u32(values: &[u32]) -> String {
values
.iter()
.map(u32::to_string)
.collect::<Vec<_>>()
.join(",")
}

/// Parse a comma-separated list of `u32` values into a `Vec<u32>`.
/// Returns `None` if any value fails to parse.
#[inline]
pub fn parse_vec_u32(s: &str) -> Option<Vec<u32>> {
s.split(',')
.map(|part| part.trim().parse::<u32>().ok())
.collect()
}

// ── editable_record_fields! macro ─────────────────────────────────────────────

/// Helper: generate descriptor-kind from kind + args (bracket-delimited).
Expand Down Expand Up @@ -329,6 +348,7 @@ macro_rules! __er_kind {
};
(DispEnum, [$ty:ty, Shared($expr:expr)]) => { $expr };
(HexArray, [$n:literal]) => { $crate::components::editable::FieldKind::String };
(VecU32, []) => { $crate::components::editable::FieldKind::TextArea };
(Opti32Enum, [$ty:ty, [$($v:literal),* $(,)?]]) => {
$crate::components::editable::FieldKind::Enum { variants: &[$($v),*] }
};
Expand Down Expand Up @@ -392,6 +412,9 @@ macro_rules! __er_get {
(HexArray, [$n:literal], $this:ident, $field:ident) => {
$crate::components::editable::hex_string(&$this.$field)
};
(VecU32, [], $this:ident, $field:ident) => {
$crate::components::editable::format_vec_u32(&$this.$field)
};
(Opti32Enum, [$ty:ty, $($rest:tt)*], $this:ident, $field:ident) => {
$crate::components::editable::get_opt_val($this.$field, |v| v.value().to_string())
};
Expand Down Expand Up @@ -473,6 +496,12 @@ macro_rules! __er_set {
true
})
};
(VecU32, [], $this:ident, $field:ident, $value:ident) => {
$crate::components::editable::parse_vec_u32(&$value).is_some_and(|data| {
$this.$field = data;
true
})
};
(Opti32Enum, [$ty:ty, $($rest:tt)*], $this:ident, $field:ident, $value:ident) => {
$crate::components::editable::set_opt_i32_enum(&mut $this.$field, $value, <$ty>::from_i32)
};
Expand Down
32 changes: 29 additions & 3 deletions dispel-gui/src/components/file_tree/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ fn render_node<'a>(
icon_char(Icon::ChevronRight)
};

button(
let dir_btn = button(
row![
text(caret_char)
.font(LUCIDE_FONT)
Expand All @@ -421,8 +421,25 @@ fn render_node<'a>(
right: 4.0,
bottom: 3.0,
left: 6.0,
})
.into()
});

let entries = vec![
Entry::item(
"Show in File Manager",
FileTreeMessage::ShowInFileManager(node.path.clone()),
),
Entry::separator(),
Entry::item(
"Copy Absolute Path",
FileTreeMessage::CopyAbsolutePath(node.path.clone()),
),
Entry::item(
"Copy Relative Path",
FileTreeMessage::CopyRelativePath(node.path.clone()),
),
];

ContextMenu::new(dir_btn, entries).into()
} else {
let name_element = create_highlighted_text(&node.name, tree_filter.search_query());

Expand Down Expand Up @@ -456,6 +473,15 @@ fn render_node<'a>(
"Show in File Manager",
FileTreeMessage::ShowInFileManager(node.path.clone()),
),
Entry::separator(),
Entry::item(
"Copy Absolute Path",
FileTreeMessage::CopyAbsolutePath(node.path.clone()),
),
Entry::item(
"Copy Relative Path",
FileTreeMessage::CopyRelativePath(node.path.clone()),
),
];

ContextMenu::new(file_btn, entries).into()
Expand Down
4 changes: 4 additions & 0 deletions dispel-gui/src/components/file_tree/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ pub enum FileTreeMessage {
ExtractToJson(PathBuf),
ValidateFile(PathBuf),
ShowInFileManager(PathBuf),
CopyAbsolutePath(PathBuf),
CopyRelativePath(PathBuf),
/// Acknowledgement for a completed clipboard write (no-op).
ClipboardWriteComplete,
}
Loading
Loading