diff --git a/Cargo.toml b/Cargo.toml index 3da2635..9f43ed0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "re0box" -version = "0.5.0" +version = "0.5.2" authors = ["descawed "] edition = "2021" description = "An item box mod for Resident Evil 0" @@ -20,12 +20,12 @@ panic = "abort" [dependencies] anyhow = "1.0" -binrw = "0.14.1" +binrw = "0.15.0" configparser = "3.1" log = "0.4" -memchr = "2.7.4" +memchr = "2.8.0" simplelog = "0.12" -windows = { version = "0.59.0", features = [ "Win32_Foundation", "Win32_System_Diagnostics_Debug", "Win32_System_Memory", "Win32_System_ProcessStatus", "Win32_System_Kernel", "Win32_System_SystemServices", "Win32_System_Threading" ] } +windows = { version = "0.62.2", features = [ "Win32_Foundation", "Win32_System_Diagnostics_Debug", "Win32_System_Memory", "Win32_System_ProcessStatus", "Win32_System_Kernel", "Win32_System_SystemServices", "Win32_System_Threading" ] } [build-dependencies] winresource = "0.1" \ No newline at end of file diff --git a/README.md b/README.md index 032deb2..e47965a 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,14 @@ This section controls logging behavior. game folder, you can also use an absolute path for a different location, such as C:\Users\Bob\Documents\re0box.log. ## Uninstall -Delete scripts\re0box.asi from the Resident Evil 0 folder. None of the other mod files will have any effect once that's -gone, but if you want to purge everything, this is the full list of files added by the mod: +Run re0box_uninstall.bat to uninstall the mod. If for some reason you want to uninstall manually, this is the full list +of files added by the mod: - dinput8.dll (note that other mods may need this file. if you have anything in your scripts folder besides re0box.asi, you should leave this one alone.) - re0box.ini - re0box.log - re0box_readme.txt +- re0box_uninstall.bat - nativePC\arc\message\msg_chS_box.arc - nativePC\arc\message\msg_chT_box.arc - nativePC\arc\message\msg_eng_box.arc diff --git a/re0box_uninstall.bat b/re0box_uninstall.bat new file mode 100644 index 0000000..5da58c5 --- /dev/null +++ b/re0box_uninstall.bat @@ -0,0 +1,144 @@ +@echo off +setlocal EnableDelayedExpansion + +echo ======================================== +echo re0box uninstaller +echo ======================================== +echo. +echo This will delete the following files and folders: +echo - scripts\re0box.asi +echo - dinput8.dll (only if scripts folder contains no other ASI files) +echo - re0box.ini +echo - re0box.log +echo - re0box_readme.txt +echo - nativePC\arc\message\msg_chS_box.arc +echo - nativePC\arc\message\msg_chT_box.arc +echo - nativePC\arc\message\msg_eng_box.arc +echo - nativePC\arc\message\msg_fre_box.arc +echo - nativePC\arc\message\msg_ger_box.arc +echo - nativePC\arc\message\msg_ita_box.arc +echo - nativePC\arc\message\msg_jpn_box.arc +echo - nativePC\arc\message\msg_spa_box.arc +echo. +echo WARNING: If you have saved with the mod installed, uninstalling +echo will cause all item boxes to be deleted from all saves. +echo. +choice /C YN /N /M "Are you sure you want to uninstall? (Y/N): " +if errorlevel 2 ( + echo Uninstall cancelled. + pause + exit /b 0 +) + +echo. +echo Starting uninstall... +echo. + +REM Delete the main ASI file +if exist "scripts\re0box.asi" ( + del "scripts\re0box.asi" + if !errorlevel! equ 0 ( + echo Deleted: scripts\re0box.asi + ) else ( + echo ERROR: Failed to delete scripts\re0box.asi + ) +) else ( + echo Not found: scripts\re0box.asi +) + +REM Check if dinput8.dll should be deleted +set "delete_dinput8=1" +if exist "scripts\" ( + for /f "delims=" %%f in ('dir /b /a-d "scripts\*.asi" 2^>nul') do ( + echo Found other ASI file: scripts\%%f + set "delete_dinput8=0" + ) +) + +REM Check if scripts folder is empty and delete it if so +if exist "scripts\" ( + dir /b "scripts\" | findstr "^" >nul + if !errorlevel! neq 0 ( + rmdir "scripts" + if exist "scripts\" ( + echo ERROR: Failed to delete scripts folder + ) else ( + echo Deleted empty folder: scripts + ) + ) else ( + echo Keeping scripts folder ^(not empty^) + ) +) + +if "%delete_dinput8%"=="1" ( + if exist "dinput8.dll" ( + del "dinput8.dll" + if !errorlevel! equ 0 ( + echo Deleted: dinput8.dll + ) else ( + echo ERROR: Failed to delete dinput8.dll + ) + ) else ( + echo Not found: dinput8.dll + ) +) else ( + echo Skipping dinput8.dll ^(other ASI files detected in scripts folder^) +) + +REM Delete config and log files +if exist "re0box.ini" ( + del "re0box.ini" + if !errorlevel! equ 0 ( + echo Deleted: re0box.ini + ) else ( + echo ERROR: Failed to delete re0box.ini + ) +) else ( + echo Not found: re0box.ini +) + +if exist "re0box.log" ( + del "re0box.log" + if !errorlevel! equ 0 ( + echo Deleted: re0box.log + ) else ( + echo ERROR: Failed to delete re0box.log + ) +) else ( + echo Not found: re0box.log +) + +if exist "re0box_readme.txt" ( + del "re0box_readme.txt" + if !errorlevel! equ 0 ( + echo Deleted: re0box_readme.txt + ) else ( + echo ERROR: Failed to delete re0box_readme.txt + ) +) else ( + echo Not found: re0box_readme.txt +) + +REM Delete message arc files +set "msg_files=msg_chS_box.arc msg_chT_box.arc msg_eng_box.arc msg_fre_box.arc msg_ger_box.arc msg_ita_box.arc msg_jpn_box.arc msg_spa_box.arc" +for %%m in (%msg_files%) do ( + if exist "nativePC\arc\message\%%m" ( + del "nativePC\arc\message\%%m" + if !errorlevel! equ 0 ( + echo Deleted: nativePC\arc\message\%%m + ) else ( + echo ERROR: Failed to delete nativePC\arc\message\%%m + ) + ) else ( + echo Not found: nativePC\arc\message\%%m + ) +) + +echo. +echo Uninstall complete! +echo This uninstaller will now delete itself. +echo. +pause + +REM Self-delete using a temporary batch file +(goto) 2>nul & del "%~f0" diff --git a/src/inventory.rs b/src/inventory.rs index a54764f..ef8b2cd 100644 --- a/src/inventory.rs +++ b/src/inventory.rs @@ -165,13 +165,7 @@ impl ItemBox { None => self.items.len(), }; - // this shouldn't happen, but if somehow we ended up with an empty slot in the - // middle of the box, just delete it - if self.items[range_start].is_empty() { - self.items.remove(range_start); - } else { - self.items[range_start..range_end].rotate_left(1); - } + self.items[range_start..range_end].rotate_left(1); check_start = range_end; } else { @@ -259,10 +253,13 @@ impl ItemBox { // the view self.items.swap(box_index - 1, box_index + 1); } + // fix any misaligned two-slot items that might have been introduced by the insertion + self.fix_misaligned(box_index + 1); self.update_view(); if self.view.is_broken() { log::warn!( - "View is in a broken state after making room for two-slot item: {:?}", + "View is in a broken state after making room for two-slot item at index {}: {:?}", + index, self ); } @@ -434,6 +431,54 @@ mod tests { assert!(view.items[BAG_SIZE - 1].is_empty()); } + #[test] + fn make_room_for_double_with_two_slot_at_end() { + let mut item_box = ItemBox { + is_open: true, + items: vec![ + Item { id: 5, count: 0 }, + Item { id: 180, count: 1 }, + Item { id: 38, count: 3 }, + Item { id: 2, count: 1 }, + Item { id: 104, count: 1 }, + Item { id: 180, count: 1 }, + Item { id: 2, count: 1 }, + Item { id: 48, count: 1 }, + Item { id: 3, count: 0 }, + Item { id: 36, count: 6 }, + Item { id: 35, count: 16 }, + Item { id: 10, count: 8 }, + Item { id: 53, count: 1 }, + Item { id: 43, count: 1 }, + Item { id: 43, count: 1 }, + Item { id: 43, count: 1 }, + Item { id: 55, count: 12 }, + Item { id: 34, count: 1 }, + Item { id: 39, count: 12 }, + Item { id: 14, count: 3 }, + Item { id: 53, count: 1 } + ], + index: 0, + view: Bag { + unknown00: 0, + items: [ + Item { id: 5, count: 0 }, + Item { id: 180, count: 1 }, + Item { id: 38, count: 3 }, + Item { id: 2, count: 1 }, + Item { id: 104, count: 1 }, + Item { id: 180, count: 1 } + ], + personal_item: Item { id: 0, count: 0 }, + equipped_item_index: -1 + } + }; + assert!(item_box.view.is_valid()); + + item_box.make_room_for_double(2); + assert!(!item_box.view.is_broken()); + } + #[test] fn scroll() { let mut item_box = ItemBox::new(); diff --git a/src/lib.rs b/src/lib.rs index 573cca3..cf9b36b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ use std::str; use anyhow::Result; use configparser::ini::Ini; use simplelog::LevelFilter; -use windows::Win32::Foundation::{BOOL, HMODULE}; +use windows::Win32::Foundation::HMODULE; use windows::Win32::System::SystemServices::{DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH}; mod patch; @@ -528,10 +528,10 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { if is_enabled { log::info!("Item box mod is enabled; installing all hooks"); // when the game tries to display the partner's inventory, show the box instead if it's open - let bag_jump = jmp(version.get_partner_bag, get_partner_bag as usize); + let bag_jump = jmp(version.get_partner_bag, get_partner_bag as *const () as usize); patch(version.get_partner_bag, &bag_jump)?; - let partner_bag_org_call = call(version.get_partner_bag_org, get_box_if_open as usize); + let partner_bag_org_call = call(version.get_partner_bag_org, get_box_if_open as *const () as usize); patch(version.get_partner_bag_org, &partner_bag_org_call)?; // nop out call to GetCharacterBag which is now handled by get_box_if_open patch(version.get_partner_bag_org + 0x10, &[NOP; 5])?; @@ -541,19 +541,19 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { let msg_format = std::ptr::read_unaligned((version.msg_load1 + 1) as *const usize); let msg_jmp1 = jmp(version.msg_load1, MSG_TRAMPOLINE1.as_ptr() as usize); - set_trampoline(&mut MSG_TRAMPOLINE1, 0, load_msg_file as usize)?; + set_trampoline(&mut MSG_TRAMPOLINE1, 0, load_msg_file as *const () as usize)?; MSG_TRAMPOLINE1[9..13].copy_from_slice(&msg_format.to_le_bytes()); set_trampoline(&mut MSG_TRAMPOLINE1, 13, version.msg_load1 + 5)?; patch(version.msg_load1, &msg_jmp1)?; let msg_jmp2 = jmp(version.msg_load2, MSG_TRAMPOLINE2.as_ptr() as usize); - set_trampoline(&mut MSG_TRAMPOLINE2, 0, load_msg_file as usize)?; + set_trampoline(&mut MSG_TRAMPOLINE2, 0, load_msg_file as *const () as usize)?; MSG_TRAMPOLINE2[9..13].copy_from_slice(&msg_format.to_le_bytes()); set_trampoline(&mut MSG_TRAMPOLINE2, 13, version.msg_load2 + 5)?; patch(version.msg_load2, &msg_jmp2)?; let msg_jmp3 = jmp(version.msg_load3, MSG_TRAMPOLINE3.as_ptr() as usize); - set_trampoline(&mut MSG_TRAMPOLINE3, 0, load_msg_file as usize)?; + set_trampoline(&mut MSG_TRAMPOLINE3, 0, load_msg_file as *const () as usize)?; MSG_TRAMPOLINE3[9..13].copy_from_slice(&msg_format.to_le_bytes()); set_trampoline(&mut MSG_TRAMPOLINE3, 13, version.msg_load3 + 5)?; patch(version.msg_load3, &msg_jmp3)?; @@ -561,21 +561,21 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { // when trying to scroll up past the top inventory row, scroll the box view let scroll_up_return = get_conditional_jump_target(version.scroll_up_check as *const c_void); let scroll_up_jump = jl(version.scroll_up_check, SCROLL_UP_TRAMPOLINE.as_ptr() as usize); - set_trampoline(&mut SCROLL_UP_TRAMPOLINE, 2, scroll_up as usize)?; + set_trampoline(&mut SCROLL_UP_TRAMPOLINE, 2, scroll_up as *const () as usize)?; set_trampoline(&mut SCROLL_UP_TRAMPOLINE, 11, scroll_up_return as usize)?; patch(version.scroll_up_check, &scroll_up_jump)?; // when trying to scroll down past the last inventory row, scroll the box view let scroll_down_return = get_conditional_jump_target(version.scroll_down_check as *const c_void); let scroll_down_jump = jmp(version.scroll_down_check, SCROLL_DOWN_TRAMPOLINE.as_ptr() as usize); - set_trampoline(&mut SCROLL_DOWN_TRAMPOLINE, 2, scroll_down as usize)?; + set_trampoline(&mut SCROLL_DOWN_TRAMPOLINE, 2, scroll_down as *const () as usize)?; set_conditional_trampoline(&mut SCROLL_DOWN_TRAMPOLINE, 13, scroll_down_return as usize)?; set_trampoline(&mut SCROLL_DOWN_TRAMPOLINE, 19, version.scroll_down_check + 6)?; patch(version.scroll_down_check, &scroll_down_jump)?; // when trying to scroll left from the first inventory cell, scroll the box view let scroll_left_jump = jmp(version.scroll_left_check, SCROLL_LEFT_TRAMPOLINE.as_ptr() as usize); - set_trampoline(&mut SCROLL_LEFT_TRAMPOLINE, 5, scroll_left as usize)?; + set_trampoline(&mut SCROLL_LEFT_TRAMPOLINE, 5, scroll_left as *const () as usize)?; set_trampoline(&mut SCROLL_LEFT_TRAMPOLINE, 15, version.scroll_left_check + 8)?; patch(version.scroll_left_check, &scroll_left_jump)?; @@ -584,7 +584,7 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { version.scroll_right_check, SCROLL_RIGHT_TRAMPOLINE.as_ptr() as usize, ); - set_trampoline(&mut SCROLL_RIGHT_TRAMPOLINE, 7, scroll_right as usize)?; + set_trampoline(&mut SCROLL_RIGHT_TRAMPOLINE, 7, scroll_right as *const () as usize)?; set_trampoline(&mut SCROLL_RIGHT_TRAMPOLINE, 15, version.scroll_right_check + 6)?; patch(version.scroll_right_check, &scroll_right_jump)?; @@ -592,14 +592,14 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { version.scroll_right_two_check, SCROLL_RIGHT_TWO_TRAMPOLINE.as_ptr() as usize, ); - set_trampoline(&mut SCROLL_RIGHT_TWO_TRAMPOLINE, 7, scroll_right as usize)?; + set_trampoline(&mut SCROLL_RIGHT_TWO_TRAMPOLINE, 7, scroll_right as *const () as usize)?; set_trampoline(&mut SCROLL_RIGHT_TWO_TRAMPOLINE, 21, version.scroll_right_two_check + 10)?; patch(version.scroll_right_two_check, &scroll_right_two_jump)?; // after the view is organized, copy its contents back into the box let organize_jump1 = jmp(version.organize_end1, ORGANIZE_TRAMPOLINE.as_ptr() as usize); let organize_jump2 = jmp(version.organize_end2, ORGANIZE_TRAMPOLINE.as_ptr() as usize); - set_trampoline(&mut ORGANIZE_TRAMPOLINE, 1, update_box as usize)?; + set_trampoline(&mut ORGANIZE_TRAMPOLINE, 1, update_box as *const () as usize)?; patch(version.organize_end1, &organize_jump1)?; patch(version.organize_end2, &organize_jump2)?; @@ -615,7 +615,7 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { set_trampoline( &mut HAS_INK_RIBBON_TRAMPOLINE, 3, - track_typewriter_message as usize, + track_typewriter_message as *const () as usize, )?; set_trampoline(&mut HAS_INK_RIBBON_TRAMPOLINE, 19, version.has_ink_ribbon + 7)?; patch(version.has_ink_ribbon, &has_ink_jump)?; @@ -624,7 +624,7 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { set_trampoline( &mut NO_INK_RIBBON_TRAMPOLINE, 3, - track_typewriter_message as usize, + track_typewriter_message as *const () as usize, )?; set_trampoline(&mut NO_INK_RIBBON_TRAMPOLINE, 19, version.no_ink_ribbon + 7)?; patch(version.no_ink_ribbon, &no_ink_jump)?; @@ -636,12 +636,12 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { set_trampoline( &mut TYPEWRITER_CHOICE_TRAMPOLINE, 6, - check_typewriter_choice as usize, + check_typewriter_choice as *const () as usize, )?; patch(version.typewriter_choice_check, &choice_call)?; let box_call = call(version.typewriter_phase_set, OPEN_BOX_TRAMPOLINE.as_ptr() as usize); - set_trampoline(&mut OPEN_BOX_TRAMPOLINE, 1, open_box as usize)?; + set_trampoline(&mut OPEN_BOX_TRAMPOLINE, 1, open_box as *const () as usize)?; set_trampoline(&mut OPEN_BOX_TRAMPOLINE, 15, version.set_room_phase)?; patch(version.typewriter_phase_set, &box_call)?; @@ -653,7 +653,7 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { set_trampoline( &mut OPEN_ANIMATION_TRAMPOLINE, 1, - show_partner_inventory as usize, + show_partner_inventory as *const () as usize, )?; set_trampoline(&mut OPEN_ANIMATION_TRAMPOLINE, 19, version.play_menu_animation)?; patch(version.inventory_open_animation, &view_call)?; @@ -663,7 +663,7 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { version.inventory_menu_start, INVENTORY_START_TRAMPOLINE.as_ptr() as usize, ); - set_trampoline(&mut INVENTORY_START_TRAMPOLINE, 2, menu_setup as usize)?; + set_trampoline(&mut INVENTORY_START_TRAMPOLINE, 2, menu_setup as *const () as usize)?; set_trampoline(&mut INVENTORY_START_TRAMPOLINE, 17, version.inventory_menu_start + 6)?; patch(version.inventory_menu_start, &init_jump)?; @@ -675,7 +675,7 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { set_trampoline( &mut CHANGE_CHARACTER_TRAMPOLINE, 2, - change_character as usize, + change_character as *const () as usize, )?; set_trampoline(&mut CHANGE_CHARACTER_TRAMPOLINE, 18, version.inventory_change_character + 7)?; patch(version.inventory_change_character, &character_jump)?; @@ -685,7 +685,7 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { version.inventory_menu_close, INVENTORY_CLOSE_TRAMPOLINE.as_ptr() as usize, ); - set_trampoline(&mut INVENTORY_CLOSE_TRAMPOLINE, 1, close_box as usize)?; + set_trampoline(&mut INVENTORY_CLOSE_TRAMPOLINE, 1, close_box as *const () as usize)?; set_trampoline(&mut INVENTORY_CLOSE_TRAMPOLINE, 12, version.inventory_menu_close + 5)?; patch(version.inventory_menu_close, &close_call)?; @@ -695,7 +695,7 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { set_trampoline( &mut SIZE_CHECK_TRAMPOLINE, 11, - make_room_for_double as usize, + make_room_for_double as *const () as usize, )?; patch(version.exchange_size_check, &double_jump)?; @@ -707,7 +707,7 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { set_trampoline( &mut SHAFT_CHECK_TRAMPOLINE, 1, - should_skip_shaft_check as usize, + should_skip_shaft_check as *const () as usize, )?; SHAFT_CHECK_TRAMPOLINE[10..14].copy_from_slice(&(version.shaft_check + 5).to_le_bytes()); SHAFT_CHECK_TRAMPOLINE[15..19].copy_from_slice(&(original_function as usize).to_le_bytes()); @@ -717,7 +717,7 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { // reset the box when starting a new game let original_function = get_call_target(version.new_game as *const c_void); let new_game_call = call(version.new_game, NEW_GAME_TRAMPOLINE.as_ptr() as usize); - set_trampoline(&mut NEW_GAME_TRAMPOLINE, 1, new_game as usize)?; + set_trampoline(&mut NEW_GAME_TRAMPOLINE, 1, new_game as *const () as usize)?; set_trampoline(&mut NEW_GAME_TRAMPOLINE, 7, original_function as usize)?; patch(version.new_game, &new_game_call)?; } else { @@ -730,26 +730,26 @@ unsafe fn initialize(is_enabled: bool, is_leave_allowed: bool) -> Result<()> { // load data let load_jump = jmp(version.post_load, LOAD_TRAMPOLINE.as_ptr() as usize); - set_trampoline(&mut LOAD_TRAMPOLINE, 2, load_data as usize)?; + set_trampoline(&mut LOAD_TRAMPOLINE, 2, load_data as *const () as usize)?; set_trampoline(&mut LOAD_TRAMPOLINE, 20, version.sub_6fc610)?; LOAD_TRAMPOLINE[16..20].copy_from_slice(&(version.post_load + 11).to_le_bytes()); patch(version.post_load, &load_jump)?; // load slot let ls_jump = jmp(version.load_slot, LOAD_SLOT_TRAMPOLINE.as_ptr() as usize); - set_trampoline(&mut LOAD_SLOT_TRAMPOLINE, 2, load_slot as usize)?; + set_trampoline(&mut LOAD_SLOT_TRAMPOLINE, 2, load_slot as *const () as usize)?; set_trampoline(&mut LOAD_SLOT_TRAMPOLINE, 17, version.load_slot + 6)?; patch(version.load_slot, &ls_jump)?; // save data let save_jump = jmp(version.steam_save, SAVE_TRAMPOLINE.as_ptr() as usize); SAVE_TRAMPOLINE[2..6].copy_from_slice(&(version.steam_save + 7).to_le_bytes()); - set_trampoline(&mut SAVE_TRAMPOLINE, 6, save_data as usize)?; + set_trampoline(&mut SAVE_TRAMPOLINE, 6, save_data as *const () as usize)?; patch(version.steam_save, &save_jump)?; // save slot let ss_jump = jmp(version.save_slot, SAVE_SLOT_TRAMPOLINE.as_ptr() as usize); - set_trampoline(&mut SAVE_SLOT_TRAMPOLINE, 2, save_slot as usize)?; + set_trampoline(&mut SAVE_SLOT_TRAMPOLINE, 2, save_slot as *const () as usize)?; set_trampoline(&mut SAVE_SLOT_TRAMPOLINE, 17, version.save_slot + 6)?; patch(version.save_slot, &ss_jump)?; @@ -808,6 +808,6 @@ fn main(reason: u32) -> Result<()> { #[no_mangle] #[allow(non_snake_case)] -extern "system" fn DllMain(_dll_module: HMODULE, reason: u32, _reserved: *const c_void) -> BOOL { - main(reason).is_ok().into() +extern "system" fn DllMain(_dll_module: HMODULE, reason: u32, _reserved: *const c_void) -> i32 { + if main(reason).is_ok() { 1 } else { 0 } }