Skip to content

Commit

Permalink
- fixed bug with tab closing
Browse files Browse the repository at this point in the history
- changed string and list textures
- made regex not require slashes
- fixed bug with selection going to cursor instead of start of selection for ctrl + x
  • Loading branch information
RealRTTV committed Apr 12, 2024
1 parent 6e6a566 commit cccddb4
Show file tree
Hide file tree
Showing 15 changed files with 117 additions and 104 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ repository = "https://github.com/RealRTTV/nbtworkbench"
keywords = ["nbt", "window", "unsafe", "editor", "tree"]
categories = ["graphics", "rendering", "text-editors", "parser-implementations"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

# Wasm Only
#[lib]
#crate-type = ["cdylib", "rlib"]
#path = "src/main.rs"

# Windows Only
[package.metadata.winres]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
#[package.metadata.winres]

[profile.release]
opt-level = 3
Expand Down
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ NBT Workbench is written completely from scratch in [Rust](https://www.rust-lang
(Features marked with a ☆ are new and not available in NBT Studio or Explorer):

* Java NBT files (`level.dat` / `hotbar.nbt`)
* Java region files (`.mca`)
* Java region files (`.mca` / `.mcr`)
* ☆ Now supports the new 1.21 LZ4 compression format
* SNBT files (`.snbt`)
*[Web Version](https://rttv.ca/main)
Expand All @@ -22,13 +22,14 @@ NBT Workbench is written completely from scratch in [Rust](https://www.rust-lang
* By holding right-click over an NBT tag: A circular action wheel will appear, which will let you make specific changes to NBT tags, this includes:
* Copying the condensed/raw or formatted/pretty SNBT version of a tag.
* ☆ Opening an array in a preferred hex editor.
* ☆ Opening nbt as SNBT in a preferred text editor.
* ☆ Opening NBT as SNBT in a preferred text editor.
* ☆ Sorting Compounds alphabetically or by type.
* ☆ Editing tag key/values in one click by simply being overtop the text.
* ☆ Searching with substrings, regex and snbt matching.
* ☆ Bookmarks
* ☆ Line Numbers
* ☆ Dark Mode
* ☆ Colored Text
* ☆ Remastered NBT Explorer Art
* ☆ CLI Mode `nbtworkbench -?`
*`nbtworkbench find` to search across multiple files
Expand Down Expand Up @@ -97,3 +98,16 @@ however, it would not come to be without the lovely projects below inspiring it.

### Icons
* Remastered/Inspired by [jaquado](https://github.com/jaquadro)'s [NBTExplorer](https://github.com/jaquadro/NBTExplorer) icons.

# Compiling
### For Windows
* You must have [Rust](https://rustup.rs) 1.78.0+ \[Nightly\] (target: x86_64-pc-windows-msvc)
* Uncomment the windows-only section of your `Cargo.toml` file and make sure the other sections are commented out.
* Run the following command to make a release build in `./target/x86_64-pc-windows-msvc/release`:\
`cargo +nightly build --release --target x86_64-pc-windows-msvc -Zbuild-std=std,panic_abort -Zbuild-std-features=panic_immediate_abort -- -Ctarget-feature=+avx`
### For Wasm
* You must have [Rust](https://rustup.rs) 1.78.0+ \[Nightly\] (target: x86_64-pc-windows-msvc)
* You must have [wasm-pack](https://crates.io/crates/wasm-pack) installed using cargo
* Uncomment the wasm-only section of your `Cargo.toml` file and make sure the other sections are commented out.
* Run the following command to compile for web assembly in `./web`:\
`wasm-pack build --release --target web --out-name nbtworkbench --out-dir web`
4 changes: 2 additions & 2 deletions src/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ pub const SELECTED_ACTION_WHEEL: [Vec2u; 8] = [
Vec2u::new(187, 57),
];
pub const TRAY_UV: Vec2u = Vec2u::new(128, 80);
pub const EDITED_UV: Vec2u = Vec2u::new(16, 64);
pub const UNEDITED_UV: Vec2u = Vec2u::new(32, 64);
pub const SAVE_UV: Vec2u = Vec2u::new(16, 64);
pub const SAVE_GRAYSCALE_UV: Vec2u = Vec2u::new(32, 64);
pub const NBT_FILE_TYPE_UV: Vec2u = Vec2u::new(32, 80);
pub const GZIP_FILE_TYPE_UV: Vec2u = Vec2u::new(48, 80);
pub const ZLIB_FILE_TYPE_UV: Vec2u = Vec2u::new(64, 80);
Expand Down
Binary file modified src/assets/atlas.hex
Binary file not shown.
Binary file modified src/assets/build/atlas.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/assets/test.nbt
Binary file not shown.
2 changes: 0 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ fn get_argument(key: &str, args: &mut Vec<String>) -> Option<String> {
}

#[inline]
#[cfg(not(target_arch = "wasm32"))]
pub fn find() -> ! {
let mut args = std::env::args().collect::<Vec<_>>();
// one for the exe, one for the `find`
Expand Down Expand Up @@ -185,7 +184,6 @@ pub fn find() -> ! {
}

#[inline]
#[cfg(not(target_arch = "wasm32"))]
pub fn reformat() -> ! {
let mut args = std::env::args().collect::<Vec<_>>();
args.drain(..2);
Expand Down
6 changes: 3 additions & 3 deletions src/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ impl TextColor {
Self::LightPurple => 0xFF55FF,
Self::Yellow => 0xFFFF55,
Self::White => 0xFFFFFF,
Self::TreeString => 0x7FE9AC,
Self::TreeKey => 0x6EADE2,
Self::TreePrimitive => 0xD19A66,
Self::TreeString => 0xA4F2C6,
Self::TreeKey => 0x8BC3F3,
Self::TreePrimitive => 0xF1B073,
Self::Custom(value) => value & 0xFFFFFF,
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/elements/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,7 @@ impl NbtRegion {
pub struct NbtChunk {
inner: Box<NbtCompound>,
last_modified: u32,
// need to restrict this file format to only use GZIP, ZLIB and Uncompressed
// need to restrict this file format to only use GZIP, ZLIB, Uncompressed, and LZ4
compression: FileFormat,
pub x: u8,
pub z: u8,
Expand Down
67 changes: 38 additions & 29 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ extern "C" {

pub static mut WORKBENCH: UnsafeCell<Workbench> = UnsafeCell::new(unsafe { Workbench::uninit() });
pub static mut WINDOW_PROPERTIES: UnsafeCell<WindowProperties> = UnsafeCell::new(WindowProperties::Fake);
pub const DOUBLE_CLICK_INTERVAL: Duration = Duration::from_millis(500);

#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
Expand Down Expand Up @@ -244,15 +245,13 @@ Options:

/// # Refactor
/// * render trees using `RenderLine` struct/enum
/// * make `Bookmarks` struct a thing and add functionality there
/// # Long Term Goals
/// * smart screen
/// * wiki page for docs on minecraft's format of stuff
/// * [chunk](NbtChunk) section rendering
/// * [chunk](elements::chunk::NbtChunk) section rendering
/// # Minor Features
/// * gear icon to swap toolbar with settings panel
/// * __ctrl + h__, open a playground `nbt` file to help with user interaction (bonus points if I have some way to tell if you haven't used this editor before)
/// * [`last_modified`](NbtChunk) field actually gets some impl
/// * [`last_modified`](elements::chunk::NbtChunk) field actually gets some impl
/// * auto save
/// * blur behind tooltip
/// # Major Features
Expand Down Expand Up @@ -300,34 +299,40 @@ pub fn get_clipboard() -> Option<String> {
return cli_clipboard::get_contents().ok();
}

#[cfg(not(target_arch = "wasm32"))]
pub fn set_clipboard(value: String) -> bool {
#[cfg(not(target_arch = "wasm32"))]
return cli_clipboard::set_contents(value).is_ok();
#[cfg(target_arch = "wasm32")]
return web_sys::window().map(|window| window.navigator()).and_then(|navigator| navigator.clipboard()).map(|clipboard| clipboard.write_text(&value)).is_some();
cli_clipboard::set_contents(value).is_ok()
}

#[cfg(target_arch = "wasm32")]
pub fn set_clipboard(value: String) -> bool {
web_sys::window().map(|window| window.navigator()).and_then(|navigator| navigator.clipboard()).map(|clipboard| clipboard.write_text(&value)).is_some()
}

#[must_use]
pub fn create_regex(mut str: String) -> Option<Regex> {
if !str.starts_with("/") {
return None;
}
let flags = 'a: {
if !str.starts_with("/") {
break 'a 0;
}

str = str.split_off(1);

let mut flags = 0_u8;
while let Some(char) = str.pop() {
match char {
'i' => flags |= 0b000001,
'g' => flags |= 0b000010,
'm' => flags |= 0b000100,
's' => flags |= 0b001000,
'u' => flags |= 0b010000,
'y' => flags |= 0b100000,
'/' => break,
_ => return None
str = str.split_off(1);

let mut flags = 0_u8;
while let Some(char) = str.pop() {
match char {
'i' => flags |= 0b000001,
'g' => flags |= 0b000010,
'm' => flags |= 0b000100,
's' => flags |= 0b001000,
'u' => flags |= 0b010000,
'y' => flags |= 0b100000,
'/' => break,
_ => return None
}
}
}
flags
};

RegexBuilder::new(&str)
.case_insensitive(flags & 0b1 > 0)
Expand All @@ -339,11 +344,15 @@ pub fn create_regex(mut str: String) -> Option<Regex> {
}

#[must_use]
#[cfg(not(target_arch = "wasm32"))]
pub fn since_epoch() -> Duration {
#[cfg(not(target_arch = "wasm32"))]
return unsafe { std::time::SystemTime::UNIX_EPOCH.elapsed().unwrap_unchecked() };
#[cfg(target_arch = "wasm32")]
return Duration::from_nanos((web_sys::js_sys::Date::now() * 1_000_000.0) as u64);
std::time::SystemTime::UNIX_EPOCH.elapsed().unwrap_or_else(|e| e.duration())
}

#[must_use]
#[cfg(target_arch = "wasm32")]
pub fn since_epoch() -> Duration {
Duration::from_nanos((web_sys::js_sys::Date::now() * 1_000_000.0) as u64)
}

pub fn sum_indices<I: Iterator<Item = usize>>(indices: I, mut root: &NbtElement) -> usize {
Expand Down
41 changes: 26 additions & 15 deletions src/tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use flate2::Compression;
use uuid::Uuid;
use zune_inflate::DeflateDecoder;

use crate::{LinkedQueue, OptionExt, panic_unchecked, RenderContext, since_epoch, SortAlgorithm, StrExt, WindowProperties};
use crate::{DOUBLE_CLICK_INTERVAL, LinkedQueue, OptionExt, panic_unchecked, RenderContext, since_epoch, SortAlgorithm, StrExt, WindowProperties};
use crate::assets::{BASE_Z, BYTE_ARRAY_GHOST_UV, BYTE_ARRAY_UV, BYTE_GRAYSCALE_UV, BYTE_UV, CHUNK_GHOST_UV, CHUNK_UV, COMPOUND_GHOST_UV, COMPOUND_ROOT_UV, COMPOUND_UV, DISABLED_REFRESH_UV, DOUBLE_GRAYSCALE_UV, DOUBLE_UV, ENABLED_FREEHAND_MODE_UV, FLOAT_GRAYSCALE_UV, FLOAT_UV, FREEHAND_MODE_UV, GZIP_FILE_TYPE_UV, HEADER_SIZE, HELD_SCROLLBAR_UV, HOVERED_WIDGET_UV, INT_ARRAY_GHOST_UV, INT_ARRAY_UV, INT_GRAYSCALE_UV, INT_UV, JUST_OVERLAPPING_BASE_Z, LINE_NUMBER_SEPARATOR_UV, LIST_GHOST_UV, LIST_UV, LONG_ARRAY_GHOST_UV, LONG_ARRAY_UV, LONG_GRAYSCALE_UV, LONG_UV, MCA_FILE_TYPE_UV, NBT_FILE_TYPE_UV, REDO_UV, REFRESH_UV, REGION_UV, SCROLLBAR_Z, SHORT_GRAYSCALE_UV, SHORT_UV, SNBT_FILE_TYPE_UV, STEAL_ANIMATION_OVERLAY_UV, STRING_GHOST_UV, STRING_UV, UNDO_UV, UNHELD_SCROLLBAR_UV, UNKNOWN_NBT_GHOST_UV, UNKNOWN_NBT_UV, UNSELECTED_WIDGET_UV, ZLIB_FILE_TYPE_UV, ZOffset};
use crate::color::TextColor;
use crate::elements::chunk::NbtRegion;
Expand All @@ -30,7 +30,7 @@ pub struct Tab {
pub compression: FileFormat,
pub undos: LinkedQueue<WorkbenchAction>,
pub redos: LinkedQueue<WorkbenchAction>,
pub history_changed: bool,
pub unsaved_changes: bool,
pub scroll: usize,
pub horizontal_scroll: usize,
pub window_height: usize,
Expand All @@ -44,6 +44,13 @@ pub struct Tab {
}

impl Tab {
pub const FILE_TYPE_FILTERS: &'static [(&'static str, &'static [&'static str])] = &[
("Uncompressed NBT File", &["nbt"]),
("SNBT File", &["snbt"]),
("Region File", &["mca", "mcr"]),
("Compressed NBT File", &["dat", "dat_old", "dat_new", "dat_mcr", "old", "schem", "schematic", "litematic"]),
];

pub fn new(nbt: NbtElement, path: &Path, compression: FileFormat, window_height: usize, window_width: usize) -> Result<Self> {
if !(nbt.id() == NbtCompound::ID || nbt.id() == NbtRegion::ID) { return Err(anyhow!("Parsed NBT was not a Compound or Region")) }

Expand All @@ -54,7 +61,7 @@ impl Tab {
compression,
undos: LinkedQueue::new(),
redos: LinkedQueue::new(),
history_changed: false,
unsaved_changes: false,
scroll: 0,
horizontal_scroll: 0,
window_height,
Expand All @@ -71,23 +78,25 @@ impl Tab {
#[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
pub fn save(&mut self, force_dialog: bool) -> Result<()> {
let path = self.path.as_deref().unwrap_or(self.name.as_ref().as_ref());
if path.try_exists().is_err() || force_dialog {
if !path.is_absolute() || force_dialog {
let mut builder = native_dialog::FileDialog::new();
builder = match self.compression {
FileFormat::Nbt => builder.add_filter("Uncompressed NBT File", &["nbt"]),
FileFormat::Snbt => builder.add_filter("SNBT File", &["snbt"]),
FileFormat::Lz4 | FileFormat::Mca => builder.add_filter("Region File", &["mca", "mcr"]),
FileFormat::Gzip | FileFormat::Zlib => builder.add_filter("Compressed NBT File", &["dat", "dat_old", "dat_new", "dat_mcr", "old"]),
let initial_index = match self.compression {
FileFormat::Nbt => 0,
FileFormat::Snbt => 1,
FileFormat::Lz4 | FileFormat::Mca => 2,
FileFormat::Gzip | FileFormat::Zlib => 3,
};
builder = builder.add_filter(Self::FILE_TYPE_FILTERS[initial_index].0, Self::FILE_TYPE_FILTERS[initial_index].1);
builder = Self::FILE_TYPE_FILTERS.iter().enumerate().filter_map(|(idx, value)| if idx == initial_index { None } else { Some(value) }).fold(builder, |builder, filter| builder.add_filter(filter.0, filter.1));
let path = builder.show_save_single_file()?.ok_or_else(|| anyhow!("Save cancelled"))?;
self.name = path.file_name().and_then(|x| x.to_str()).expect("Path has a filename").to_string().into_boxed_str();
std::fs::write(&path, self.compression.encode(&self.value))?;
self.path = Some(path);
self.history_changed = false;
self.unsaved_changes = false;
Ok(())
} else {
std::fs::write(path, self.compression.encode(&self.value))?;
self.history_changed = false;
self.unsaved_changes = false;
Ok(())
}
}
Expand Down Expand Up @@ -323,7 +332,7 @@ impl Tab {
pub fn append_to_history(&mut self, action: WorkbenchAction) {
self.undos.push(action);
self.redos.clear();
self.history_changed = true;
self.unsaved_changes = true;
}

#[must_use]
Expand Down Expand Up @@ -626,22 +635,24 @@ impl Tab {

#[cfg(not(target_arch = "wasm32"))]
pub fn refresh(&mut self, sort_algorithm: SortAlgorithm) -> Result<()> {
if self.history_changed && core::mem::replace(&mut self.last_close_attempt, since_epoch()).as_millis() > 3000 {
let Some(path) = self.path.as_deref() else { return Err(anyhow!("File path was not present in tab")) };

if self.unsaved_changes && (since_epoch() - core::mem::replace(&mut self.last_close_attempt, since_epoch())) > DOUBLE_CLICK_INTERVAL {
return Ok(());
}

let Some(path) = self.path.as_deref() else { return Err(anyhow!("File path was not present in tab")) };
let bytes = std::fs::read(path)?;
let (value, format) = Tab::parse_raw(path, bytes, sort_algorithm)?;

self.bookmarks.clear();
self.scroll = 0;
self.compression = format;
self.history_changed = false;
self.unsaved_changes = false;
self.undos.clear();
self.redos.clear();
self.uuid = Uuid::new_v4();
self.selected_text = None;
self.last_close_attempt = Duration::ZERO;
let old = core::mem::replace(&mut self.value, Box::new(value));
std::thread::Builder::new().stack_size(50_331_648 /*48MiB*/).spawn(move || drop(old)).expect("Failed to spawn thread");

Expand Down
1 change: 1 addition & 0 deletions src/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ impl<Additional: Clone, Cache: Cachelike<Additional>> Text<Additional, Cache> {
self.value = format!("{low}{high}");
self.selection = None;
}
self.cursor = start;
return NothingSpecial;
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/vertex_buffer_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ impl VertexBufferBuilder {
pub fn draw_tooltip(&mut self, text: &[&str], pos: impl Into<(usize, usize)>, force_draw_right: bool) {
use core::fmt::Write;

if self.drew_tooltip { return }

let (mut x, y) = pos.into();
let y = y + 16;
let text_width = text.iter().map(|x| x.width()).max().unwrap_or(0);
Expand Down
Loading

0 comments on commit cccddb4

Please sign in to comment.