diff --git a/Cargo.toml b/Cargo.toml index cbe1c93..78d40a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,12 +33,13 @@ category = "Game" osx_minimum_system_version = "10.12" [features] -default = ["scripting", "backend-sdl", "render-opengl", "ogg-playback", "exe", "netplay"] +default = ["default-base", "backend-sdl", "render-opengl", "exe"] +default-base = ["scripting-lua", "ogg-playback", "netplay"] ogg-playback = ["lewton"] backend-sdl = ["sdl2", "sdl2-sys"] backend-glutin = ["winit", "glutin", "render-opengl"] render-opengl = [] -scripting = ["lua-ffi"] +scripting-lua = ["lua-ffi"] netplay = ["tokio", "serde_cbor"] editor = [] hooks = ["libc"] diff --git a/drsandroid/Cargo.toml b/drsandroid/Cargo.toml index 0c63f33..27d87e4 100644 --- a/drsandroid/Cargo.toml +++ b/drsandroid/Cargo.toml @@ -11,4 +11,4 @@ crate-type = ["cdylib"] ndk = "0.3" ndk-glue = "0.3" ndk-sys = "0.2" -doukutsu-rs = { path = "../", default-features = false, features = ["android", "backend-glutin", "ogg-playback", "scripting"] } +doukutsu-rs = { path = "../", default-features = false, features = ["default-base", "backend-glutin"] } diff --git a/src/bmfont.rs b/src/bmfont.rs index 06fc096..e19a18d 100644 --- a/src/bmfont.rs +++ b/src/bmfont.rs @@ -1,11 +1,10 @@ use std::collections::HashMap; use std::io; -use byteorder::{LE, ReadBytesExt}; +use byteorder::{ReadBytesExt, LE}; use crate::framework::error::GameError::ResourceLoadError; use crate::framework::error::GameResult; -use crate::str; #[derive(Debug)] pub struct BmChar { @@ -43,7 +42,7 @@ impl BMFont { data.read_exact(&mut magic)?; if magic != MAGIC { - return Err(ResourceLoadError(str!( "Invalid magic"))); + return Err(ResourceLoadError("Invalid magic".to_owned())); } while let Ok(block_type) = data.read_u8() { @@ -80,30 +79,16 @@ impl BMFont { let chnl = data.read_u8()?; if let Some(chr) = std::char::from_u32(id) { - chars.insert(chr, BmChar { - x, - y, - width, - height, - xoffset, - yoffset, - xadvance, - page, - chnl, - }); + chars.insert(chr, BmChar { x, y, width, height, xoffset, yoffset, xadvance, page, chnl }); } } } - _ => { return Err(ResourceLoadError(str!( "Unknown block type."))); } + _ => { + return Err(ResourceLoadError("Unknown block type.".to_owned())); + } } } - Ok(Self { - pages, - font_size, - line_height, - base, - chars, - }) + Ok(Self { pages, font_size, line_height, base, chars }) } } diff --git a/src/bmfont_renderer.rs b/src/bmfont_renderer.rs index db97dda..0c63d86 100644 --- a/src/bmfont_renderer.rs +++ b/src/bmfont_renderer.rs @@ -2,13 +2,12 @@ use std::collections::HashSet; use std::path::PathBuf; use crate::bmfont::BMFont; -use crate::common::{FILE_TYPES, Rect}; +use crate::common::{Rect, FILE_TYPES}; use crate::engine_constants::EngineConstants; use crate::framework::context::Context; use crate::framework::error::GameError::ResourceLoadError; use crate::framework::error::GameResult; use crate::framework::filesystem; -use crate::str; use crate::texture_set::TextureSet; pub struct BMFontRenderer { @@ -21,7 +20,7 @@ impl BMFontRenderer { let root = PathBuf::from(root); let full_path = &root.join(PathBuf::from(desc_path)); let desc_stem = - full_path.file_stem().ok_or_else(|| ResourceLoadError(str!("Cannot extract the file stem.")))?; + full_path.file_stem().ok_or_else(|| ResourceLoadError("Cannot extract the file stem.".to_owned()))?; let stem = full_path.parent().unwrap_or(full_path).join(desc_stem); let font = BMFont::load_from(filesystem::open(ctx, &full_path)?)?; @@ -101,7 +100,16 @@ impl BMFontRenderer { texture_set: &mut TextureSet, ctx: &mut Context, ) -> GameResult { - self.draw_colored_text_scaled(iter.clone(), x + scale, y + scale, scale, (0, 0, 0, 150), constants, texture_set, ctx)?; + self.draw_colored_text_scaled( + iter.clone(), + x + scale, + y + scale, + scale, + (0, 0, 0, 150), + constants, + texture_set, + ctx, + )?; self.draw_colored_text_scaled(iter, x, y, scale, color, constants, texture_set, ctx) } diff --git a/src/common.rs b/src/common.rs index eca5f70..50a22c7 100644 --- a/src/common.rs +++ b/src/common.rs @@ -21,40 +21,40 @@ lazy_static! { } bitfield! { - #[derive(Clone, Copy)] - #[repr(C)] - pub struct Flag(u32); - impl Debug; + #[derive(Clone, Copy)] + #[repr(C)] + pub struct Flag(u32); + impl Debug; - /// Set if left wall was hit. (corresponds to flag & 0x01) - pub hit_left_wall, set_hit_left_wall: 0; - /// Set if top wall was hit. (corresponds to flag & 0x02) - pub hit_top_wall, set_hit_top_wall: 1; - /// Set if right wall was hit. (corresponds to flag & 0x04) - pub hit_right_wall, set_hit_right_wall: 2; - /// Set if bottom wall was hit. (corresponds to flag & 0x08) - pub hit_bottom_wall, set_hit_bottom_wall: 3; - /// Set if entity stays on right slope. (corresponds to flag & 0x10) - pub hit_right_slope, set_hit_right_slope: 4; - /// Set if entity stays on left slope. (corresponds to flag & 0x20) - pub hit_left_slope, set_hit_left_slope: 5; - /// Unknown purpose (corresponds to flag & 0x40) - pub flag_x40, set_flag_x40: 6; - /// Unknown purpose (corresponds to flag & 0x80) - pub flag_x80, set_flag_x80: 7; - /// Set if entity is in water. (corresponds to flag & 0x100) - pub in_water, set_in_water: 8; - pub weapon_hit_block, set_weapon_hit_block: 9; // 0x200 - pub hit_by_spike, set_hit_by_spike: 10; // 0x400 - pub water_splash_facing_right, set_water_splash_facing_right: 11; // 0x800 - pub force_left, set_force_left: 12; // 0x1000 - pub force_up, set_force_up: 13; // 0x2000 - pub force_right, set_force_right: 14; // 0x4000 - pub force_down, set_force_down: 15; // 0x8000 - pub hit_left_higher_half, set_hit_left_higher_half: 16; // 0x10000 - pub hit_left_lower_half, set_hit_left_lower_half: 17; // 0x20000 - pub hit_right_lower_half, set_hit_right_lower_half: 18; // 0x40000 - pub hit_right_higher_half, set_hit_right_higher_half: 19; // 0x80000 + /// Set if left wall was hit. (corresponds to flag & 0x01) + pub hit_left_wall, set_hit_left_wall: 0; + /// Set if top wall was hit. (corresponds to flag & 0x02) + pub hit_top_wall, set_hit_top_wall: 1; + /// Set if right wall was hit. (corresponds to flag & 0x04) + pub hit_right_wall, set_hit_right_wall: 2; + /// Set if bottom wall was hit. (corresponds to flag & 0x08) + pub hit_bottom_wall, set_hit_bottom_wall: 3; + /// Set if entity stays on right slope. (corresponds to flag & 0x10) + pub hit_right_slope, set_hit_right_slope: 4; + /// Set if entity stays on left slope. (corresponds to flag & 0x20) + pub hit_left_slope, set_hit_left_slope: 5; + /// Unknown purpose (corresponds to flag & 0x40) + pub flag_x40, set_flag_x40: 6; + /// Unknown purpose (corresponds to flag & 0x80) + pub flag_x80, set_flag_x80: 7; + /// Set if entity is in water. (corresponds to flag & 0x100) + pub in_water, set_in_water: 8; + pub weapon_hit_block, set_weapon_hit_block: 9; // 0x200 + pub hit_by_spike, set_hit_by_spike: 10; // 0x400 + pub water_splash_facing_right, set_water_splash_facing_right: 11; // 0x800 + pub force_left, set_force_left: 12; // 0x1000 + pub force_up, set_force_up: 13; // 0x2000 + pub force_right, set_force_right: 14; // 0x4000 + pub force_down, set_force_down: 15; // 0x8000 + pub hit_left_higher_half, set_hit_left_higher_half: 16; // 0x10000 + pub hit_left_lower_half, set_hit_left_lower_half: 17; // 0x20000 + pub hit_right_lower_half, set_hit_right_lower_half: 18; // 0x40000 + pub hit_right_higher_half, set_hit_right_higher_half: 19; // 0x80000 } impl Flag { @@ -68,79 +68,79 @@ impl Flag { } bitfield! { - #[derive(Clone, Copy)] - #[repr(C)] - pub struct Equipment(u16); - impl Debug; + #[derive(Clone, Copy)] + #[repr(C)] + pub struct Equipment(u16); + impl Debug; - pub has_booster_0_8, set_booster_0_8: 0; // 0x01 / 0001 - pub has_map, set_map: 1; // 0x02 / 0002 - pub has_arms_barrier, set_arms_barrier: 2; // 0x04 / 0004 - pub has_turbocharge, set_turbocharge: 3; // 0x08 / 0008 - pub has_air_tank, set_air_tank: 4; // 0x10 / 0016 - pub has_booster_2_0, set_booster_2_0: 5; // 0x20 / 0032 - pub has_mimiga_mask, set_mimiga_mask: 6; // 0x40 / 0064 - pub has_whimsical_star, set_whimsical_star: 7; // 0x080 / 0128 - pub has_nikumaru, set_nikumaru: 8; // 0x100 / 0256 - // for custom equips - pub unused_1, set_unused_1: 9; // 0x200 / 0512 - pub unused_2, set_unused_2: 10; // 0x400 / 1024 - pub unused_3, set_unused_3: 11; // 0x800 / 2048 - pub unused_4, set_unused_4: 12; // 0x1000 / 4096 - pub unused_5, set_unused_5: 13; // 0x2000 / 8192 - // bit 14 and 15 aren't accessible via TSC without abusing overflows (won't work in strict mode) - pub unused_6, set_unused_6: 14; // 0x4000 / @384 - pub unused_7, set_unused_7: 15; // 0x8000 / P768 + pub has_booster_0_8, set_booster_0_8: 0; // 0x01 / 0001 + pub has_map, set_map: 1; // 0x02 / 0002 + pub has_arms_barrier, set_arms_barrier: 2; // 0x04 / 0004 + pub has_turbocharge, set_turbocharge: 3; // 0x08 / 0008 + pub has_air_tank, set_air_tank: 4; // 0x10 / 0016 + pub has_booster_2_0, set_booster_2_0: 5; // 0x20 / 0032 + pub has_mimiga_mask, set_mimiga_mask: 6; // 0x40 / 0064 + pub has_whimsical_star, set_whimsical_star: 7; // 0x080 / 0128 + pub has_nikumaru, set_nikumaru: 8; // 0x100 / 0256 + // for custom equips + pub unused_1, set_unused_1: 9; // 0x200 / 0512 + pub unused_2, set_unused_2: 10; // 0x400 / 1024 + pub unused_3, set_unused_3: 11; // 0x800 / 2048 + pub unused_4, set_unused_4: 12; // 0x1000 / 4096 + pub unused_5, set_unused_5: 13; // 0x2000 / 8192 + // bit 14 and 15 aren't accessible via TSC without abusing overflows (won't work in strict mode) + pub unused_6, set_unused_6: 14; // 0x4000 / @384 + pub unused_7, set_unused_7: 15; // 0x8000 / P768 } bitfield! { - #[derive(Clone, Copy)] - #[repr(C)] - pub struct Condition(u16); - impl Debug; + #[derive(Clone, Copy)] + #[repr(C)] + pub struct Condition(u16); + impl Debug; - pub interacted, set_interacted: 0; // 0x01 - pub hidden, set_hidden: 1; // 0x02 - pub fallen, set_fallen: 2; // 0x04 - pub explode_die, set_explode_die: 3; // 0x08 - pub damage_boss, set_damage_boss: 4; // 0x10 - pub increase_acceleration, set_increase_acceleration: 5; // 0x20 - pub cond_x40, set_cond_x40: 6; // 0x40 - pub alive, set_alive: 7; // 0x80 + pub interacted, set_interacted: 0; // 0x01 + pub hidden, set_hidden: 1; // 0x02 + pub fallen, set_fallen: 2; // 0x04 + pub explode_die, set_explode_die: 3; // 0x08 + pub damage_boss, set_damage_boss: 4; // 0x10 + pub increase_acceleration, set_increase_acceleration: 5; // 0x20 + pub cond_x40, set_cond_x40: 6; // 0x40 + pub alive, set_alive: 7; // 0x80 - // engine specific flags - pub drs_novanish, set_drs_novanish: 14; - pub drs_boss, set_drs_boss: 15; + // engine specific flags + pub drs_novanish, set_drs_novanish: 14; + pub drs_boss, set_drs_boss: 15; } bitfield! { - #[derive(Clone, Copy, Serialize, Deserialize)] - #[repr(C)] - pub struct ControlFlags(u16); - impl Debug; + #[derive(Clone, Copy, Serialize, Deserialize)] + #[repr(C)] + pub struct ControlFlags(u16); + impl Debug; - pub tick_world, set_tick_world: 0; // 0x01 - pub control_enabled, set_control_enabled: 1; // 0x02 - pub interactions_disabled, set_interactions_disabled: 2; // 0x04 - pub credits_running, set_credits_running: 3; // 0x08 + pub tick_world, set_tick_world: 0; // 0x01 + pub control_enabled, set_control_enabled: 1; // 0x02 + pub interactions_disabled, set_interactions_disabled: 2; // 0x04 + pub credits_running, set_credits_running: 3; // 0x08 - // engine specific flags - pub friendly_fire, set_friendly_fire: 14; + // engine specific flags + pub friendly_fire, set_friendly_fire: 14; } bitfield! { - #[derive(Clone, Copy)] - #[repr(C)] - pub struct BulletFlag(u16); - impl Debug; - pub flag_x01, set_flag_x01: 0; // 0x01 - pub flag_x02, set_flag_x02: 1; // 0x02 - pub no_collision_checks, set_no_collision_checks: 2; // 0x04 - pub bounce_from_walls, set_bounce_from_walls: 3; // 0x08 - pub flag_x10, set_flag_x10: 4; // 0x10 - pub flag_x20, set_flag_x20: 5; // 0x20 - pub can_destroy_snack, set_can_destroy_snack: 6; // 0x40 - pub flag_x80, set_flag_x80: 7; // 0x80 + #[derive(Clone, Copy)] + #[repr(C)] + pub struct BulletFlag(u16); + impl Debug; + pub flag_x01, set_flag_x01: 0; // 0x01 + pub flag_x02, set_flag_x02: 1; // 0x02 + pub no_collision_checks, set_no_collision_checks: 2; // 0x04 + pub bounce_from_walls, set_bounce_from_walls: 3; // 0x08 + pub flag_x10, set_flag_x10: 4; // 0x10 + pub flag_x20, set_flag_x20: 5; // 0x20 + pub can_destroy_snack, set_can_destroy_snack: 6; // 0x40 + pub flag_x80, set_flag_x80: 7; // 0x80 } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/src/components/inventory.rs b/src/components/inventory.rs index 64afdf9..0402000 100644 --- a/src/components/inventory.rs +++ b/src/components/inventory.rs @@ -8,7 +8,7 @@ use crate::input::touch_controls::TouchControlType; use crate::inventory::Inventory; use crate::player::Player; use crate::shared_game_state::SharedGameState; -use crate::text_script::ScriptMode; +use crate::scripting::tsc::text_script::ScriptMode; use crate::weapon::{WeaponLevel, WeaponType}; #[derive(Copy, Clone, PartialEq, Eq)] diff --git a/src/components/stage_select.rs b/src/components/stage_select.rs index e0d6885..8320382 100644 --- a/src/components/stage_select.rs +++ b/src/components/stage_select.rs @@ -7,7 +7,7 @@ use crate::frame::Frame; use crate::input::touch_controls::TouchControlType; use crate::player::Player; use crate::shared_game_state::SharedGameState; -use crate::text_script::ScriptMode; +use crate::scripting::tsc::text_script::ScriptMode; pub struct StageSelect { pub current_teleport_slot: u8, diff --git a/src/engine_constants/mod.rs b/src/engine_constants/mod.rs index 66f0deb..9063299 100644 --- a/src/engine_constants/mod.rs +++ b/src/engine_constants/mod.rs @@ -6,10 +6,9 @@ use crate::case_insensitive_hashmap; use crate::common::{BulletFlag, Color, Rect}; use crate::engine_constants::npcs::NPCConsts; use crate::player::ControlMode; -use crate::sound::pixtone::{Channel, PixToneParameters, Waveform, Envelope}; +use crate::scripting::tsc::text_script::TextScriptEncoding; +use crate::sound::pixtone::{Channel, Envelope, PixToneParameters, Waveform}; use crate::sound::SoundManager; -use crate::str; -use crate::text_script::TextScriptEncoding; mod npcs; @@ -308,7 +307,7 @@ impl EngineConstants { intro_player_pos: (3, 3), new_game_stage: 13, new_game_event: 200, - new_game_player_pos: (10, 8) + new_game_player_pos: (10, 8), }, player: PlayerConsts { life: 3, @@ -1421,7 +1420,7 @@ impl EngineConstants { text_speed_fast: 1, }, title: TitleConsts { - intro_text: "Studio Pixel presents".to_string(), + intro_text: "Studio Pixel presents".to_owned(), logo_rect: Rect { left: 0, top: 0, right: 144, bottom: 40 }, menu_left_top: Rect { left: 0, top: 0, right: 8, bottom: 8 }, menu_right_top: Rect { left: 236, top: 0, right: 244, bottom: 8 }, @@ -1434,59 +1433,59 @@ impl EngineConstants { menu_right: Rect { left: 236, top: 8, right: 244, bottom: 16 }, }, inventory_dim_color: Color::from_rgba(0, 0, 0, 0), - font_path: "csfont.fnt".to_string(), + font_path: "csfont.fnt".to_owned(), font_scale: 1.0, font_space_offset: 0.0, soundtracks: HashMap::new(), music_table: vec![ - "xxxx".to_string(), - "wanpaku".to_string(), - "anzen".to_string(), - "gameover".to_string(), - "gravity".to_string(), - "weed".to_string(), - "mdown2".to_string(), - "fireeye".to_string(), - "vivi".to_string(), - "mura".to_string(), - "fanfale1".to_string(), - "ginsuke".to_string(), - "cemetery".to_string(), - "plant".to_string(), - "kodou".to_string(), - "fanfale3".to_string(), - "fanfale2".to_string(), - "dr".to_string(), - "escape".to_string(), - "jenka".to_string(), - "maze".to_string(), - "access".to_string(), - "ironh".to_string(), - "grand".to_string(), - "curly".to_string(), - "oside".to_string(), - "requiem".to_string(), - "wanpak2".to_string(), - "quiet".to_string(), - "lastcave".to_string(), - "balcony".to_string(), - "lastbtl".to_string(), - "lastbt3".to_string(), - "ending".to_string(), - "zonbie".to_string(), - "bdown".to_string(), - "hell".to_string(), - "jenka2".to_string(), - "marine".to_string(), - "ballos".to_string(), - "toroko".to_string(), - "white".to_string(), - "kaze".to_string(), + "xxxx".to_owned(), + "wanpaku".to_owned(), + "anzen".to_owned(), + "gameover".to_owned(), + "gravity".to_owned(), + "weed".to_owned(), + "mdown2".to_owned(), + "fireeye".to_owned(), + "vivi".to_owned(), + "mura".to_owned(), + "fanfale1".to_owned(), + "ginsuke".to_owned(), + "cemetery".to_owned(), + "plant".to_owned(), + "kodou".to_owned(), + "fanfale3".to_owned(), + "fanfale2".to_owned(), + "dr".to_owned(), + "escape".to_owned(), + "jenka".to_owned(), + "maze".to_owned(), + "access".to_owned(), + "ironh".to_owned(), + "grand".to_owned(), + "curly".to_owned(), + "oside".to_owned(), + "requiem".to_owned(), + "wanpak2".to_owned(), + "quiet".to_owned(), + "lastcave".to_owned(), + "balcony".to_owned(), + "lastbtl".to_owned(), + "lastbt3".to_owned(), + "ending".to_owned(), + "zonbie".to_owned(), + "bdown".to_owned(), + "hell".to_owned(), + "jenka2".to_owned(), + "marine".to_owned(), + "ballos".to_owned(), + "toroko".to_owned(), + "white".to_owned(), + "kaze".to_owned(), ], organya_paths: vec![ - "/org/".to_string(), // NXEngine - "/base/Org/".to_string(), // CS+ - "/Resource/ORG/".to_string(), // CSE2E + "/org/".to_owned(), // NXEngine + "/base/Org/".to_owned(), // CS+ + "/Resource/ORG/".to_owned(), // CSE2E ], } } @@ -1496,15 +1495,15 @@ impl EngineConstants { self.is_cs_plus = true; self.supports_og_textures = true; - self.tex_sizes.insert(str!("Caret"), (320, 320)); - self.tex_sizes.insert(str!("MyChar"), (200, 384)); - self.tex_sizes.insert(str!("Npc/NpcRegu"), (320, 410)); + self.tex_sizes.insert("Caret".to_owned(), (320, 320)); + self.tex_sizes.insert("MyChar".to_owned(), (200, 384)); + self.tex_sizes.insert("Npc/NpcRegu".to_owned(), (320, 410)); self.title.logo_rect = Rect { left: 0, top: 0, right: 214, bottom: 50 }; - self.font_path = str!("csfont.fnt"); + self.font_path = "csfont.fnt".to_owned(); self.font_scale = 0.5; self.font_space_offset = 2.0; - self.soundtracks.insert("Remastered".to_string(), "/base/Ogg11/".to_string()); - self.soundtracks.insert("New".to_string(), "/base/Ogg/".to_string()); + self.soundtracks.insert("Remastered".to_owned(), "/base/Ogg11/".to_owned()); + self.soundtracks.insert("New".to_owned(), "/base/Ogg/".to_owned()); let typewriter_sample = PixToneParameters { // fx2 (CS+) @@ -1539,8 +1538,8 @@ impl EngineConstants { self.is_switch = true; self.supports_og_textures = true; - self.tex_sizes.insert(str!("bkMoon"), (427, 240)); - self.tex_sizes.insert(str!("bkFog"), (427, 240)); + self.tex_sizes.insert("bkMoon".to_owned(), (427, 240)); + self.tex_sizes.insert("bkFog".to_owned(), (427, 240)); self.title.logo_rect = Rect { left: 0, top: 0, right: 214, bottom: 62 }; self.inventory_dim_color = Color::from_rgba(0, 0, 32, 150); self.textscript.encoding = TextScriptEncoding::UTF8; @@ -1549,11 +1548,9 @@ impl EngineConstants { self.textscript.text_shadow = true; self.textscript.text_speed_normal = 1; self.textscript.text_speed_fast = 0; - self.soundtracks.insert("Famitracks".to_string(), "/base/ogg17/".to_string()); - self.soundtracks.insert("Ridiculon".to_string(), "/base/ogg_ridic/".to_string()); + self.soundtracks.insert("Famitracks".to_owned(), "/base/ogg17/".to_owned()); + self.soundtracks.insert("Ridiculon".to_owned(), "/base/ogg_ridic/".to_owned()); } - pub fn apply_constant_json_files(&mut self) { - - } + pub fn apply_constant_json_files(&mut self) {} } diff --git a/src/lib.rs b/src/lib.rs index 50669d2..c88b091 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,5 @@ #[macro_use] extern crate log; -#[cfg_attr(feature = "scripting", macro_use)] -#[cfg(feature = "scripting")] -extern crate lua_ffi; extern crate strum; #[macro_use] extern crate strum_macros; @@ -55,7 +52,6 @@ mod player; mod profile; mod rng; mod scene; -#[cfg(feature = "scripting")] mod scripting; mod settings; #[cfg(feature = "backend-gfx")] @@ -63,7 +59,6 @@ mod shaders; mod shared_game_state; mod sound; mod stage; -mod text_script; mod texture_set; mod weapon; @@ -281,7 +276,7 @@ pub fn init(options: LaunchOptions) -> GameResult { let game = UnsafeCell::new(Game::new(&mut context)?); let state_ref = unsafe { &mut *((&mut *game.get()).state.get()) }; - #[cfg(feature = "scripting")] + #[cfg(feature = "scripting-lua")] { state_ref.lua.update_refs(unsafe { (&*game.get()).state.get() }, &mut context as *mut Context); } diff --git a/src/live_debugger.rs b/src/live_debugger.rs index 61e37ac..a537d63 100644 --- a/src/live_debugger.rs +++ b/src/live_debugger.rs @@ -5,7 +5,7 @@ use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::scene::game_scene::GameScene; use crate::shared_game_state::SharedGameState; -use crate::text_script::TextScriptExecutionState; +use crate::scripting::tsc::text_script::TextScriptExecutionState; #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[repr(u8)] @@ -121,7 +121,7 @@ impl LiveDebugger { self.flags_visible = !self.flags_visible; } - #[cfg(feature = "scripting")] + #[cfg(feature = "scripting-lua")] { ui.same_line(0.0); if ui.button(im_str!("Reload Scripts"), [0.0, 0.0]) { diff --git a/src/macros.rs b/src/macros.rs index fd387cd..74f3bd7 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -5,16 +5,6 @@ pub use core::fmt; #[doc(hidden)] pub use core::mem::size_of; -#[macro_export] -macro_rules! str { - () => { - String::new() - }; - ($x:expr) => { - ToString::to_string(&$x) - }; -} - // extended version of https://github.com/dzamlo/rust-bitfield #[macro_export(local_inner_macros)] diff --git a/src/map.rs b/src/map.rs index 4d11c2c..d0e79ed 100644 --- a/src/map.rs +++ b/src/map.rs @@ -13,7 +13,6 @@ use crate::framework::error::{GameError, GameResult}; use crate::framework::filesystem; use crate::shared_game_state::TileSize; use crate::stage::{PxPackScroll, PxPackStageData, StageData}; -use crate::str; static SUPPORTED_PXM_VERSIONS: [u8; 1] = [0x10]; static SUPPORTED_PXE_VERSIONS: [u8; 2] = [0, 0x10]; @@ -43,7 +42,7 @@ impl Map { map_data.read_exact(&mut magic)?; if &magic != b"PXM" { - return Err(ResourceLoadError(str!("Invalid magic"))); + return Err(ResourceLoadError("Invalid magic".to_owned())); } let version = map_data.read_u8()?; @@ -80,7 +79,7 @@ impl Map { // based on https://github.com/tilderain/pxEdit/blob/kero/pxMap.py if &magic != b"PXPACK121127a**\0" { - return Err(ResourceLoadError(str!("Invalid magic"))); + return Err(ResourceLoadError("Invalid magic".to_owned())); } fn read_string(map_data: &mut R) -> GameResult { @@ -153,7 +152,7 @@ impl Map { map_data.read_exact(&mut magic)?; if &magic != b"pxMAP01\0" { - return Err(ResourceLoadError(str!("Invalid magic"))); + return Err(ResourceLoadError("Invalid magic".to_owned())); } let width_fg = map_data.read_u16::()?; @@ -170,7 +169,7 @@ impl Map { map_data.read_exact(&mut magic)?; if &magic != b"pxMAP01\0" { - return Err(ResourceLoadError(str!("Invalid magic"))); + return Err(ResourceLoadError("Invalid magic".to_owned())); } let width_mg = map_data.read_u16::()?; @@ -188,7 +187,7 @@ impl Map { map_data.read_exact(&mut magic)?; if &magic != b"pxMAP01\0" { - return Err(ResourceLoadError(str!("Invalid magic"))); + return Err(ResourceLoadError("Invalid magic".to_owned())); } let width_bg = map_data.read_u16::()?; @@ -214,7 +213,7 @@ impl Map { attrib_data.read_exact(&mut magic)?; if &magic != b"pxMAP01\0" { - return Err(ResourceLoadError(str!("Invalid magic"))); + return Err(ResourceLoadError("Invalid magic".to_owned())); } attrib_data.read_u16::()?; @@ -475,7 +474,7 @@ impl NPCData { data.read_exact(&mut magic)?; if &magic != b"PXE" { - return Err(ResourceLoadError(str!("Invalid magic"))); + return Err(ResourceLoadError("Invalid magic".to_owned())); } let version = data.read_u8()?; diff --git a/src/npc/mod.rs b/src/npc/mod.rs index 366ceac..5ba2609 100644 --- a/src/npc/mod.rs +++ b/src/npc/mod.rs @@ -20,7 +20,6 @@ use crate::player::Player; use crate::rng::Xoroshiro32PlusPlus; use crate::shared_game_state::SharedGameState; use crate::stage::Stage; -use crate::str; use crate::weapon::bullet::BulletManager; pub mod ai; @@ -199,7 +198,7 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &mut BulletManager, &mu ) -> GameResult { #[allow(unused_assignments)] let mut npc_hook_ran = false; - #[cfg(feature = "scripting")] + #[cfg(feature = "scripting-lua")] { npc_hook_ran = state.lua.try_run_npc_hook(self.id, self.npc_type); } @@ -655,9 +654,9 @@ impl NPCTable { pub fn new() -> NPCTable { NPCTable { entries: Vec::new(), - tileset_name: str!("Stage/Prt0"), - tex_npc1_name: str!("Npc/Npc0"), - tex_npc2_name: str!("Npc/Npc0"), + tileset_name: "Stage/Prt0".to_owned(), + tex_npc1_name: "Npc/Npc0".to_owned(), + tex_npc2_name: "Npc/Npc0".to_owned(), } } diff --git a/src/player/player_list.rs b/src/player/player_list.rs index e1689c8..0c8865b 100644 --- a/src/player/player_list.rs +++ b/src/player/player_list.rs @@ -1,13 +1,7 @@ -use crate::player::Player; - -pub struct RemotePlayerList { - -} +pub struct RemotePlayerList {} impl RemotePlayerList { pub fn new() -> RemotePlayerList { - RemotePlayerList { - - } + RemotePlayerList {} } } diff --git a/src/profile.rs b/src/profile.rs index 896780b..74ff50b 100644 --- a/src/profile.rs +++ b/src/profile.rs @@ -10,7 +10,6 @@ use crate::framework::error::GameResult; use crate::player::ControlMode; use crate::scene::game_scene::GameScene; use crate::shared_game_state::SharedGameState; -use crate::str; use crate::weapon::{WeaponLevel, WeaponType}; pub struct WeaponData { @@ -294,7 +293,7 @@ impl GameProfile { pub fn load_from_save(mut data: R) -> GameResult { // Do041220 if data.read_u64::()? != 0x446f303431323230 { - return Err(ResourceLoadError(str!("Invalid magic"))); + return Err(ResourceLoadError("Invalid magic".to_owned())); } let current_map = data.read_u32::()?; @@ -354,7 +353,7 @@ impl GameProfile { data.read_exact(&mut map_flags)?; if data.read_u32::()? != 0x464c4147 { - return Err(ResourceLoadError(str!("Invalid FLAG signature"))); + return Err(ResourceLoadError("Invalid FLAG signature".to_owned())); } let mut flags = [0u8; 1000]; diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index 1e39c73..ab08b28 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -32,7 +32,7 @@ use crate::scene::title_scene::TitleScene; use crate::scene::Scene; use crate::shared_game_state::{SharedGameState, TileSize}; use crate::stage::{BackgroundType, Stage}; -use crate::text_script::{ConfirmSelection, ScriptMode, TextScriptExecutionState, TextScriptLine, TextScriptVM}; +use crate::scripting::tsc::text_script::{ConfirmSelection, ScriptMode, TextScriptExecutionState, TextScriptLine, TextScriptVM}; use crate::texture_set::SpriteBatch; use crate::weapon::bullet::BulletManager; use crate::weapon::{Weapon, WeaponType}; @@ -1776,7 +1776,7 @@ impl Scene for GameScene { state.textscript_vm.set_scene_script(self.stage.load_text_script(&state.base_path, &state.constants, ctx)?); state.textscript_vm.suspend = false; state.tile_size = self.stage.map.tile_size; - #[cfg(feature = "scripting")] + #[cfg(feature = "scripting-lua")] state.lua.set_game_scene(self as *mut _); self.player1.controller = state.settings.create_player1_controller(); @@ -1948,7 +1948,7 @@ impl Scene for GameScene { self.flash.tick(state, ())?; - #[cfg(feature = "scripting")] + #[cfg(feature = "scripting-lua")] state.lua.scene_tick(); if state.control_flags.tick_world() { diff --git a/src/scene/loading_scene.rs b/src/scene/loading_scene.rs index 8450fd0..575daf1 100644 --- a/src/scene/loading_scene.rs +++ b/src/scene/loading_scene.rs @@ -6,7 +6,7 @@ use crate::scene::no_data_scene::NoDataScene; use crate::scene::Scene; use crate::shared_game_state::SharedGameState; use crate::stage::StageData; -use crate::text_script::TextScript; +use crate::scripting::tsc::text_script::TextScript; pub struct LoadingScene { tick: usize, diff --git a/src/scripting/boot.lua b/src/scripting/lua/boot.lua similarity index 100% rename from src/scripting/boot.lua rename to src/scripting/lua/boot.lua diff --git a/src/scripting/doukutsu.d.ts b/src/scripting/lua/doukutsu.d.ts similarity index 100% rename from src/scripting/doukutsu.d.ts rename to src/scripting/lua/doukutsu.d.ts diff --git a/src/scripting/doukutsu.rs b/src/scripting/lua/doukutsu.rs similarity index 99% rename from src/scripting/doukutsu.rs rename to src/scripting/lua/doukutsu.rs index 3be3538..29df035 100644 --- a/src/scripting/doukutsu.rs +++ b/src/scripting/lua/doukutsu.rs @@ -1,13 +1,15 @@ use std::io::Read; +use lua_ffi::c_str; use lua_ffi::ffi::luaL_Reg; +use lua_ffi::lua_method; use lua_ffi::{c_int, LuaObject, State}; use crate::common::{Direction, Rect}; use crate::framework::filesystem; use crate::rng::RNG; -use crate::scripting::{check_status, LuaScriptingState, DRS_RUNTIME_GLOBAL}; use crate::scene::game_scene::LightingMode; +use crate::scripting::lua::{check_status, LuaScriptingState, DRS_RUNTIME_GLOBAL}; pub struct Doukutsu { pub ptr: *mut LuaScriptingState, diff --git a/src/scripting/lua/mod.rs b/src/scripting/lua/mod.rs new file mode 100644 index 0000000..5ce714b --- /dev/null +++ b/src/scripting/lua/mod.rs @@ -0,0 +1,232 @@ +use std::io::Read; +use std::ptr::null_mut; + +use lua_ffi::lua_fn; +use lua_ffi::ffi::lua_State; +use lua_ffi::types::LuaValue; +use lua_ffi::{c_int, State, ThreadStatus}; + +use crate::common::Rect; +use crate::framework::context::Context; +use crate::framework::error::{GameError, GameResult}; +use crate::framework::filesystem; +use crate::framework::filesystem::File; +use crate::scene::game_scene::GameScene; +use crate::scripting::lua::doukutsu::Doukutsu; +use crate::shared_game_state::SharedGameState; + +mod doukutsu; +mod scene; + +pub struct LuaScriptingState { + state: Option, + state_ptr: *mut SharedGameState, + ctx_ptr: *mut Context, + game_scene: *mut GameScene, +} + +pub(in crate::scripting) static DRS_API_GLOBAL: &str = "__doukutsu_rs"; +pub(in crate::scripting) static DRS_RUNTIME_GLOBAL: &str = "__doukutsu_rs_runtime_dont_touch"; + +static BOOT_SCRIPT: &str = include_str!("boot.lua"); + +pub(in crate::scripting) fn check_status(status: ThreadStatus, state: &mut State) -> GameResult { + match status { + ThreadStatus::Ok | ThreadStatus::Yield => { + return Ok(()); + } + _ => {} + } + + let error = state.to_str(-1).unwrap_or("???"); + match status { + ThreadStatus::RuntimeError => Err(GameError::EventLoopError(format!("Lua Runtime Error: {}", error))), + ThreadStatus::SyntaxError => Err(GameError::EventLoopError(format!("Lua Syntax Error: {}", error))), + ThreadStatus::MemoryError => Err(GameError::EventLoopError(format!("Lua Memory Error: {}", error))), + ThreadStatus::MsgHandlerError => { + Err(GameError::EventLoopError(format!("Lua Message Handler Error: {}", error))) + } + ThreadStatus::FileError => Err(GameError::EventLoopError(format!("Lua File Error: {}", error))), + ThreadStatus::Unknown => Err(GameError::EventLoopError(format!("Unknown Error: {}", error))), + _ => Ok(()), + } +} + +fn print(state: &mut State) -> c_int { + if let Some(msg) = state.to_str(1) { + log::info!("[Lua] {}", msg); + } + + 0 +} + +impl LuaScriptingState { + pub fn new() -> LuaScriptingState { + LuaScriptingState { state: None, state_ptr: null_mut(), ctx_ptr: null_mut(), game_scene: null_mut() } + } + + pub fn update_refs(&mut self, state: *mut SharedGameState, ctx: *mut Context) { + self.state_ptr = state; + self.ctx_ptr = ctx; + } + + pub fn set_game_scene(&mut self, game_scene: *mut GameScene) { + self.game_scene = game_scene; + } + + fn load_script(mut state: &mut State, path: &str, mut script: File) -> bool { + let mut buf = Vec::new(); + let res = script.read_to_end(&mut buf); + + if let Err(err) = res { + log::warn!("Error reading script {}: {}", path, err); + return false; + } + + let name = format!("@{}", path); + let res = state.load_buffer(&buf, &name); + let res = check_status(res, &mut state); + if let Err(err) = res { + log::warn!("Error loading script {}: {}", path, err); + return false; + } + + state.get_global(DRS_RUNTIME_GLOBAL); + state.get_field(-1, "_initializeScript"); + state.push_value(-3); + + let res = state.pcall(1, 0, 0); + if let Err((_, err)) = res { + log::warn!("Error evaluating script {}: {}", path, err); + return false; + } + + log::info!("Successfully loaded Lua script: {}", path); + + true + } + + pub fn reload_scripts(&mut self, ctx: &mut Context) -> GameResult { + let mut state = State::new(); + state.open_libs(); + + state.push(lua_fn!(print)); + state.set_global("print"); + + state.push(Doukutsu { ptr: self as *mut LuaScriptingState }); + state.set_global(DRS_API_GLOBAL); + + log::info!("Initializing Lua scripting engine..."); + let res = state.do_string(BOOT_SCRIPT); + check_status(res, &mut state)?; + + if filesystem::exists(ctx, "/drs-scripts/") { + let mut script_count = 0; + let files = filesystem::read_dir(ctx, "/drs-scripts/")? + .filter(|f| f.to_string_lossy().to_lowercase().ends_with(".lua")); + + for file in files { + let path = file.clone(); + + match filesystem::open(ctx, file) { + Ok(script) => { + if LuaScriptingState::load_script(&mut state, path.to_string_lossy().as_ref(), script) { + script_count += 1; + } + } + Err(err) => { + log::warn!("Error opening script {:?}: {}", path, err); + } + } + } + + if script_count > 0 { + log::info!("{} Lua scripts have been loaded.", script_count); + } + } + + let modcs_path = "/Scripts/main.lua"; + if filesystem::exists(ctx, modcs_path) { + log::info!("Loading ModCS main script..."); + + match filesystem::open(ctx, modcs_path) { + Ok(script) => { + if !LuaScriptingState::load_script(&mut state, modcs_path, script) { + log::warn!("Error loading ModCS main script."); + } + } + Err(err) => { + log::warn!("Error opening script {:?}: {}", modcs_path, err); + } + } + } + + self.state = Some(state); + + Ok(()) + } +} + +impl LuaValue for Rect { + fn push_val(self, l: *mut lua_State) { + unsafe { + lua_ffi::ffi::lua_newtable(l); + lua_ffi::ffi::lua_pushinteger(l, self.left as isize); + lua_ffi::ffi::lua_rawseti(l, -2, 1); + lua_ffi::ffi::lua_pushinteger(l, self.top as isize); + lua_ffi::ffi::lua_rawseti(l, -2, 2); + lua_ffi::ffi::lua_pushinteger(l, self.right as isize); + lua_ffi::ffi::lua_rawseti(l, -2, 3); + lua_ffi::ffi::lua_pushinteger(l, self.bottom as isize); + lua_ffi::ffi::lua_rawseti(l, -2, 4); + } + } +} + +impl LuaValue for Rect { + fn push_val(self, l: *mut lua_State) { + unsafe { + lua_ffi::ffi::lua_newtable(l); + lua_ffi::ffi::lua_pushinteger(l, self.left as isize); + lua_ffi::ffi::lua_rawseti(l, -2, 1); + lua_ffi::ffi::lua_pushinteger(l, self.top as isize); + lua_ffi::ffi::lua_rawseti(l, -2, 2); + lua_ffi::ffi::lua_pushinteger(l, self.right as isize); + lua_ffi::ffi::lua_rawseti(l, -2, 3); + lua_ffi::ffi::lua_pushinteger(l, self.bottom as isize); + lua_ffi::ffi::lua_rawseti(l, -2, 4); + } + } +} + +impl LuaValue for Rect { + fn push_val(self, l: *mut lua_State) { + unsafe { + lua_ffi::ffi::lua_newtable(l); + lua_ffi::ffi::lua_pushinteger(l, self.left as isize); + lua_ffi::ffi::lua_rawseti(l, -2, 1); + lua_ffi::ffi::lua_pushinteger(l, self.top as isize); + lua_ffi::ffi::lua_rawseti(l, -2, 2); + lua_ffi::ffi::lua_pushinteger(l, self.right as isize); + lua_ffi::ffi::lua_rawseti(l, -2, 3); + lua_ffi::ffi::lua_pushinteger(l, self.bottom as isize); + lua_ffi::ffi::lua_rawseti(l, -2, 4); + } + } +} + +impl LuaValue for Rect { + fn push_val(self, l: *mut lua_State) { + unsafe { + lua_ffi::ffi::lua_newtable(l); + lua_ffi::ffi::lua_pushnumber(l, self.left as f64); + lua_ffi::ffi::lua_rawseti(l, -2, 1); + lua_ffi::ffi::lua_pushnumber(l, self.top as f64); + lua_ffi::ffi::lua_rawseti(l, -2, 2); + lua_ffi::ffi::lua_pushnumber(l, self.right as f64); + lua_ffi::ffi::lua_rawseti(l, -2, 3); + lua_ffi::ffi::lua_pushnumber(l, self.bottom as f64); + lua_ffi::ffi::lua_rawseti(l, -2, 4); + } + } +} diff --git a/src/scripting/scene.rs b/src/scripting/lua/scene.rs similarity index 91% rename from src/scripting/scene.rs rename to src/scripting/lua/scene.rs index ccf79a0..640921e 100644 --- a/src/scripting/scene.rs +++ b/src/scripting/lua/scene.rs @@ -1,8 +1,10 @@ +use lua_ffi::c_str; use lua_ffi::ffi::luaL_Reg; +use lua_ffi::lua_method; use lua_ffi::{c_int, LuaObject, State}; use crate::scene::game_scene::GameScene; -use crate::scripting::{LuaScriptingState, DRS_RUNTIME_GLOBAL}; +use crate::scripting::lua::{LuaScriptingState, DRS_RUNTIME_GLOBAL}; pub struct LuaGameScene { valid_reference: bool, diff --git a/src/scripting/tsconfig.json b/src/scripting/lua/tsconfig.json similarity index 100% rename from src/scripting/tsconfig.json rename to src/scripting/lua/tsconfig.json diff --git a/src/scripting/mod.rs b/src/scripting/mod.rs index 4a0da7b..ed065cc 100644 --- a/src/scripting/mod.rs +++ b/src/scripting/mod.rs @@ -1,235 +1,3 @@ -use std::io::Read; -use std::ptr::null_mut; - - -use crate::framework::context::Context; -use crate::framework::error::{GameResult, GameError}; - - -use lua_ffi::{c_int, State, ThreadStatus}; - -use crate::scene::game_scene::GameScene; -use crate::scripting::doukutsu::Doukutsu; -use crate::shared_game_state::SharedGameState; -use crate::framework::filesystem::File; -use crate::framework::filesystem; -use lua_ffi::types::LuaValue; -use crate::common::Rect; -use lua_ffi::ffi::lua_State; - -mod doukutsu; -mod scene; - -pub struct LuaScriptingState { - state: Option, - state_ptr: *mut SharedGameState, - ctx_ptr: *mut Context, - game_scene: *mut GameScene, -} - -pub(in crate::scripting) static DRS_API_GLOBAL: &str = "__doukutsu_rs"; -pub(in crate::scripting) static DRS_RUNTIME_GLOBAL: &str = "__doukutsu_rs_runtime_dont_touch"; - -static BOOT_SCRIPT: &str = include_str!("boot.lua"); - -pub(in crate::scripting) fn check_status(status: ThreadStatus, state: &mut State) -> GameResult { - match status { - ThreadStatus::Ok | ThreadStatus::Yield => { return Ok(()); } - _ => {} - } - - let error = state.to_str(-1).unwrap_or("???"); - match status { - ThreadStatus::RuntimeError => Err(GameError::EventLoopError(format!("Lua Runtime Error: {}", error))), - ThreadStatus::SyntaxError => Err(GameError::EventLoopError(format!("Lua Syntax Error: {}", error))), - ThreadStatus::MemoryError => Err(GameError::EventLoopError(format!("Lua Memory Error: {}", error))), - ThreadStatus::MsgHandlerError => Err(GameError::EventLoopError(format!("Lua Message Handler Error: {}", error))), - ThreadStatus::FileError => Err(GameError::EventLoopError(format!("Lua File Error: {}", error))), - ThreadStatus::Unknown => Err(GameError::EventLoopError(format!("Unknown Error: {}", error))), - _ => Ok(()) - } -} - -fn print(state: &mut State) -> c_int { - if let Some(msg) = state.to_str(1) { - log::info!("[Lua] {}", msg); - } - - 0 -} - -impl LuaScriptingState { - pub fn new() -> LuaScriptingState { - LuaScriptingState { - state: None, - state_ptr: null_mut(), - ctx_ptr: null_mut(), - game_scene: null_mut(), - } - } - - pub fn update_refs(&mut self, state: *mut SharedGameState, ctx: *mut Context) { - self.state_ptr = state; - self.ctx_ptr = ctx; - } - - pub fn set_game_scene(&mut self, game_scene: *mut GameScene) { - self.game_scene = game_scene; - } - - fn load_script(mut state: &mut State, path: &str, mut script: File) -> bool { - let mut buf = Vec::new(); - let res = script.read_to_end(&mut buf); - - if let Err(err) = res { - log::warn!("Error reading script {}: {}", path, err); - return false; - } - - let name = format!("@{}", path); - let res = state.load_buffer(&buf, &name); - let res = check_status(res, &mut state); - if let Err(err) = res { - log::warn!("Error loading script {}: {}", path, err); - return false; - } - - state.get_global(DRS_RUNTIME_GLOBAL); - state.get_field(-1, "_initializeScript"); - state.push_value(-3); - - let res = state.pcall(1, 0, 0); - if let Err((_, err)) = res { - log::warn!("Error evaluating script {}: {}", path, err); - return false; - } - - log::info!("Successfully loaded Lua script: {}", path); - - true - } - - pub fn reload_scripts(&mut self, ctx: &mut Context) -> GameResult { - let mut state = State::new(); - state.open_libs(); - - state.push(lua_fn!(print)); - state.set_global("print"); - - state.push(Doukutsu { ptr: self as *mut LuaScriptingState }); - state.set_global(DRS_API_GLOBAL); - - log::info!("Initializing Lua scripting engine..."); - let res = state.do_string(BOOT_SCRIPT); - check_status(res, &mut state)?; - - if filesystem::exists(ctx, "/drs-scripts/") { - let mut script_count = 0; - let files = filesystem::read_dir(ctx, "/drs-scripts/")? - .filter(|f| f.to_string_lossy().to_lowercase().ends_with(".lua")); - - for file in files { - let path = file.clone(); - - match filesystem::open(ctx, file) { - Ok(script) => { - if LuaScriptingState::load_script(&mut state, path.to_string_lossy().as_ref(), script) { - script_count += 1; - } - } - Err(err) => { - log::warn!("Error opening script {:?}: {}", path, err); - } - } - } - - if script_count > 0 { - log::info!("{} Lua scripts have been loaded.", script_count); - } - } - - let modcs_path = "/Scripts/main.lua"; - if filesystem::exists(ctx, modcs_path) { - log::info!("Loading ModCS main script..."); - - match filesystem::open(ctx, modcs_path) { - Ok(script) => { - if !LuaScriptingState::load_script(&mut state, modcs_path, script) { - log::warn!("Error loading ModCS main script."); - } - } - Err(err) => { - log::warn!("Error opening script {:?}: {}", modcs_path, err); - } - } - } - - self.state = Some(state); - - Ok(()) - } -} - -impl LuaValue for Rect { - fn push_val(self, l: *mut lua_State) { - unsafe { - lua_ffi::ffi::lua_newtable(l); - lua_ffi::ffi::lua_pushinteger(l, self.left as isize); - lua_ffi::ffi::lua_rawseti(l, -2, 1); - lua_ffi::ffi::lua_pushinteger(l, self.top as isize); - lua_ffi::ffi::lua_rawseti(l, -2, 2); - lua_ffi::ffi::lua_pushinteger(l, self.right as isize); - lua_ffi::ffi::lua_rawseti(l, -2, 3); - lua_ffi::ffi::lua_pushinteger(l, self.bottom as isize); - lua_ffi::ffi::lua_rawseti(l, -2, 4); - } - } -} - -impl LuaValue for Rect { - fn push_val(self, l: *mut lua_State) { - unsafe { - lua_ffi::ffi::lua_newtable(l); - lua_ffi::ffi::lua_pushinteger(l, self.left as isize); - lua_ffi::ffi::lua_rawseti(l, -2, 1); - lua_ffi::ffi::lua_pushinteger(l, self.top as isize); - lua_ffi::ffi::lua_rawseti(l, -2, 2); - lua_ffi::ffi::lua_pushinteger(l, self.right as isize); - lua_ffi::ffi::lua_rawseti(l, -2, 3); - lua_ffi::ffi::lua_pushinteger(l, self.bottom as isize); - lua_ffi::ffi::lua_rawseti(l, -2, 4); - } - } -} - -impl LuaValue for Rect { - fn push_val(self, l: *mut lua_State) { - unsafe { - lua_ffi::ffi::lua_newtable(l); - lua_ffi::ffi::lua_pushinteger(l, self.left as isize); - lua_ffi::ffi::lua_rawseti(l, -2, 1); - lua_ffi::ffi::lua_pushinteger(l, self.top as isize); - lua_ffi::ffi::lua_rawseti(l, -2, 2); - lua_ffi::ffi::lua_pushinteger(l, self.right as isize); - lua_ffi::ffi::lua_rawseti(l, -2, 3); - lua_ffi::ffi::lua_pushinteger(l, self.bottom as isize); - lua_ffi::ffi::lua_rawseti(l, -2, 4); - } - } -} - -impl LuaValue for Rect { - fn push_val(self, l: *mut lua_State) { - unsafe { - lua_ffi::ffi::lua_newtable(l); - lua_ffi::ffi::lua_pushnumber(l, self.left as f64); - lua_ffi::ffi::lua_rawseti(l, -2, 1); - lua_ffi::ffi::lua_pushnumber(l, self.top as f64); - lua_ffi::ffi::lua_rawseti(l, -2, 2); - lua_ffi::ffi::lua_pushnumber(l, self.right as f64); - lua_ffi::ffi::lua_rawseti(l, -2, 3); - lua_ffi::ffi::lua_pushnumber(l, self.bottom as f64); - lua_ffi::ffi::lua_rawseti(l, -2, 4); - } - } -} +#[cfg(feature = "scripting-lua")] +pub mod lua; +pub mod tsc; diff --git a/src/scripting/tsc/bytecode_utils.rs b/src/scripting/tsc/bytecode_utils.rs new file mode 100644 index 0000000..0373435 --- /dev/null +++ b/src/scripting/tsc/bytecode_utils.rs @@ -0,0 +1,103 @@ +use std::io::{Cursor, Read}; + +use crate::encoding::{read_cur_shift_jis, read_cur_wtf8}; +use crate::framework::error::GameError::ParseError; +use crate::framework::error::GameResult; +use crate::scripting::tsc::opcodes::OpCode; +use crate::scripting::tsc::text_script::TextScriptEncoding; + +pub fn put_varint(val: i32, out: &mut Vec) { + let mut x = ((val as u32) >> 31) ^ ((val as u32) << 1); + + loop { + let mut n = (x & 0x7f) as u8; + x >>= 7; + + if x != 0 { + n |= 0x80; + } + + out.push(n); + + if x == 0 { + break; + } + } +} + +pub fn read_cur_varint(cursor: &mut Cursor<&Vec>) -> GameResult { + let mut result = 0u32; + + for o in 0..5 { + let mut n = [0u8]; + cursor.read_exact(&mut n)?; + let [n] = n; + + result |= (n as u32 & 0x7f) << (o * 7); + + if n & 0x80 == 0 { + break; + } + } + + Ok(((result << 31) ^ (result >> 1)) as i32) +} + +#[allow(unused)] +pub fn read_varint>(iter: &mut I) -> GameResult { + let mut result = 0u32; + + for o in 0..5 { + let n = iter.next().ok_or_else(|| ParseError("Script unexpectedly ended.".to_owned()))?; + result |= (n as u32 & 0x7f) << (o * 7); + + if n & 0x80 == 0 { + break; + } + } + + Ok(((result << 31) ^ (result >> 1)) as i32) +} + +pub fn put_string(buffer: &mut Vec, out: &mut Vec, encoding: TextScriptEncoding) { + if buffer.len() == 0 { + return; + } + + let mut cursor: Cursor<&Vec> = Cursor::new(buffer); + let mut tmp_buf = Vec::new(); + let mut remaining = buffer.len() as u32; + let mut chars = 0; + + while remaining > 0 { + let (consumed, chr) = match encoding { + TextScriptEncoding::UTF8 => read_cur_wtf8(&mut cursor, remaining), + TextScriptEncoding::ShiftJIS => read_cur_shift_jis(&mut cursor, remaining), + }; + + remaining -= consumed; + chars += 1; + + put_varint(chr as i32, &mut tmp_buf); + } + + buffer.clear(); + + put_varint(OpCode::_STR as i32, out); + put_varint(chars, out); + out.append(&mut tmp_buf); +} + +#[test] +fn test_varint() { + for n in -4000..=4000 { + let mut out = Vec::new(); + put_varint(n, &mut out); + + let result = read_varint(&mut out.iter().copied()).unwrap(); + assert_eq!(result, n); + let mut cur = Cursor::new(&out); + let result = read_cur_varint(&mut cur).unwrap(); + assert_eq!(result, n); + } +} diff --git a/src/scripting/tsc/compiler.rs b/src/scripting/tsc/compiler.rs new file mode 100644 index 0000000..a05d008 --- /dev/null +++ b/src/scripting/tsc/compiler.rs @@ -0,0 +1,314 @@ +use std::collections::HashMap; +use std::iter::Peekable; +use std::str::FromStr; + +use itertools::Itertools; + +use crate::encoding::read_cur_wtf8; +use crate::framework::error::GameError::ParseError; +use crate::framework::error::GameResult; +use crate::scripting::tsc::bytecode_utils::{put_string, put_varint}; +use crate::scripting::tsc::opcodes::OpCode; +use crate::scripting::tsc::parse_utils::{expect_char, read_number, skip_until}; +use crate::scripting::tsc::text_script::{TextScript, TextScriptEncoding}; + +impl TextScript { + /// Compiles a decrypted text script data into internal bytecode. + pub fn compile(data: &[u8], strict: bool, encoding: TextScriptEncoding) -> GameResult { + let mut event_map = HashMap::new(); + let mut iter = data.iter().copied().peekable(); + let mut last_event = 0; + + while let Some(&chr) = iter.peek() { + match chr { + b'#' => { + iter.next(); + let event_num = read_number(&mut iter)? as u16; + if iter.peek().is_some() { + skip_until(b'\n', &mut iter)?; + iter.next(); + } + last_event = event_num; + + if event_map.contains_key(&event_num) { + if strict { + return Err(ParseError(format!("Event {} has been defined twice.", event_num))); + } + + match skip_until(b'#', &mut iter).ok() { + Some(_) => { + continue; + } + None => { + break; + } + } + } + + let bytecode = TextScript::compile_event(&mut iter, strict, encoding)?; + log::info!("Successfully compiled event #{} ({} bytes generated).", event_num, bytecode.len()); + event_map.insert(event_num, bytecode); + } + b'\r' | b'\n' | b' ' | b'\t' => { + iter.next(); + } + n => { + // CS+ boss rush is the buggiest shit ever. + if !strict && last_event == 0 { + iter.next(); + continue; + } + + return Err(ParseError(format!("Unexpected token in event {}: {}", last_event, n as char))); + } + } + } + + Ok(TextScript { event_map }) + } + + fn compile_event>( + iter: &mut Peekable, + strict: bool, + encoding: TextScriptEncoding, + ) -> GameResult> { + let mut bytecode = Vec::new(); + let mut char_buf = Vec::with_capacity(16); + let mut allow_next_event = true; + + while let Some(&chr) = iter.peek() { + match chr { + b'#' if allow_next_event => { + if !char_buf.is_empty() { + put_string(&mut char_buf, &mut bytecode, encoding); + } + + // some events end without { + allow_next_event = false; + if char_buf.len() > 2 { + if let Some(&c) = char_buf.last() { + if c == b'\n' { + let _ = char_buf.pop(); + } + } + } + + if !char_buf.is_empty() { + put_string(&mut char_buf, &mut bytecode, encoding); + } + + iter.next(); + let n = iter + .next_tuple::<(u8, u8, u8)>() + .map(|t| [t.0, t.1, t.2]) + .ok_or_else(|| ParseError("Script unexpectedly ended.".to_owned()))?; + + let code = String::from_utf8_lossy(&n); + + TextScript::compile_code(code.as_ref(), strict, iter, &mut bytecode)?; + } + b'\r' => { + iter.next(); + } + b'\n' => { + allow_next_event = true; + char_buf.push(chr); + + iter.next(); + } + _ => { + allow_next_event = false; + char_buf.push(chr); + + iter.next(); + } + } + } + + Ok(bytecode) + } + + fn compile_code>( + code: &str, + strict: bool, + iter: &mut Peekable, + out: &mut Vec, + ) -> GameResult { + let instr = OpCode::from_str(code).map_err(|_| ParseError(format!("Unknown opcode: {}", code)))?; + + match instr { + // Zero operand codes + OpCode::AEp + | OpCode::CAT + | OpCode::CIL + | OpCode::CLO + | OpCode::CLR + | OpCode::CPS + | OpCode::CRE + | OpCode::CSS + | OpCode::END + | OpCode::ESC + | OpCode::FLA + | OpCode::FMU + | OpCode::FRE + | OpCode::HMC + | OpCode::INI + | OpCode::KEY + | OpCode::LDP + | OpCode::MLP + | OpCode::MM0 + | OpCode::MNA + | OpCode::MS2 + | OpCode::MS3 + | OpCode::MSG + | OpCode::NOD + | OpCode::PRI + | OpCode::RMU + | OpCode::SAT + | OpCode::SLP + | OpCode::SMC + | OpCode::SPS + | OpCode::STC + | OpCode::SVP + | OpCode::TUR + | OpCode::WAS + | OpCode::ZAM + | OpCode::HM2 + | OpCode::POP + | OpCode::KE2 + | OpCode::FR2 => { + put_varint(instr as i32, out); + } + // One operand codes + OpCode::BOA + | OpCode::BSL + | OpCode::FOB + | OpCode::FOM + | OpCode::QUA + | OpCode::UNI + | OpCode::MYB + | OpCode::MYD + | OpCode::FAI + | OpCode::FAO + | OpCode::WAI + | OpCode::FAC + | OpCode::GIT + | OpCode::NUM + | OpCode::DNA + | OpCode::DNP + | OpCode::FLm + | OpCode::FLp + | OpCode::MPp + | OpCode::SKm + | OpCode::SKp + | OpCode::EQp + | OpCode::EQm + | OpCode::MLp + | OpCode::ITp + | OpCode::ITm + | OpCode::AMm + | OpCode::UNJ + | OpCode::MPJ + | OpCode::YNJ + | OpCode::EVE + | OpCode::XX1 + | OpCode::SIL + | OpCode::LIp + | OpCode::SOU + | OpCode::CMU + | OpCode::SSS + | OpCode::ACH + | OpCode::S2MV + | OpCode::S2PJ + | OpCode::PSH => { + let operand = read_number(iter)?; + put_varint(instr as i32, out); + put_varint(operand as i32, out); + } + // Two operand codes + OpCode::FON + | OpCode::MOV + | OpCode::AMp + | OpCode::NCJ + | OpCode::ECJ + | OpCode::FLJ + | OpCode::ITJ + | OpCode::SKJ + | OpCode::AMJ + | OpCode::SMP + | OpCode::PSp + | OpCode::IpN + | OpCode::FFm => { + let operand_a = read_number(iter)?; + if strict { + expect_char(b':', iter)?; + } else { + iter.next().ok_or_else(|| ParseError("Script unexpectedly ended.".to_owned()))?; + } + let operand_b = read_number(iter)?; + + put_varint(instr as i32, out); + put_varint(operand_a as i32, out); + put_varint(operand_b as i32, out); + } + // Three operand codes + OpCode::ANP | OpCode::CNP | OpCode::INP | OpCode::TAM | OpCode::CMP | OpCode::INJ => { + let operand_a = read_number(iter)?; + if strict { + expect_char(b':', iter)?; + } else { + iter.next().ok_or_else(|| ParseError("Script unexpectedly ended.".to_owned()))?; + } + let operand_b = read_number(iter)?; + if strict { + expect_char(b':', iter)?; + } else { + iter.next().ok_or_else(|| ParseError("Script unexpectedly ended.".to_owned()))?; + } + let operand_c = read_number(iter)?; + + put_varint(instr as i32, out); + put_varint(operand_a as i32, out); + put_varint(operand_b as i32, out); + put_varint(operand_c as i32, out); + } + // Four operand codes + OpCode::TRA | OpCode::MNP | OpCode::SNP => { + let operand_a = read_number(iter)?; + if strict { + expect_char(b':', iter)?; + } else { + iter.next().ok_or_else(|| ParseError("Script unexpectedly ended.".to_owned()))?; + } + let operand_b = read_number(iter)?; + if strict { + expect_char(b':', iter)?; + } else { + iter.next().ok_or_else(|| ParseError("Script unexpectedly ended.".to_owned()))?; + } + let operand_c = read_number(iter)?; + if strict { + expect_char(b':', iter)?; + } else { + iter.next().ok_or_else(|| ParseError("Script unexpectedly ended.".to_owned()))?; + } + let operand_d = read_number(iter)?; + + put_varint(instr as i32, out); + put_varint(operand_a as i32, out); + put_varint(operand_b as i32, out); + put_varint(operand_c as i32, out); + put_varint(operand_d as i32, out); + } + OpCode::_NOP | OpCode::_UNI | OpCode::_STR | OpCode::_END => { + unreachable!() + } + } + + Ok(()) + } +} diff --git a/src/scripting/tsc/credit_script.rs b/src/scripting/tsc/credit_script.rs new file mode 100644 index 0000000..6b2b80f --- /dev/null +++ b/src/scripting/tsc/credit_script.rs @@ -0,0 +1,3 @@ +pub struct CreditScript { + +} diff --git a/src/scripting/tsc/decompiler.rs b/src/scripting/tsc/decompiler.rs new file mode 100644 index 0000000..687200b --- /dev/null +++ b/src/scripting/tsc/decompiler.rs @@ -0,0 +1,186 @@ +use std::io::Cursor; + +use num_traits::FromPrimitive; + +use crate::framework::error::GameError::InvalidValue; +use crate::framework::error::GameResult; +use crate::scripting::tsc::bytecode_utils::read_cur_varint; +use crate::scripting::tsc::opcodes::OpCode; +use crate::scripting::tsc::text_script::TextScript; + +impl TextScript { + pub fn decompile_event(&self, id: u16) -> GameResult { + if let Some(bytecode) = self.event_map.get(&id) { + let mut result = String::new(); + let mut cursor = Cursor::new(bytecode); + + while let Ok(op_num) = read_cur_varint(&mut cursor) { + let op_maybe: Option = FromPrimitive::from_i32(op_num); + + if let Some(op) = op_maybe { + match op { + // Zero operand codes + OpCode::AEp + | OpCode::CAT + | OpCode::CIL + | OpCode::CLO + | OpCode::CLR + | OpCode::CPS + | OpCode::CRE + | OpCode::CSS + | OpCode::END + | OpCode::ESC + | OpCode::FLA + | OpCode::FMU + | OpCode::FRE + | OpCode::HMC + | OpCode::INI + | OpCode::KEY + | OpCode::LDP + | OpCode::MLP + | OpCode::MM0 + | OpCode::MNA + | OpCode::MS2 + | OpCode::MS3 + | OpCode::MSG + | OpCode::NOD + | OpCode::PRI + | OpCode::RMU + | OpCode::SAT + | OpCode::SLP + | OpCode::SMC + | OpCode::SPS + | OpCode::STC + | OpCode::SVP + | OpCode::TUR + | OpCode::WAS + | OpCode::ZAM + | OpCode::HM2 + | OpCode::POP + | OpCode::KE2 + | OpCode::FR2 => { + result.push_str(format!("{:?}()\n", op).as_str()); + } + // One operand codes + OpCode::BOA + | OpCode::BSL + | OpCode::FOB + | OpCode::FOM + | OpCode::QUA + | OpCode::UNI + | OpCode::MYB + | OpCode::MYD + | OpCode::FAI + | OpCode::FAO + | OpCode::WAI + | OpCode::FAC + | OpCode::GIT + | OpCode::NUM + | OpCode::DNA + | OpCode::DNP + | OpCode::FLm + | OpCode::FLp + | OpCode::MPp + | OpCode::SKm + | OpCode::SKp + | OpCode::EQp + | OpCode::EQm + | OpCode::MLp + | OpCode::ITp + | OpCode::ITm + | OpCode::AMm + | OpCode::UNJ + | OpCode::MPJ + | OpCode::YNJ + | OpCode::EVE + | OpCode::XX1 + | OpCode::SIL + | OpCode::LIp + | OpCode::SOU + | OpCode::CMU + | OpCode::SSS + | OpCode::ACH + | OpCode::S2MV + | OpCode::S2PJ + | OpCode::PSH => { + let par_a = read_cur_varint(&mut cursor)?; + + result.push_str(format!("{:?}({})\n", op, par_a).as_str()); + } + // Two operand codes + OpCode::FON + | OpCode::MOV + | OpCode::AMp + | OpCode::NCJ + | OpCode::ECJ + | OpCode::FLJ + | OpCode::ITJ + | OpCode::SKJ + | OpCode::AMJ + | OpCode::SMP + | OpCode::PSp + | OpCode::IpN + | OpCode::FFm => { + let par_a = read_cur_varint(&mut cursor)?; + let par_b = read_cur_varint(&mut cursor)?; + + result.push_str(format!("{:?}({}, {})\n", op, par_a, par_b).as_str()); + } + // Three operand codes + OpCode::ANP | OpCode::CNP | OpCode::INP | OpCode::TAM | OpCode::CMP | OpCode::INJ => { + let par_a = read_cur_varint(&mut cursor)?; + let par_b = read_cur_varint(&mut cursor)?; + let par_c = read_cur_varint(&mut cursor)?; + + result.push_str(format!("{:?}({}, {}, {})\n", op, par_a, par_b, par_c).as_str()); + } + // Four operand codes + OpCode::TRA | OpCode::MNP | OpCode::SNP => { + let par_a = read_cur_varint(&mut cursor)?; + let par_b = read_cur_varint(&mut cursor)?; + let par_c = read_cur_varint(&mut cursor)?; + let par_d = read_cur_varint(&mut cursor)?; + + result.push_str(format!("{:?}({}, {}, {}, {})\n", op, par_a, par_b, par_c, par_d).as_str()); + } + OpCode::_STR => { + let len = read_cur_varint(&mut cursor)?; + + result.push_str(format!("%string(len = {}, value = \"", len).as_str()); + for _ in 0..len { + let chr = std::char::from_u32(read_cur_varint(&mut cursor)? as u32).unwrap_or('?'); + match chr { + '\n' => { + result.push_str("\\n"); + } + '\r' => { + result.push_str("\\r"); + } + '\t' => { + result.push_str("\\t"); + } + '\u{0000}'..='\u{001f}' | '\u{0080}'..='\u{ffff}' => { + result.push_str(chr.escape_unicode().to_string().as_str()); + } + _ => { + result.push(chr); + } + } + } + result.push_str("\")\n"); + } + OpCode::_NOP => result.push_str("%no_op()\n"), + OpCode::_UNI => result.push_str("%unimplemented()\n"), + OpCode::_END => result.push_str("%end_marker()\n"), + } + } else { + break; + } + } + + Ok(result) + } else { + Err(InvalidValue("Unknown script.".to_string())) + } + } +} diff --git a/src/scripting/tsc/mod.rs b/src/scripting/tsc/mod.rs new file mode 100644 index 0000000..ee034a0 --- /dev/null +++ b/src/scripting/tsc/mod.rs @@ -0,0 +1,7 @@ +mod bytecode_utils; +mod compiler; +pub mod credit_script; +mod decompiler; +mod opcodes; +mod parse_utils; +pub mod text_script; diff --git a/src/scripting/tsc/opcodes.rs b/src/scripting/tsc/opcodes.rs new file mode 100644 index 0000000..15c0092 --- /dev/null +++ b/src/scripting/tsc/opcodes.rs @@ -0,0 +1,269 @@ +use num_derive::FromPrimitive; + +/// Engine's text script VM operation codes. +#[derive(EnumString, Debug, FromPrimitive, PartialEq)] +#[repr(i32)] +pub enum OpCode { + // ---- Internal opcodes (used by bytecode, no TSC representation) + /// internal: no operation + _NOP = 0, + /// internal: unimplemented + _UNI, + /// internal: string marker + _STR, + /// internal: implicit END marker + _END, + + // ---- Vanilla opcodes ---- + /// >(expect: u8, iter: &mut I) -> GameResult { + let res = iter.next(); + + match res { + Some(n) if n == expect => Ok(()), + Some(n) => Err(ParseError(format!("Expected {}, found {}", expect as char, n as char))), + None => Err(ParseError("Script unexpectedly ended.".to_string())), + } +} + +pub fn skip_until>(expect: u8, iter: &mut Peekable) -> GameResult { + while let Some(&chr) = iter.peek() { + if chr == expect { + return Ok(()); + } else { + iter.next(); + } + } + + Err(ParseError("Script unexpectedly ended.".to_string())) +} + +/// Reads a 4 digit TSC formatted number from iterator. +/// Intentionally does no '0'..'9' range checking, since it was often exploited by modders. +pub fn read_number>(iter: &mut Peekable) -> GameResult { + Some(0) + .and_then(|result| iter.next().map(|v| result + 1000 * v.wrapping_sub(b'0') as i32)) + .and_then(|result| iter.next().map(|v| result + 100 * v.wrapping_sub(b'0') as i32)) + .and_then(|result| iter.next().map(|v| result + 10 * v.wrapping_sub(b'0') as i32)) + .and_then(|result| iter.next().map(|v| result + v.wrapping_sub(b'0') as i32)) + .ok_or_else(|| ParseError("Script unexpectedly ended.".to_string())) +} diff --git a/src/text_script.rs b/src/scripting/tsc/text_script.rs similarity index 69% rename from src/text_script.rs rename to src/scripting/tsc/text_script.rs index 48f0f9b..35d17f0 100644 --- a/src/text_script.rs +++ b/src/scripting/tsc/text_script.rs @@ -4,310 +4,37 @@ use std::io; use std::io::Cursor; use std::io::Seek; use std::io::SeekFrom; -use std::iter::Peekable; use std::ops::Not; -use std::str::FromStr; -use byteorder::ReadBytesExt; -use itertools::Itertools; -use num_derive::FromPrimitive; use num_traits::{clamp, FromPrimitive}; use crate::bitfield; +use crate::common::Direction::{Left, Right}; use crate::common::{Direction, FadeDirection, FadeState, Rect}; -use crate::encoding::{read_cur_shift_jis, read_cur_wtf8}; use crate::engine_constants::EngineConstants; use crate::entity::GameEntity; use crate::frame::UpdateTarget; use crate::framework::context::Context; -use crate::framework::error::GameError::{InvalidValue, ParseError}; use crate::framework::error::GameResult; use crate::input::touch_controls::TouchControlType; use crate::npc::NPC; use crate::player::{ControlMode, TargetPlayer}; use crate::scene::game_scene::GameScene; use crate::scene::title_scene::TitleScene; +use crate::scripting::tsc::bytecode_utils::read_cur_varint; +use crate::scripting::tsc::opcodes::OpCode; use crate::shared_game_state::SharedGameState; -use crate::str; use crate::weapon::WeaponType; -use crate::common::Direction::{Left, Right}; - -/// Engine's text script VM operation codes. -#[derive(EnumString, Debug, FromPrimitive, PartialEq)] -#[repr(i32)] -pub enum OpCode { - // ---- Internal opcodes (used by bytecode, no TSC representation) - /// internal: no operation - _NOP = 0, - /// internal: unimplemented - _UNI, - /// internal: string marker - _STR, - /// internal: implicit END marker - _END, - - // ---- Vanilla opcodes ---- - /// Self { - TextScriptVM::new() - } -} - pub struct Scripts { /// Head.tsc - shared part of map scripts pub global_script: TextScript, @@ -434,21 +155,6 @@ impl Scripts { } } -fn read_cur_varint(cursor: &mut Cursor<&Vec>) -> GameResult { - let mut result = 0u32; - - for o in 0..5 { - let n = cursor.read_u8()?; - result |= (n as u32 & 0x7f) << (o * 7); - - if n & 0x80 == 0 { - break; - } - } - - Ok(((result << 31) ^ (result >> 1)) as i32) -} - impl TextScriptVM { pub fn new() -> Self { Self { @@ -609,12 +315,14 @@ impl TextScriptVM { } if remaining > 1 { - let ticks = if state.textscript_vm.flags.fast() || state.textscript_vm.flags.cutscene_skip() { + let ticks = if state.textscript_vm.flags.fast() || state.textscript_vm.flags.cutscene_skip() + { 0 - } else if remaining != 2 && (game_scene.player1.controller.jump() - || game_scene.player1.controller.shoot() - || game_scene.player2.controller.jump() - || game_scene.player2.controller.shoot()) + } else if remaining != 2 + && (game_scene.player1.controller.jump() + || game_scene.player1.controller.shoot() + || game_scene.player2.controller.jump() + || game_scene.player2.controller.shoot()) { state.constants.textscript.text_speed_fast } else { @@ -886,7 +594,6 @@ impl TextScriptVM { game_scene.player1.cond.set_interacted(new_direction == 3); game_scene.player2.cond.set_interacted(new_direction == 3); - game_scene.player1.vel_x = 0; game_scene.player2.vel_x = 0; @@ -926,11 +633,11 @@ impl TextScriptVM { } Direction::FacingPlayer => { for npc in game_scene.npc_list.iter_alive() { - if npc.event_num == new_direction as u16{ + if npc.event_num == new_direction as u16 { if game_scene.player1.x >= npc.x { game_scene.player1.direction = Left; game_scene.player1.vel_x = 0x200; - }else{ + } else { game_scene.player1.direction = Right; game_scene.player1.vel_x = -0x200; } @@ -938,7 +645,7 @@ impl TextScriptVM { if game_scene.player2.x >= npc.x { game_scene.player2.direction = Left; game_scene.player2.vel_x = 0x200; - }else{ + } else { game_scene.player2.direction = Right; game_scene.player2.vel_x = -0x200; } @@ -1225,7 +932,7 @@ impl TextScriptVM { let index = read_cur_varint(&mut cursor)? as usize; if let Some(num) = state.textscript_vm.numbers.get(index) { - let mut str = num.to_string().chars().collect_vec(); + let mut str = num.to_string().chars().collect(); match state.textscript_vm.current_line { TextScriptLine::Line1 => state.textscript_vm.line_1.append(&mut str), @@ -1797,7 +1504,7 @@ impl TextScriptVM { } pub struct TextScript { - event_map: HashMap>, + pub(in crate::scripting::tsc) event_map: HashMap>, } impl Clone for TextScript { @@ -1840,595 +1547,12 @@ impl TextScript { } pub fn get_event_ids(&self) -> Vec { - self.event_map.keys().copied().sorted().collect_vec() - } - - /// Compiles a decrypted text script data into internal bytecode. - pub fn compile(data: &[u8], strict: bool, encoding: TextScriptEncoding) -> GameResult { - let mut event_map = HashMap::new(); - let mut iter = data.iter().copied().peekable(); - let mut last_event = 0; - - while let Some(&chr) = iter.peek() { - match chr { - b'#' => { - iter.next(); - let event_num = TextScript::read_number(&mut iter)? as u16; - if iter.peek().is_some() { - TextScript::skip_until(b'\n', &mut iter)?; - iter.next(); - } - last_event = event_num; - - if event_map.contains_key(&event_num) { - if strict { - return Err(ParseError(format!("Event {} has been defined twice.", event_num))); - } - - match TextScript::skip_until(b'#', &mut iter).ok() { - Some(_) => { - continue; - } - None => { - break; - } - } - } - - let bytecode = TextScript::compile_event(&mut iter, strict, encoding)?; - log::info!("Successfully compiled event #{} ({} bytes generated).", event_num, bytecode.len()); - event_map.insert(event_num, bytecode); - } - b'\r' | b'\n' | b' ' | b'\t' => { - iter.next(); - } - n => { - // CS+ boss rush is the buggiest shit ever. - if !strict && last_event == 0 { - iter.next(); - continue; - } - - return Err(ParseError(format!("Unexpected token in event {}: {}", last_event, n as char))); - } - } - } - - Ok(TextScript { event_map }) - } - - fn compile_event>( - iter: &mut Peekable, - strict: bool, - encoding: TextScriptEncoding, - ) -> GameResult> { - let mut bytecode = Vec::new(); - let mut char_buf = Vec::with_capacity(16); - let mut allow_next_event = true; - - while let Some(&chr) = iter.peek() { - match chr { - b'#' if allow_next_event => { - if !char_buf.is_empty() { - TextScript::put_string(&mut char_buf, &mut bytecode, encoding); - } - - // some events end without { - allow_next_event = false; - if char_buf.len() > 2 { - if let Some(&c) = char_buf.last() { - if c == b'\n' { - let _ = char_buf.pop(); - } - } - } - - if !char_buf.is_empty() { - TextScript::put_string(&mut char_buf, &mut bytecode, encoding); - } - - iter.next(); - let n = iter - .next_tuple::<(u8, u8, u8)>() - .map(|t| [t.0, t.1, t.2]) - .ok_or_else(|| ParseError(str!("Script unexpectedly ended.")))?; - - let code = String::from_utf8_lossy(&n); - - TextScript::compile_code(code.as_ref(), strict, iter, &mut bytecode)?; - } - b'\r' => { - iter.next(); - } - b'\n' => { - allow_next_event = true; - char_buf.push(chr); - - iter.next(); - } - _ => { - allow_next_event = false; - char_buf.push(chr); - - iter.next(); - } - } - } - - Ok(bytecode) - } - - fn put_string(buffer: &mut Vec, out: &mut Vec, encoding: TextScriptEncoding) { - if buffer.len() == 0 { - return; - } - - let mut cursor: Cursor<&Vec> = Cursor::new(buffer); - let mut tmp_buf = Vec::new(); - let mut remaining = buffer.len() as u32; - let mut chars = 0; - - while remaining > 0 { - let (consumed, chr) = match encoding { - TextScriptEncoding::UTF8 => read_cur_wtf8(&mut cursor, remaining), - TextScriptEncoding::ShiftJIS => read_cur_shift_jis(&mut cursor, remaining), - }; - - remaining -= consumed; - chars += 1; - - TextScript::put_varint(chr as i32, &mut tmp_buf); - } - - buffer.clear(); - - TextScript::put_varint(OpCode::_STR as i32, out); - TextScript::put_varint(chars, out); - out.append(&mut tmp_buf); - } - - fn put_varint(val: i32, out: &mut Vec) { - let mut x = ((val as u32) >> 31) ^ ((val as u32) << 1); - - loop { - let mut n = (x & 0x7f) as u8; - x >>= 7; - - if x != 0 { - n |= 0x80; - } - - out.push(n); - - if x == 0 { - break; - } - } - } - - #[allow(unused)] - fn read_varint>(iter: &mut I) -> GameResult { - let mut result = 0u32; - - for o in 0..5 { - let n = iter.next().ok_or_else(|| ParseError(str!("Script unexpectedly ended.")))?; - result |= (n as u32 & 0x7f) << (o * 7); - - if n & 0x80 == 0 { - break; - } - } - - Ok(((result << 31) ^ (result >> 1)) as i32) - } - - fn compile_code>( - code: &str, - strict: bool, - iter: &mut Peekable, - out: &mut Vec, - ) -> GameResult { - let instr = OpCode::from_str(code).map_err(|_| ParseError(format!("Unknown opcode: {}", code)))?; - - match instr { - // Zero operand codes - OpCode::AEp - | OpCode::CAT - | OpCode::CIL - | OpCode::CLO - | OpCode::CLR - | OpCode::CPS - | OpCode::CRE - | OpCode::CSS - | OpCode::END - | OpCode::ESC - | OpCode::FLA - | OpCode::FMU - | OpCode::FRE - | OpCode::HMC - | OpCode::INI - | OpCode::KEY - | OpCode::LDP - | OpCode::MLP - | OpCode::MM0 - | OpCode::MNA - | OpCode::MS2 - | OpCode::MS3 - | OpCode::MSG - | OpCode::NOD - | OpCode::PRI - | OpCode::RMU - | OpCode::SAT - | OpCode::SLP - | OpCode::SMC - | OpCode::SPS - | OpCode::STC - | OpCode::SVP - | OpCode::TUR - | OpCode::WAS - | OpCode::ZAM - | OpCode::HM2 - | OpCode::POP - | OpCode::KE2 - | OpCode::FR2 => { - TextScript::put_varint(instr as i32, out); - } - // One operand codes - OpCode::BOA - | OpCode::BSL - | OpCode::FOB - | OpCode::FOM - | OpCode::QUA - | OpCode::UNI - | OpCode::MYB - | OpCode::MYD - | OpCode::FAI - | OpCode::FAO - | OpCode::WAI - | OpCode::FAC - | OpCode::GIT - | OpCode::NUM - | OpCode::DNA - | OpCode::DNP - | OpCode::FLm - | OpCode::FLp - | OpCode::MPp - | OpCode::SKm - | OpCode::SKp - | OpCode::EQp - | OpCode::EQm - | OpCode::MLp - | OpCode::ITp - | OpCode::ITm - | OpCode::AMm - | OpCode::UNJ - | OpCode::MPJ - | OpCode::YNJ - | OpCode::EVE - | OpCode::XX1 - | OpCode::SIL - | OpCode::LIp - | OpCode::SOU - | OpCode::CMU - | OpCode::SSS - | OpCode::ACH - | OpCode::S2MV - | OpCode::S2PJ - | OpCode::PSH => { - let operand = TextScript::read_number(iter)?; - TextScript::put_varint(instr as i32, out); - TextScript::put_varint(operand as i32, out); - } - // Two operand codes - OpCode::FON - | OpCode::MOV - | OpCode::AMp - | OpCode::NCJ - | OpCode::ECJ - | OpCode::FLJ - | OpCode::ITJ - | OpCode::SKJ - | OpCode::AMJ - | OpCode::SMP - | OpCode::PSp - | OpCode::IpN - | OpCode::FFm => { - let operand_a = TextScript::read_number(iter)?; - if strict { - TextScript::expect_char(b':', iter)?; - } else { - iter.next().ok_or_else(|| ParseError(str!("Script unexpectedly ended.")))?; - } - let operand_b = TextScript::read_number(iter)?; - - TextScript::put_varint(instr as i32, out); - TextScript::put_varint(operand_a as i32, out); - TextScript::put_varint(operand_b as i32, out); - } - // Three operand codes - OpCode::ANP | OpCode::CNP | OpCode::INP | OpCode::TAM | OpCode::CMP | OpCode::INJ => { - let operand_a = TextScript::read_number(iter)?; - if strict { - TextScript::expect_char(b':', iter)?; - } else { - iter.next().ok_or_else(|| ParseError(str!("Script unexpectedly ended.")))?; - } - let operand_b = TextScript::read_number(iter)?; - if strict { - TextScript::expect_char(b':', iter)?; - } else { - iter.next().ok_or_else(|| ParseError(str!("Script unexpectedly ended.")))?; - } - let operand_c = TextScript::read_number(iter)?; - - TextScript::put_varint(instr as i32, out); - TextScript::put_varint(operand_a as i32, out); - TextScript::put_varint(operand_b as i32, out); - TextScript::put_varint(operand_c as i32, out); - } - // Four operand codes - OpCode::TRA | OpCode::MNP | OpCode::SNP => { - let operand_a = TextScript::read_number(iter)?; - if strict { - TextScript::expect_char(b':', iter)?; - } else { - iter.next().ok_or_else(|| ParseError(str!("Script unexpectedly ended.")))?; - } - let operand_b = TextScript::read_number(iter)?; - if strict { - TextScript::expect_char(b':', iter)?; - } else { - iter.next().ok_or_else(|| ParseError(str!("Script unexpectedly ended.")))?; - } - let operand_c = TextScript::read_number(iter)?; - if strict { - TextScript::expect_char(b':', iter)?; - } else { - iter.next().ok_or_else(|| ParseError(str!("Script unexpectedly ended.")))?; - } - let operand_d = TextScript::read_number(iter)?; - - TextScript::put_varint(instr as i32, out); - TextScript::put_varint(operand_a as i32, out); - TextScript::put_varint(operand_b as i32, out); - TextScript::put_varint(operand_c as i32, out); - TextScript::put_varint(operand_d as i32, out); - } - OpCode::_NOP | OpCode::_UNI | OpCode::_STR | OpCode::_END => { - unreachable!() - } - } - - Ok(()) - } - - pub fn decompile_event(&self, id: u16) -> GameResult { - if let Some(bytecode) = self.event_map.get(&id) { - let mut result = String::new(); - let mut cursor = Cursor::new(bytecode); - - while let Ok(op_num) = read_cur_varint(&mut cursor) { - let op_maybe: Option = FromPrimitive::from_i32(op_num); - - if let Some(op) = op_maybe { - match op { - // Zero operand codes - OpCode::AEp - | OpCode::CAT - | OpCode::CIL - | OpCode::CLO - | OpCode::CLR - | OpCode::CPS - | OpCode::CRE - | OpCode::CSS - | OpCode::END - | OpCode::ESC - | OpCode::FLA - | OpCode::FMU - | OpCode::FRE - | OpCode::HMC - | OpCode::INI - | OpCode::KEY - | OpCode::LDP - | OpCode::MLP - | OpCode::MM0 - | OpCode::MNA - | OpCode::MS2 - | OpCode::MS3 - | OpCode::MSG - | OpCode::NOD - | OpCode::PRI - | OpCode::RMU - | OpCode::SAT - | OpCode::SLP - | OpCode::SMC - | OpCode::SPS - | OpCode::STC - | OpCode::SVP - | OpCode::TUR - | OpCode::WAS - | OpCode::ZAM - | OpCode::HM2 - | OpCode::POP - | OpCode::KE2 - | OpCode::FR2 => { - result.push_str(format!("{:?}()\n", op).as_str()); - } - // One operand codes - OpCode::BOA - | OpCode::BSL - | OpCode::FOB - | OpCode::FOM - | OpCode::QUA - | OpCode::UNI - | OpCode::MYB - | OpCode::MYD - | OpCode::FAI - | OpCode::FAO - | OpCode::WAI - | OpCode::FAC - | OpCode::GIT - | OpCode::NUM - | OpCode::DNA - | OpCode::DNP - | OpCode::FLm - | OpCode::FLp - | OpCode::MPp - | OpCode::SKm - | OpCode::SKp - | OpCode::EQp - | OpCode::EQm - | OpCode::MLp - | OpCode::ITp - | OpCode::ITm - | OpCode::AMm - | OpCode::UNJ - | OpCode::MPJ - | OpCode::YNJ - | OpCode::EVE - | OpCode::XX1 - | OpCode::SIL - | OpCode::LIp - | OpCode::SOU - | OpCode::CMU - | OpCode::SSS - | OpCode::ACH - | OpCode::S2MV - | OpCode::S2PJ - | OpCode::PSH => { - let par_a = read_cur_varint(&mut cursor)?; - - result.push_str(format!("{:?}({})\n", op, par_a).as_str()); - } - // Two operand codes - OpCode::FON - | OpCode::MOV - | OpCode::AMp - | OpCode::NCJ - | OpCode::ECJ - | OpCode::FLJ - | OpCode::ITJ - | OpCode::SKJ - | OpCode::AMJ - | OpCode::SMP - | OpCode::PSp - | OpCode::IpN - | OpCode::FFm => { - let par_a = read_cur_varint(&mut cursor)?; - let par_b = read_cur_varint(&mut cursor)?; - - result.push_str(format!("{:?}({}, {})\n", op, par_a, par_b).as_str()); - } - // Three operand codes - OpCode::ANP | OpCode::CNP | OpCode::INP | OpCode::TAM | OpCode::CMP | OpCode::INJ => { - let par_a = read_cur_varint(&mut cursor)?; - let par_b = read_cur_varint(&mut cursor)?; - let par_c = read_cur_varint(&mut cursor)?; - - result.push_str(format!("{:?}({}, {}, {})\n", op, par_a, par_b, par_c).as_str()); - } - // Four operand codes - OpCode::TRA | OpCode::MNP | OpCode::SNP => { - let par_a = read_cur_varint(&mut cursor)?; - let par_b = read_cur_varint(&mut cursor)?; - let par_c = read_cur_varint(&mut cursor)?; - let par_d = read_cur_varint(&mut cursor)?; - - result.push_str(format!("{:?}({}, {}, {}, {})\n", op, par_a, par_b, par_c, par_d).as_str()); - } - OpCode::_STR => { - let len = read_cur_varint(&mut cursor)?; - - result.push_str(format!("%string(len = {}, value = \"", len).as_str()); - for _ in 0..len { - let chr = std::char::from_u32(read_cur_varint(&mut cursor)? as u32).unwrap_or('?'); - match chr { - '\n' => { - result.push_str("\\n"); - } - '\r' => { - result.push_str("\\r"); - } - '\t' => { - result.push_str("\\t"); - } - '\u{0000}'..='\u{001f}' | '\u{0080}'..='\u{ffff}' => { - result.push_str(chr.escape_unicode().to_string().as_str()); - } - _ => { - result.push(chr); - } - } - } - result.push_str("\")\n"); - } - OpCode::_NOP => result.push_str("%no_op()\n"), - OpCode::_UNI => result.push_str("%unimplemented()\n"), - OpCode::_END => result.push_str("%end_marker()\n"), - } - } else { - break; - } - } - - Ok(result) - } else { - Err(InvalidValue("Unknown script.".to_string())) - } - } - - fn expect_char>(expect: u8, iter: &mut I) -> GameResult { - let res = iter.next(); - - match res { - Some(n) if n == expect => Ok(()), - Some(n) => Err(ParseError(format!("Expected {}, found {}", expect as char, n as char))), - None => Err(ParseError(str!("Script unexpectedly ended."))), - } - } - - fn skip_until>(expect: u8, iter: &mut Peekable) -> GameResult { - while let Some(&chr) = iter.peek() { - if chr == expect { - return Ok(()); - } else { - iter.next(); - } - } - - Err(ParseError(str!("Script unexpectedly ended."))) - } - - /// Reads a 4 digit TSC formatted number from iterator. - /// Intentionally does no '0'..'9' range checking, since it was often exploited by modders. - fn read_number>(iter: &mut Peekable) -> GameResult { - Some(0) - .and_then(|result| iter.next().map(|v| result + 1000 * v.wrapping_sub(b'0') as i32)) - .and_then(|result| iter.next().map(|v| result + 100 * v.wrapping_sub(b'0') as i32)) - .and_then(|result| iter.next().map(|v| result + 10 * v.wrapping_sub(b'0') as i32)) - .and_then(|result| iter.next().map(|v| result + v.wrapping_sub(b'0') as i32)) - .ok_or_else(|| ParseError(str!("Script unexpectedly ended."))) + let mut vec: Vec = self.event_map.keys().copied().collect(); + vec.sort(); + vec } pub fn has_event(&self, id: u16) -> bool { self.event_map.contains_key(&id) } } - -#[test] -fn test_varint() { - for n in -4000..=4000 { - let mut out = Vec::new(); - TextScript::put_varint(n, &mut out); - - let result = TextScript::read_varint(&mut out.iter().copied()).unwrap(); - assert_eq!(result, n); - let mut cur = Cursor::new(&out); - let result = read_cur_varint(&mut cur).unwrap(); - assert_eq!(result, n); - } -} diff --git a/src/shared_game_state.rs b/src/shared_game_state.rs index e39b89d..7b6150e 100644 --- a/src/shared_game_state.rs +++ b/src/shared_game_state.rs @@ -23,13 +23,12 @@ use crate::rng::XorShift; use crate::scene::game_scene::GameScene; use crate::scene::title_scene::TitleScene; use crate::scene::Scene; -#[cfg(feature = "scripting")] -use crate::scripting::LuaScriptingState; +#[cfg(feature = "scripting-lua")] +use crate::scripting::lua::LuaScriptingState; use crate::settings::Settings; use crate::sound::SoundManager; use crate::stage::StageData; -use crate::str; -use crate::text_script::{ScriptMode, TextScriptExecutionState, TextScriptVM}; +use crate::scripting::tsc::text_script::{ScriptMode, TextScriptExecutionState, TextScriptVM}; use crate::texture_set::TextureSet; #[derive(PartialEq, Eq, Copy, Clone, serde::Serialize, serde::Deserialize)] @@ -143,7 +142,7 @@ pub struct SharedGameState { pub constants: EngineConstants, pub font: BMFontRenderer, pub texture_set: TextureSet, - #[cfg(feature = "scripting")] + #[cfg(feature = "scripting-lua")] pub lua: LuaScriptingState, pub sound_manager: SoundManager, pub settings: Settings, @@ -223,7 +222,7 @@ impl SharedGameState { teleporter_slots: Vec::with_capacity(8), carets: Vec::with_capacity(32), touch_controls: TouchControls::new(), - base_path: str!(base_path), + base_path: base_path.to_owned(), npc_table: NPCTable::new(), npc_super_pos: (0, 0), npc_curly_target: (0, 0), @@ -243,7 +242,7 @@ impl SharedGameState { constants, font, texture_set, - #[cfg(feature = "scripting")] + #[cfg(feature = "scripting-lua")] lua: LuaScriptingState::new(), sound_manager, settings, @@ -290,7 +289,7 @@ impl SharedGameState { pub fn start_new_game(&mut self, ctx: &mut Context) -> GameResult { self.reset(); - #[cfg(feature = "scripting")] + #[cfg(feature = "scripting-lua")] self.lua.reload_scripts(ctx)?; let mut next_scene = GameScene::new(self, ctx, self.constants.game.new_game_stage as usize)?; @@ -309,7 +308,7 @@ impl SharedGameState { } pub fn start_intro(&mut self, ctx: &mut Context) -> GameResult { - #[cfg(feature = "scripting")] + #[cfg(feature = "scripting-lua")] self.lua.reload_scripts(ctx)?; let start_stage_id = self.constants.game.intro_stage as usize; @@ -356,7 +355,7 @@ impl SharedGameState { profile.apply(self, &mut next_scene, ctx); - #[cfg(feature = "scripting")] + #[cfg(feature = "scripting-lua")] self.lua.reload_scripts(ctx)?; self.next_scene = Some(Box::new(next_scene)); diff --git a/src/sound/mod.rs b/src/sound/mod.rs index c1107d0..50f75ee 100644 --- a/src/sound/mod.rs +++ b/src/sound/mod.rs @@ -24,7 +24,6 @@ use crate::sound::org_playback::{OrgPlaybackEngine, SavedOrganyaPlaybackState}; use crate::sound::organya::Song; use crate::sound::pixtone::{PixToneParameters, PixTonePlayback}; use crate::sound::wave_bank::SoundBank; -use crate::str; mod fir; #[cfg(feature = "ogg-playback")] @@ -73,7 +72,7 @@ impl SoundManager { let host = cpal::default_host(); let device = - host.default_output_device().ok_or_else(|| AudioError(str!("Error initializing audio device.")))?; + host.default_output_device().ok_or_else(|| AudioError("Error initializing audio device.".to_owned()))?; let config = device.default_output_config()?; let bnk = wave_bank::SoundBank::load_from(filesystem::open(ctx, "/builtin/organya-wavetable-doukutsu.bin")?)?; @@ -284,7 +283,7 @@ impl SoundManager { } if speed <= 0.0 { - return Err(InvalidValue(str!("Speed must be bigger than 0.0!"))); + return Err(InvalidValue("Speed must be bigger than 0.0!".to_owned())); } self.tx.send(PlaybackMessage::SetSpeed(speed))?; diff --git a/src/stage.rs b/src/stage.rs index 6a67301..6d79437 100644 --- a/src/stage.rs +++ b/src/stage.rs @@ -12,7 +12,7 @@ use crate::framework::error::GameError::ResourceLoadError; use crate::framework::error::GameResult; use crate::framework::filesystem; use crate::map::{Map, NPCData}; -use crate::text_script::TextScript; +use crate::scripting::tsc::text_script::TextScript; use crate::common::Color; #[derive(Debug, PartialEq, Eq, Hash)] diff --git a/src/texture_set.rs b/src/texture_set.rs index 11ad06b..d181b1a 100644 --- a/src/texture_set.rs +++ b/src/texture_set.rs @@ -15,7 +15,6 @@ use crate::framework::filesystem; use crate::framework::graphics::{create_texture, FilterMode}; use crate::settings::Settings; use crate::shared_game_state::Season; -use crate::str; pub static mut I_MAG: f32 = 1.0; pub static mut G_MAG: f32 = 1.0; @@ -412,7 +411,7 @@ impl TextureSet { if !self.tex_map.contains_key(name) { let batch = self.load_texture(ctx, constants, name)?; - self.tex_map.insert(str!(name), batch); + self.tex_map.insert(name.to_owned(), batch); } Ok(self.tex_map.get_mut(name).unwrap())