From afe9e8ac4dea33a49090f22dc89e8719a5f7a2d9 Mon Sep 17 00:00:00 2001 From: RTTV Date: Fri, 24 May 2024 10:59:23 -0400 Subject: [PATCH] - added serde-like indexing of elements for no reason yet - changed minimum dimensions for texture to 4096 to support more devices - updated msrv to 1.80.0 --- Cargo.toml | 25 +-- README.md | 4 +- src/assets.rs | 2 + src/element_action.rs | 4 +- src/elements/chunk.rs | 36 ++--- src/elements/element.rs | 347 ++++++++++++++++++++++++++++++++++++---- src/elements/null.rs | 58 +++++++ src/main.rs | 26 +-- src/tab.rs | 2 +- src/text_shader.rs | 2 +- src/window.rs | 17 +- src/workbench_action.rs | 25 ++- web/index.html | 24 +-- 13 files changed, 455 insertions(+), 117 deletions(-) create mode 100644 src/elements/null.rs diff --git a/Cargo.toml b/Cargo.toml index 7655bfe..ef596e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nbtworkbench" -version = "1.3.0" +version = "1.3.1" edition = "2021" description = "A modern NBT Editor written in Rust designed for performance and efficiency." license-file = "LICENSE" @@ -50,22 +50,23 @@ winres = "0.1.12" [dependencies] zune-inflate = { version = "0.2.54", features = ["gzip", "zlib"] } -flate2 = "1.0.27" -winit = "0.29.10" -wgpu = { version = "=0.19.1", default-features = false, features = ["webgl", "wgsl", "dx12", "metal"] } +flate2 = "1.0.28" +winit = "0.29.15" +wgpu = { version = "=0.19.4", default-features = false, features = ["webgl", "wgsl", "dx12", "metal"] } fxhash = "0.2.1" -hashbrown = { version = "0.14.0", features = ["raw", "inline-more", "nightly"], default-features = false } -getrandom = { version = "0.2.12", features = ["js"] } -notify = "6.0.1" -uuid = { version = "1.4.1", features = ["v4"] } +hashbrown = { version = "0.14.3", features = ["raw", "inline-more", "nightly"], default-features = false } +getrandom = { version = "0.2.14", features = ["js"] } +notify = "6.1.1" +uuid = { version = "1.8.0", features = ["v4"] } compact_str = "0.7.1" wgsl-inline = { version = "0.2.0", features = ["minify"] } static_assertions = "1.1.0" -anyhow = "1.0.79" -lz4_flex = { version = "0.11.2", default-features = false, features = ["std", "nightly"] } -regex = "1.10.3" +anyhow = "1.0.82" +lz4_flex = { version = "0.11.3", default-features = false, features = ["std", "nightly"] } +regex = "1.10.4" glob = "0.3.1" zune-png = { version = "0.4.10", features = [] } +polonius-the-crab = "0.4.1" [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3.9", features = [] } @@ -78,4 +79,4 @@ native-dialog = "0.7.0" [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen = "0.2.91" wasm-bindgen-futures = "0.4.41" -web-sys = { version = "=0.3.67", features = ["console", "Document", "Window", "Element", "Clipboard", "DateTimeValue", "HtmlElement", "HtmlDocument", "HtmlTextAreaElement", "Blob", "HtmlAreaElement", "Url"] } +web-sys = { version = "=0.3.69", features = ["console", "Document", "Window", "Element", "Clipboard", "DateTimeValue", "HtmlElement", "HtmlDocument", "HtmlTextAreaElement", "Blob", "HtmlAreaElement", "Url"] } diff --git a/README.md b/README.md index ab1bb4a..b03e23c 100644 --- a/README.md +++ b/README.md @@ -105,12 +105,12 @@ however, it would not come to be without the lovely projects below inspiring it. # Compiling ### For Windows -* You must have [Rust](https://rustup.rs) 1.78.0+ \[Nightly\] (target: x86_64-pc-windows-msvc) +* You must have [Rust](https://rustup.rs) 1.80.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 [Rust](https://rustup.rs) 1.80.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`:\ diff --git a/src/assets.rs b/src/assets.rs index 22244a6..d7acb63 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -82,6 +82,8 @@ pub const RENAME_TAIL_UV: Vec2u = Vec2u::new(32, 112); pub const MOVE_TAIL_UV: Vec2u = Vec2u::new(48, 112); pub const REPLACE_TAIL_UV: Vec2u = Vec2u::new(64, 112); pub const REORDER_TAIL_UV: Vec2u = Vec2u::new(80, 112); +pub const BULK_UV: Vec2u = Vec2u::new(240, 240); +pub const BULK_TAIL_UV: Vec2u = Vec2u::new(240, 240); pub const UNDO_UV: Vec2u = Vec2u::new(32, 144); pub const REDO_UV: Vec2u = Vec2u::new(48, 144); pub const LINE_NUMBER_SEPARATOR_UV: Vec2u = Vec2u::new(60, 64); diff --git a/src/element_action.rs b/src/element_action.rs index 243e551..3a3c39e 100644 --- a/src/element_action.rs +++ b/src/element_action.rs @@ -7,9 +7,9 @@ use compact_str::CompactString; use notify::{EventKind, PollWatcher, RecursiveMode, Watcher}; use uuid::Uuid; -use crate::{panic_unchecked, set_clipboard, FileUpdateSubscription, StrExt}; +use crate::{panic_unchecked, set_clipboard, FileUpdateSubscription}; #[cfg(not(target_arch = "wasm32"))] -use crate::{FileUpdateSubscriptionType, assets::{OPEN_ARRAY_IN_HEX_UV, OPEN_IN_TXT}, since_epoch}; +use crate::{FileUpdateSubscriptionType, assets::{OPEN_ARRAY_IN_HEX_UV, OPEN_IN_TXT}, since_epoch, StrExt}; use crate::assets::{ACTION_WHEEL_Z, COPY_FORMATTED_UV, COPY_RAW_UV, SORT_COMPOUND_BY_NAME, SORT_COMPOUND_BY_TYPE}; use crate::elements::chunk::NbtChunk; use crate::elements::compound::NbtCompound; diff --git a/src/elements/chunk.rs b/src/elements/chunk.rs index 33f0f89..c8897c5 100644 --- a/src/elements/chunk.rs +++ b/src/elements/chunk.rs @@ -93,8 +93,8 @@ impl NbtRegion { #[must_use] pub fn from_be_bytes(bytes: &[u8], sort: SortAlgorithm) -> Option { - fn parse(raw: u32, bytes: &[u8], sort: SortAlgorithm) -> Option<(FileFormat, NbtElement)> { - if raw < 512 { return Some((FileFormat::Zlib, unsafe { core::mem::zeroed() })) } + fn parse(raw: u32, bytes: &[u8], sort: SortAlgorithm) -> Option<(FileFormat, Option)> { + if raw < 512 { return Some((FileFormat::Zlib, None)) } let len = (raw as usize & 0xFF) * 4096; let offset = ((raw >> 8) - 2) as usize * 4096; @@ -128,11 +128,11 @@ impl NbtRegion { 4 => (FileFormat::Lz4, NbtElement::from_be_file(&lz4_flex::decompress(data, data.len()).ok()?, sort)?), _ => return None, }; - if element.id() != NbtCompound::ID { return None } - Some((compression, element)) - } else { - None + if let Some(element) = element.into_compound() { + return Some((compression, Some(element))); + } } + None } if bytes.len() < 8192 { return None } @@ -155,23 +155,19 @@ impl NbtRegion { threads.push((timestamp, s.spawn(move || parse(offset, bytes, sort)))); } - - for (pos, (timestamp, thread)) in threads.into_iter().enumerate() { + let mut pos = 0; + for (timestamp, thread) in threads { let (format, element) = thread.join().ok()??; - if element.id() == NbtCompound::ID { + if let Some(element) = element { unsafe { region.insert_unchecked( pos, region.len(), - NbtElement::Chunk(NbtChunk::from_compound( - core::mem::transmute(element), - ((pos >> 5) as u8 & 31, pos as u8 & 31), - format, - timestamp, - )), + NbtElement::Chunk(NbtChunk::from_compound(element, ((pos >> 5) as u8 & 31, pos as u8 & 31), format, timestamp, )), ); } } + pos += 1; } Some(region) @@ -198,20 +194,14 @@ impl NbtRegion { for (pos, (timestamp, thread)) in threads.into_iter().enumerate() { let (format, element) = thread?; - if element.id() == NbtCompound::ID { + if let Some(element) = element { unsafe { region.insert_unchecked( pos, region.len(), - NbtElement::Chunk(NbtChunk::from_compound( - core::mem::transmute_copy(&element), - ((pos >> 5) as u8 & 31, pos as u8 & 31), - format, - timestamp, - )), + NbtElement::Chunk(NbtChunk::from_compound(element, ((pos >> 5) as u8 & 31, pos as u8 & 31), format, timestamp, )), ); } - core::mem::forget(element); } } diff --git a/src/elements/element.rs b/src/elements/element.rs index 7d68c67..7ba95f7 100644 --- a/src/elements/element.rs +++ b/src/elements/element.rs @@ -2,13 +2,14 @@ use std::alloc::{alloc, dealloc, Layout}; use std::fmt::{Debug, Display, Error, Formatter}; use std::intrinsics::likely; use std::mem::{ManuallyDrop, MaybeUninit}; -use std::ops::Deref; +use std::ops::{Deref, Index, IndexMut}; #[cfg(not(target_arch = "wasm32"))] use std::thread::Scope; use std::{fmt, fmt::Write}; use compact_str::{format_compact, CompactString, ToCompactString}; use hashbrown::raw::RawTable; +use polonius_the_crab::{polonius, polonius_return}; use crate::assets::{BASE_Z, BYTE_ARRAY_UV, BYTE_UV, CONNECTION_UV, DOUBLE_UV, FLOAT_UV, INT_ARRAY_UV, INT_UV, LONG_ARRAY_UV, LONG_UV, SHORT_UV, ZOffset}; use crate::be_decoder::BigEndianDecoder; @@ -19,6 +20,7 @@ use crate::elements::list::{NbtList, ValueIterator, ValueMutIterator}; use crate::elements::string::NbtString; use crate::encoder::UncheckedBufWriter; use crate::{panic_unchecked, since_epoch, SortAlgorithm, array, primitive, DropFn, RenderContext, StrExt, VertexBufferBuilder, TextColor, assets::JUST_OVERLAPPING_BASE_TEXT_Z}; +use crate::elements::null::NbtNull; use crate::formatter::PrettyFormatter; use crate::le_decoder::LittleEndianDecoder; use crate::tab::FileFormat; @@ -56,6 +58,7 @@ pub union NbtElement { compound: ManuallyDrop, int_array: ManuallyDrop, long_array: ManuallyDrop, + null: ManuallyDrop, id: NbtElementId, } @@ -79,6 +82,7 @@ impl NbtElement { NbtCompound::ID => self.compound.matches(&other.compound), NbtIntArray::ID => self.int_array.matches(&other.int_array), NbtLongArray::ID => self.long_array.matches(&other.long_array), + NbtNull::ID => self.null.matches(&other.null), _ => core::hint::unreachable_unchecked(), } } @@ -122,6 +126,9 @@ impl Clone for NbtElement { NbtLongArray::ID => Self { long_array: self.long_array.clone(), }, + NbtNull::ID => Self { + null: self.null.clone() + }, _ => core::hint::unreachable_unchecked(), }; element.id.id = self.id.id; @@ -132,6 +139,9 @@ impl Clone for NbtElement { #[allow(non_snake_case)] impl NbtElement { + pub const NULL: NbtElement = unsafe { core::mem::zeroed() }; + pub const NULL_REF: &'static NbtElement = &Self::NULL; + #[inline] pub fn set_id(&mut self, id: u8) { unsafe { core::ptr::write(core::ptr::addr_of_mut!(self.id.id), id); } @@ -276,6 +286,16 @@ impl NbtElement { this.set_id(NbtRegion::ID); this } + + #[must_use] + #[inline] + pub fn Null(this: NbtNull) -> Self { + let mut this = Self { + null: ManuallyDrop::new(this), + }; + this.set_id(NbtNull::ID); + this + } } impl NbtElement { @@ -496,6 +516,7 @@ impl NbtElement { NbtLongArray::ID => self.long_array.to_be_bytes(writer), NbtChunk::ID => self.chunk.to_be_bytes(writer), NbtRegion::ID => self.region.to_be_bytes(writer), + NbtNull::ID => self.null.to_be_bytes(writer), _ => core::hint::unreachable_unchecked(), }; } @@ -538,6 +559,7 @@ impl NbtElement { NbtLongArray::ID => self.long_array.to_le_bytes(writer), NbtChunk::ID => { /* no */ }, NbtRegion::ID => { /* no */ }, + NbtNull::ID => self.null.to_le_bytes(writer), _ => core::hint::unreachable_unchecked(), }; } @@ -573,7 +595,7 @@ impl NbtElement { FileFormat::Zlib, since_epoch().as_secs() as u32, )), - _ => unsafe { core::mem::zeroed() }, + _ => Self::Null(NbtNull), } } @@ -672,7 +694,8 @@ impl NbtElement { NbtChunk::ID => self.chunk.render(builder, remaining_scroll, tail, ctx), NbtRegion::ID => { // can't be done at all - } + }, + NbtNull::ID => self.null.render(builder, str, ctx), _ => core::hint::unreachable_unchecked(), } } @@ -714,6 +737,7 @@ impl NbtElement { NbtLongArray::ID => "Long Array", NbtChunk::ID => "Chunk", NbtRegion::ID => "Region", + NbtNull::ID => "null", _ => unsafe { panic_unchecked("Invalid element id") }, } } @@ -721,7 +745,6 @@ impl NbtElement { #[inline] pub fn render_icon(id: u8, pos: impl Into<(usize, usize)>, z: ZOffset, builder: &mut VertexBufferBuilder) { match id { - 0 => {} NbtByte::ID => NbtByte::render_icon(pos, z, builder), NbtShort::ID => NbtShort::render_icon(pos, z, builder), NbtInt::ID => NbtInt::render_icon(pos, z, builder), @@ -736,6 +759,7 @@ impl NbtElement { NbtLongArray::ID => NbtLongArray::render_icon(pos, z, builder), NbtChunk::ID => NbtChunk::render_icon(pos, z, builder), NbtRegion::ID => NbtRegion::render_icon(pos, z, builder), + NbtNull::ID => NbtNull::render_icon(pos, z, builder), _ => unsafe { panic_unchecked("Invalid element id") }, } } @@ -921,6 +945,7 @@ impl NbtElement { NbtLongArray::ID => (self.long_array.value(), TextColor::TreeKey), NbtChunk::ID => (self.chunk.z.to_compact_string(), TextColor::TreePrimitive), NbtRegion::ID => (self.region.value(), TextColor::TreeKey), + NbtNull::ID => (CompactString::new_inline("null"), TextColor::TreeKey), _ => core::hint::unreachable_unchecked(), } } @@ -1217,6 +1242,7 @@ impl NbtElement { #[cfg(not(target_arch = "wasm32"))] ElementAction::OpenInTxt, ], + NbtNull::ID => &[], _ => core::hint::unreachable_unchecked(), } } @@ -1241,6 +1267,7 @@ impl Display for NbtElement { NbtLongArray::ID => write!(f, "{}", &*self.long_array), NbtChunk::ID => write!(f, "{}", &*self.chunk), NbtRegion::ID => Err(Error), + NbtNull::ID => write!(f, "{}", &*self.null), _ => core::hint::unreachable_unchecked(), } } @@ -1265,6 +1292,7 @@ impl NbtElement { NbtLongArray::ID => self.long_array.pretty_fmt(f), NbtChunk::ID => self.chunk.pretty_fmt(f), NbtRegion::ID => self.region.pretty_fmt(f), + NbtNull::ID => self.null.pretty_fmt(f), _ => core::hint::unreachable_unchecked(), } } @@ -1397,9 +1425,74 @@ impl Drop for NbtElement { } } } - _ => {} + NbtNull::ID => {} + _ => core::hint::unreachable_unchecked() + } + } + } +} + +impl<'a> Index<&'a str> for NbtElement { + type Output = NbtElement; + + fn index(&self, index: &'a str) -> &Self::Output { + let map = match self.as_pattern() { + NbtPattern::Compound(compound) => &*compound.entries, + NbtPattern::Chunk(chunk) => &*chunk.entries, + _ => return Self::NULL_REF, + }; + + if let Some(idx) = map.idx_of(index) && let Some((_, value)) = map.get_idx(idx) { + value + } else { + Self::NULL_REF + } + } +} + +impl<'a> IndexMut<&'a str> for NbtElement { + fn index_mut(&mut self, index: &str) -> &mut Self::Output { + fn try_index<'b>(this: &'b mut NbtElement, index: &str) -> Option<&'b mut NbtElement> { + let map = match this.as_pattern_mut() { + NbtPatternMut::Compound(compound) => &mut *compound.entries, + NbtPatternMut::Chunk(chunk) => &mut *chunk.entries, + _ => return None, + }; + + if let Some(idx) = map.idx_of(index) && let Some((_, value)) = map.get_idx_mut(idx) { + Some(value) + } else { + None } } + + let mut this = self; + polonius!(|this| -> &'polonius mut NbtElement { + if let Some(x) = try_index(this, index) { + polonius_return!(x); + } + }); + this + } +} + +impl Index for NbtElement { + type Output = NbtElement; + + fn index(&self, idx: usize) -> &Self::Output { + self.get(idx).unwrap_or(Self::NULL_REF) + } +} + +impl IndexMut for NbtElement { + fn index_mut(&mut self, idx: usize) -> &mut Self::Output { + let mut this = self; + polonius!(|this| -> &'polonius mut NbtElement { + if let Some(x) = this.get_mut(idx) { + polonius_return!(x); + } + }); + this } } @@ -1407,7 +1500,6 @@ impl Drop for NbtElement { #[must_use] pub fn id_to_string_name(id: u8) -> (&'static str, &'static str) { match id { - 0 => ("entry", "entries"), NbtByte::ID => ("byte", "bytes"), NbtShort::ID => ("short", "shorts"), NbtInt::ID => ("int", "ints"), @@ -1422,6 +1514,7 @@ pub fn id_to_string_name(id: u8) -> (&'static str, &'static str) { NbtLongArray::ID => ("long array", "long arrays"), NbtChunk::ID => ("chunk", "chunks"), NbtRegion::ID => ("region", "regions"), + NbtNull::ID => ("entry", "entries"), _ => unsafe { panic_unchecked("Invalid id") }, } } @@ -1441,6 +1534,7 @@ pub enum NbtPattern<'a> { LongArray(&'a NbtLongArray), Chunk(&'a NbtChunk), Region(&'a NbtRegion), + Null(&'a NbtNull), } pub enum NbtPatternMut<'a> { @@ -1458,6 +1552,7 @@ pub enum NbtPatternMut<'a> { LongArray(&'a mut NbtLongArray), Chunk(&'a mut NbtChunk), Region(&'a mut NbtRegion), + Null(&'a mut NbtNull), } impl NbtElement { @@ -1478,6 +1573,7 @@ impl NbtElement { NbtLongArray::ID => NbtPattern::LongArray(unsafe { self.as_long_array_unchecked() }), NbtChunk::ID => NbtPattern::Chunk(unsafe { self.as_chunk_unchecked() }), NbtRegion::ID => NbtPattern::Region(unsafe { self.as_region_unchecked() }), + NbtNull::ID => NbtPattern::Null(unsafe { self.as_null_unchecked() }), _ => unsafe { panic_unchecked("variant wasn't known") }, } } @@ -1498,6 +1594,7 @@ impl NbtElement { NbtLongArray::ID => NbtPatternMut::LongArray(unsafe { self.as_long_array_unchecked_mut() }), NbtChunk::ID => NbtPatternMut::Chunk(unsafe { self.as_chunk_unchecked_mut() }), NbtRegion::ID => NbtPatternMut::Region(unsafe { self.as_region_unchecked_mut() }), + NbtNull::ID => NbtPatternMut::Null(unsafe { self.as_null_unchecked_mut() }), _ => unsafe { panic_unchecked("variant wasn't known") }, } } @@ -1510,7 +1607,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::Byte` #[inline] #[must_use] - pub unsafe fn into_byte_unchecked(self) -> NbtByte { core::ptr::addr_of!(*self.byte).read() } + pub unsafe fn into_byte_unchecked(self) -> NbtByte { + let result = core::ptr::read(core::ptr::addr_of!(*self.byte)); + core::mem::forget(self); + result + } /// # Safety /// @@ -1531,7 +1632,11 @@ impl NbtElement { pub fn into_byte(self) -> Option { unsafe { if self.id() == NbtByte::ID { - Some(core::ptr::addr_of!(*self.byte).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.byte)); + core::mem::forget(self); + result + }) } else { None } @@ -1570,7 +1675,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::Short` #[inline] #[must_use] - pub unsafe fn into_short_unchecked(self) -> NbtShort { core::ptr::addr_of!(*self.short).read() } + pub unsafe fn into_short_unchecked(self) -> NbtShort { + let result = core::ptr::read(core::ptr::addr_of!(*self.short)); + core::mem::forget(self); + result + } /// # Safety /// @@ -1591,7 +1700,11 @@ impl NbtElement { pub fn into_short(self) -> Option { unsafe { if self.id() == NbtShort::ID { - Some(core::ptr::addr_of!(*self.short).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.short)); + core::mem::forget(self); + result + }) } else { None } @@ -1630,7 +1743,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::Int` #[inline] #[must_use] - pub unsafe fn into_int_unchecked(self) -> NbtInt { core::ptr::addr_of!(*self.int).read() } + pub unsafe fn into_int_unchecked(self) -> NbtInt { + let result = core::ptr::read(core::ptr::addr_of!(*self.int)); + core::mem::forget(self); + result + } /// # Safety /// @@ -1651,7 +1768,11 @@ impl NbtElement { pub fn into_int(self) -> Option { unsafe { if self.id() == NbtInt::ID { - Some(core::ptr::addr_of!(*self.int).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.int)); + core::mem::forget(self); + result + }) } else { None } @@ -1690,7 +1811,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::Long` #[inline] #[must_use] - pub unsafe fn into_long_unchecked(self) -> NbtLong { core::ptr::addr_of!(*self.long).read() } + pub unsafe fn into_long_unchecked(self) -> NbtLong { + let result = core::ptr::read(core::ptr::addr_of!(*self.long)); + core::mem::forget(self); + result + } /// # Safety /// @@ -1711,7 +1836,11 @@ impl NbtElement { pub fn into_long(self) -> Option { unsafe { if self.id() == NbtLong::ID { - Some(core::ptr::addr_of!(*self.long).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.long)); + core::mem::forget(self); + result + }) } else { None } @@ -1750,7 +1879,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::Float` #[inline] #[must_use] - pub unsafe fn into_float_unchecked(self) -> NbtFloat { core::ptr::addr_of!(*self.float).read() } + pub unsafe fn into_float_unchecked(self) -> NbtFloat { + let result = core::ptr::read(core::ptr::addr_of!(*self.float)); + core::mem::forget(self); + result + } /// # Safety /// @@ -1771,7 +1904,11 @@ impl NbtElement { pub fn into_float(self) -> Option { unsafe { if self.id() == NbtFloat::ID { - Some(core::ptr::addr_of!(*self.float).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.float)); + core::mem::forget(self); + result + }) } else { None } @@ -1810,7 +1947,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::Double` #[inline] #[must_use] - pub unsafe fn into_double_unchecked(self) -> NbtDouble { core::ptr::addr_of!(*self.double).read() } + pub unsafe fn into_double_unchecked(self) -> NbtDouble { + let result = core::ptr::read(core::ptr::addr_of!(*self.double)); + core::mem::forget(self); + result + } /// # Safety /// @@ -1831,7 +1972,11 @@ impl NbtElement { pub fn into_double(self) -> Option { unsafe { if self.id() == NbtDouble::ID { - Some(core::ptr::addr_of!(*self.double).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.double)); + core::mem::forget(self); + result + }) } else { None } @@ -1870,7 +2015,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::ByteArray` #[inline] #[must_use] - pub unsafe fn into_byte_array_unchecked(self) -> NbtByteArray { core::ptr::addr_of!(*self.byte_array).read() } + pub unsafe fn into_byte_array_unchecked(self) -> NbtByteArray { + let result = core::ptr::read(core::ptr::addr_of!(*self.byte_array)); + core::mem::forget(self); + result + } /// # Safety /// @@ -1891,7 +2040,11 @@ impl NbtElement { pub fn into_byte_array(self) -> Option { unsafe { if self.id() == NbtByteArray::ID { - Some(core::ptr::addr_of!(*self.byte_array).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.byte_array)); + core::mem::forget(self); + result + }) } else { None } @@ -1930,7 +2083,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::String` #[inline] #[must_use] - pub unsafe fn into_string_unchecked(self) -> NbtString { core::ptr::addr_of!(*self.string).read() } + pub unsafe fn into_string_unchecked(self) -> NbtString { + let result = core::ptr::read(core::ptr::addr_of!(*self.string)); + core::mem::forget(self); + result + } /// # Safety /// @@ -1951,7 +2108,11 @@ impl NbtElement { pub fn into_string(self) -> Option { unsafe { if self.id() == NbtString::ID { - Some(core::ptr::addr_of!(*self.string).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.string)); + core::mem::forget(self); + result + }) } else { None } @@ -1990,7 +2151,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::List` #[inline] #[must_use] - pub unsafe fn into_list_unchecked(self) -> NbtList { core::ptr::addr_of!(*self.list).read() } + pub unsafe fn into_list_unchecked(self) -> NbtList { + let result = core::ptr::read(core::ptr::addr_of!(*self.list)); + core::mem::forget(self); + result + } /// # Safety /// @@ -2011,7 +2176,11 @@ impl NbtElement { pub fn into_list(self) -> Option { unsafe { if self.id() == NbtList::ID { - Some(core::ptr::addr_of!(*self.list).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.list)); + core::mem::forget(self); + result + }) } else { None } @@ -2050,7 +2219,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::Compound` #[inline] #[must_use] - pub unsafe fn into_compound_unchecked(self) -> NbtCompound { core::ptr::addr_of!(*self.compound).read() } + pub unsafe fn into_compound_unchecked(self) -> NbtCompound { + let result = core::ptr::read(core::ptr::addr_of!(*self.compound)); + core::mem::forget(self); + result + } /// # Safety /// @@ -2071,8 +2244,13 @@ impl NbtElement { pub fn into_compound(self) -> Option { unsafe { if self.id() == NbtCompound::ID { - Some(core::ptr::addr_of!(*self.compound).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.compound)); + core::mem::forget(self); + result + }) } else { + drop(self); None } } @@ -2110,7 +2288,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::IntArray` #[inline] #[must_use] - pub unsafe fn into_int_array_unchecked(self) -> NbtIntArray { core::ptr::addr_of!(*self.int_array).read() } + pub unsafe fn into_int_array_unchecked(self) -> NbtIntArray { + let result = core::ptr::read(core::ptr::addr_of!(*self.int_array)); + core::mem::forget(self); + result + } /// # Safety /// @@ -2131,8 +2313,13 @@ impl NbtElement { pub fn into_int_array(self) -> Option { unsafe { if self.id() == NbtIntArray::ID { - Some(core::ptr::addr_of!(*self.int_array).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.int_array)); + core::mem::forget(self); + result + }) } else { + drop(self); None } } @@ -2170,7 +2357,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::LongArray` #[inline] #[must_use] - pub unsafe fn into_long_array_unchecked(self) -> NbtLongArray { core::ptr::addr_of!(*self.long_array).read() } + pub unsafe fn into_long_array_unchecked(self) -> NbtLongArray { + let result = core::ptr::read(core::ptr::addr_of!(*self.long_array)); + core::mem::forget(self); + result + } /// # Safety /// @@ -2191,7 +2382,11 @@ impl NbtElement { pub fn into_long_array(self) -> Option { unsafe { if self.id() == NbtLongArray::ID { - Some(core::ptr::addr_of!(*self.long_array).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.long_array)); + core::mem::forget(self); + result + }) } else { None } @@ -2230,7 +2425,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::Chunk` #[inline] #[must_use] - pub unsafe fn into_chunk_unchecked(self) -> NbtChunk { core::ptr::addr_of!(*self.chunk).read() } + pub unsafe fn into_chunk_unchecked(self) -> NbtChunk { + let result = core::ptr::read(core::ptr::addr_of!(*self.chunk)); + core::mem::forget(self); + result + } /// # Safety /// @@ -2251,7 +2450,11 @@ impl NbtElement { pub fn into_chunk(self) -> Option { unsafe { if self.id() == NbtChunk::ID { - Some(core::ptr::addr_of!(*self.chunk).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.chunk)); + core::mem::forget(self); + result + }) } else { None } @@ -2290,7 +2493,11 @@ impl NbtElement { /// * `self` must be of variant `NbtElement::Region` #[inline] #[must_use] - pub unsafe fn into_region_unchecked(self) -> NbtRegion { core::ptr::addr_of!(*self.region).read() } + pub unsafe fn into_region_unchecked(self) -> NbtRegion { + let result = core::ptr::read(core::ptr::addr_of!(*self.region)); + core::mem::forget(self); + result + } /// # Safety /// @@ -2311,7 +2518,11 @@ impl NbtElement { pub fn into_region(self) -> Option { unsafe { if self.id() == NbtRegion::ID { - Some(core::ptr::addr_of!(*self.region).read()) + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.region)); + core::mem::forget(self); + result + }) } else { None } @@ -2342,3 +2553,71 @@ impl NbtElement { } } } + +#[allow(dead_code)] +impl NbtElement { + /// # Safety + /// + /// * `self` must be of variant `NbtElement::Null` + #[inline] + #[must_use] + pub unsafe fn into_null_unchecked(self) -> NbtNull { + let result = core::ptr::read(core::ptr::addr_of!(*self.null)); + core::mem::forget(self); + result + } + + /// # Safety + /// + /// * `self` must be of variant `NbtElement::Null` + #[inline] + #[must_use] + pub unsafe fn as_null_unchecked(&self) -> &NbtNull { &self.null } + + /// # Safety + /// + /// * `self` must be of variant `NbtElement::Null` + #[inline] + #[must_use] + pub unsafe fn as_null_unchecked_mut(&mut self) -> &mut NbtNull { &mut self.null } + + #[inline] + #[must_use] + pub fn into_null(self) -> Option { + unsafe { + if self.id() == NbtNull::ID { + Some({ + let result = core::ptr::read(core::ptr::addr_of!(*self.null)); + core::mem::forget(self); + result + }) + } else { + None + } + } + } + + #[inline] + #[must_use] + pub fn as_null(&self) -> Option<&NbtNull> { + unsafe { + if self.id() == NbtNull::ID { + Some(&self.null) + } else { + None + } + } + } + + #[inline] + #[must_use] + pub fn as_null_mut(&mut self) -> Option<&mut NbtNull> { + unsafe { + if self.id() == NbtNull::ID { + Some(&mut self.null) + } else { + None + } + } + } +} diff --git a/src/elements/null.rs b/src/elements/null.rs new file mode 100644 index 0000000..76e6b7c --- /dev/null +++ b/src/elements/null.rs @@ -0,0 +1,58 @@ +use std::fmt::{Display, Formatter, Write}; + +use crate::assets::{BASE_Z, JUST_OVERLAPPING_BASE_TEXT_Z, ZOffset}; +use crate::color::TextColor; +use crate::encoder::UncheckedBufWriter; +use crate::formatter::PrettyFormatter; +use crate::RenderContext; +use crate::vertex_buffer_builder::VertexBufferBuilder; + +#[derive(Clone)] +pub struct NbtNull; + +impl NbtNull { + pub const ID: u8 = 0; + + #[inline] + pub fn to_be_bytes(&self, _: &mut UncheckedBufWriter) {} + + + #[inline] + pub fn to_le_bytes(&self, _: &mut UncheckedBufWriter) {} + + #[inline] + pub fn render(&self, builder: &mut VertexBufferBuilder, name: Option<&str>, ctx: &mut RenderContext) { + ctx.line_number(); + Self::render_icon(ctx.pos(), BASE_Z, builder); + ctx.render_errors(ctx.pos(), builder); + if ctx.forbid(ctx.pos()) { + builder.settings(ctx.pos() + (20, 0), false, JUST_OVERLAPPING_BASE_TEXT_Z); + if let Some(key) = name { + builder.color = TextColor::TreeKey.to_raw(); + let _ = write!(builder, "{key}: null "); + }; + + builder.color = TextColor::TreeKey.to_raw(); + let _ = write!(builder, "null"); + } + + ctx.y_offset += 16; + } + + #[inline] + pub fn render_icon(pos: impl Into<(usize, usize)>, z: ZOffset, builder: &mut VertexBufferBuilder) { builder.draw_texture_z(pos, z, (240, 240), (16, 16)); } +} + +impl Display for NbtNull { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "null") + } +} + +impl NbtNull { + pub fn pretty_fmt(&self, f: &mut PrettyFormatter) { f.write_str("null") } +} + +impl NbtNull { + pub fn matches(&self, _: &Self) -> bool { true } +} diff --git a/src/main.rs b/src/main.rs index a5eb185..68285c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,8 +20,7 @@ new_uninit, optimize_attribute, panic_update_hook, - stmt_expr_attributes, - unchecked_math + stmt_expr_attributes )] #![windows_subsystem = "windows"] @@ -56,28 +55,28 @@ use crate::workbench::Workbench; mod alert; mod assets; -mod color; mod be_decoder; +mod bookmark; +#[cfg(not(target_arch = "wasm32"))] +mod cli; +mod color; +mod copy_shader; +mod element_action; mod encoder; +mod formatter; +mod le_decoder; +mod search_box; mod selected_text; mod shader; mod tab; +mod text; mod text_shader; +mod tooltip_effect_shader; mod tree_travel; mod vertex_buffer_builder; mod window; mod workbench; mod workbench_action; -mod element_action; -mod search_box; -mod text; -#[cfg(not(target_arch = "wasm32"))] -mod cli; -mod formatter; -mod bookmark; -mod tooltip_effect_shader; -mod copy_shader; -mod le_decoder; #[macro_export] macro_rules! flags { @@ -1371,6 +1370,7 @@ pub mod elements { pub mod list; pub mod primitive; pub mod string; + pub mod null; } const_assert_eq!( diff --git a/src/tab.rs b/src/tab.rs index ff32195..3dd335f 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -251,7 +251,7 @@ impl Tab { } { - let enabled = self.path.as_deref().is_some_and(|path| path.exists()) && cfg!(not(target_os = "wasm32")); + let enabled = self.path.as_deref().is_some_and(|path| path.exists()) && !cfg!(target_arch = "wasm32"); let widget_uv = if (296..312).contains(&ctx.mouse_x) && (26..42).contains(&ctx.mouse_y) { #[cfg(target_arch = "wasm32")] builder.draw_tooltip(&["Refresh Tab (Disabled on WebAssembly version)"], (ctx.mouse_x, ctx.mouse_y), false); diff --git a/src/text_shader.rs b/src/text_shader.rs index 2dbd414..ae6c94e 100644 --- a/src/text_shader.rs +++ b/src/text_shader.rs @@ -55,7 +55,7 @@ wgsl! { let x = u32(input.uv[0] * 16.0); let y = u32(input.uv[1] * 16.0); let bit_index = input.character * 256u + y * 16 + x; - let byte = u32(textureLoad(buf, vec2(bit_index / 8u % 256u, bit_index / 8u / 256u), 0)[0] * 255.0); + let byte = u32(textureLoad(buf, vec2(bit_index / 8u % 512u, bit_index / 8u / 512u), 0)[0] * 255.0); let bit = (byte >> (7u - bit_index % 8u)) & 1u; if (bit == 0u) { discard; diff --git a/src/window.rs b/src/window.rs index c8b1ecd..733863b 100644 --- a/src/window.rs +++ b/src/window.rs @@ -155,10 +155,7 @@ impl<'window> State<'window> { &DeviceDescriptor { required_features: adapter.features(), required_limits: if cfg!(target_arch = "wasm32") { - Limits { - max_texture_dimension_2d: 8192, - ..Limits::downlevel_webgl2_defaults() - } + Limits::downlevel_webgl2_defaults() } else { Limits::default() }, @@ -317,8 +314,8 @@ impl<'window> State<'window> { let unicode_texture = device.create_texture(&TextureDescriptor { label: Some("Unicode Texture Array"), size: Extent3d { - width: 256, - height: assets::UNICODE_LEN as u32 / 256, + width: 512, + height: assets::UNICODE_LEN as u32 / 512, depth_or_array_layers: 1, }, mip_level_count: 1, @@ -335,11 +332,11 @@ impl<'window> State<'window> { ).decode_zlib().ok().panic_unchecked("there is no way this fails, otherwise i deserve the ub that comes from this.") }, ImageDataLayout { offset: 0, - bytes_per_row: Some(256), - rows_per_image: Some(assets::UNICODE_LEN as u32 / 256), + bytes_per_row: Some(512), + rows_per_image: Some(assets::UNICODE_LEN as u32 / 512), }, Extent3d { - width: 256, - height: assets::UNICODE_LEN as u32 / 256, + width: 512, + height: assets::UNICODE_LEN as u32 / 512, depth_or_array_layers: 1, }); let unicode_texture_view = unicode_texture.create_view(&TextureViewDescriptor::default()); diff --git a/src/workbench_action.rs b/src/workbench_action.rs index b90431f..8b45b9f 100644 --- a/src/workbench_action.rs +++ b/src/workbench_action.rs @@ -2,7 +2,7 @@ use std::mem::MaybeUninit; use compact_str::{CompactString, ToCompactString}; use std::path::PathBuf; -use crate::assets::{ADD_TAIL_UV, ADD_UV, MOVE_TAIL_UV, MOVE_UV, REMOVE_TAIL_UV, REMOVE_UV, RENAME_TAIL_UV, RENAME_UV, REORDER_TAIL_UV, REORDER_UV, REPLACE_TAIL_UV, REPLACE_UV}; +use crate::assets::{ADD_TAIL_UV, ADD_UV, BULK_TAIL_UV, BULK_UV, MOVE_TAIL_UV, MOVE_UV, REMOVE_TAIL_UV, REMOVE_UV, RENAME_TAIL_UV, RENAME_UV, REORDER_TAIL_UV, REORDER_UV, REPLACE_TAIL_UV, REPLACE_UV}; use crate::elements::element::NbtElement; use crate::vertex_buffer_builder::VertexBufferBuilder; use crate::{encompasses, encompasses_or_equal, FileUpdateSubscription}; @@ -36,6 +36,9 @@ pub enum WorkbenchAction { ReorderCompound { indices: Box<[usize]>, reordering_indices: Box<[usize]>, + }, + Bulk { + actions: Box<[WorkbenchAction]>, } } @@ -365,7 +368,7 @@ impl WorkbenchAction { let mut inverted_indices = Box::<[usize]>::new_uninit_slice(previous_entries.len()); let mut current_line_number = line_number + 1; let mut current_true_line_number = true_line_number + 1; - for (idx, &new_idx) in reordering_indices.into_iter().enumerate() { + for (idx, &new_idx) in reordering_indices.iter().enumerate() { let entry = core::mem::replace(previous_entries.get_unchecked_mut(new_idx), MaybeUninit::uninit()).assume_init(); *indices.find(entry.hash, |&x| x == new_idx).panic_unchecked("index obviously exists").as_mut() = idx; let line_number = *line_numbers.get_unchecked(new_idx); @@ -392,6 +395,17 @@ impl WorkbenchAction { indices: traversal_indices, reordering_indices: inverted_indices.assume_init(), }) + }, + Self::Bulk { actions } => { + let mut array = Box::new_uninit_slice(actions.len()); + + for (idx, action) in actions.into_vec().into_iter().rev().enumerate() { + array[idx].write(action.undo(root, bookmarks, subscription, path, name)); + } + + return Some(Self::Bulk { + actions: array.assume_init() + }) } }) } @@ -403,12 +417,9 @@ impl WorkbenchAction { Self::Add { .. } => builder.draw_texture(pos, if tail { ADD_TAIL_UV } else { ADD_UV }, (16, 16)), Self::Rename { .. } => builder.draw_texture(pos, if tail { RENAME_TAIL_UV } else { RENAME_UV }, (16, 16)), Self::Move { .. } => builder.draw_texture(pos, if tail { MOVE_TAIL_UV } else { MOVE_UV }, (16, 16)), - Self::Replace { .. } => builder.draw_texture( - pos, - if tail { REPLACE_TAIL_UV } else { REPLACE_UV }, - (16, 16), - ), + Self::Replace { .. } => builder.draw_texture(pos, if tail { REPLACE_TAIL_UV } else { REPLACE_UV }, (16, 16)), Self::ReorderCompound { .. } => builder.draw_texture(pos, if tail { REORDER_TAIL_UV } else { REORDER_UV }, (16, 16)), + Self::Bulk { .. } => builder.draw_texture(pos, if tail { BULK_TAIL_UV } else { BULK_UV }, (16, 16)), } } } diff --git a/web/index.html b/web/index.html index 907404e..0762ad2 100644 --- a/web/index.html +++ b/web/index.html @@ -72,6 +72,18 @@ +
+
+
+

NBT Workbench has crashed!

+

If you'd like to help out and report this problem, please:

+ +
+
+
-
-
-
-

NBT Workbench has crashed!

-

If you'd like to help out and report this problem, please:

- -
-
-
\ No newline at end of file