From 7bf0b671670bc7519d0806934ab6ef1f11099853 Mon Sep 17 00:00:00 2001 From: RektInator <7003455+RektInator@users.noreply.github.com> Date: Mon, 14 Apr 2025 12:23:08 +0200 Subject: [PATCH 001/542] wip --- src/common/platform/file_management.hpp | 27 +++++++++---- src/common/platform/process.hpp | 21 +++++++---- src/windows-emulator/syscalls.cpp | 31 ++++++++++++++- src/windows-emulator/syscalls/exception.cpp | 14 +++++-- src/windows-emulator/syscalls/file.cpp | 42 +++++++++++++++++++++ src/windows-emulator/syscalls/memory.cpp | 6 +-- src/windows-emulator/syscalls/section.cpp | 24 +++++++----- src/windows-emulator/syscalls/thread.cpp | 6 +-- src/windows-emulator/syscalls/token.cpp | 18 +++++++++ 9 files changed, 153 insertions(+), 36 deletions(-) diff --git a/src/common/platform/file_management.hpp b/src/common/platform/file_management.hpp index bbef40df..13e53053 100644 --- a/src/common/platform/file_management.hpp +++ b/src/common/platform/file_management.hpp @@ -97,7 +97,7 @@ typedef enum _FSINFOCLASS FileFsMaximumInformation } FSINFOCLASS, *PFSINFOCLASS; -typedef enum _FSINFOCLASS FS_INFORMATION_CLASS; +using FS_INFORMATION_CLASS = enum _FSINFOCLASS; typedef enum _FILE_INFORMATION_CLASS { @@ -210,7 +210,7 @@ typedef enum _FILE_INFORMATION_CLASS FileMaximumInformation } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; -typedef enum _OBJECT_INFORMATION_CLASS +using OBJECT_INFORMATION_CLASS = enum _OBJECT_INFORMATION_CLASS { ObjectBasicInformation, // q: OBJECT_BASIC_INFORMATION ObjectNameInformation, // q: OBJECT_NAME_INFORMATION @@ -220,9 +220,9 @@ typedef enum _OBJECT_INFORMATION_CLASS ObjectSessionInformation, // s: void // change object session // (requires SeTcbPrivilege) ObjectSessionObjectInformation, // s: void // change object session // (requires SeTcbPrivilege) MaxObjectInfoClass -} OBJECT_INFORMATION_CLASS; +}; -typedef enum _HARDERROR_RESPONSE_OPTION +using HARDERROR_RESPONSE_OPTION = enum _HARDERROR_RESPONSE_OPTION { OptionAbortRetryIgnore, OptionOk, @@ -233,9 +233,9 @@ typedef enum _HARDERROR_RESPONSE_OPTION OptionShutdownSystem, OptionOkNoWait, OptionCancelTryContinue -} HARDERROR_RESPONSE_OPTION; +}; -typedef enum _HARDERROR_RESPONSE +using HARDERROR_RESPONSE = enum _HARDERROR_RESPONSE { ResponseReturnToCaller, ResponseNotHandled, @@ -248,9 +248,9 @@ typedef enum _HARDERROR_RESPONSE ResponseYes, ResponseTryAgain, ResponseContinue -} HARDERROR_RESPONSE; +}; -typedef USHORT RTL_ATOM; +using RTL_ATOM = USHORT; template struct IO_STATUS_BLOCK @@ -333,6 +333,17 @@ typedef struct _FILE_BASIC_INFORMATION ULONG FileAttributes; // Specifies one or more FILE_ATTRIBUTE_XXX flags. } FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION; +typedef struct _FILE_NETWORK_OPEN_INFORMATION +{ + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER AllocationSize; + LARGE_INTEGER EndOfFile; + ULONG FileAttributes; +} FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION; + typedef struct _FILE_DIRECTORY_INFORMATION { ULONG NextEntryOffset; diff --git a/src/common/platform/process.hpp b/src/common/platform/process.hpp index cd512b9d..85b1a1a0 100644 --- a/src/common/platform/process.hpp +++ b/src/common/platform/process.hpp @@ -25,7 +25,7 @@ (CONTEXT_CONTROL_64 | CONTEXT_INTEGER_64 | CONTEXT_SEGMENTS_64 | CONTEXT_FLOATING_POINT_64 | \ CONTEXT_DEBUG_REGISTERS_64) -typedef enum _SYSTEM_INFORMATION_CLASS +using SYSTEM_INFORMATION_CLASS = enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation, // q: SYSTEM_BASIC_INFORMATION SystemProcessorInformation, // q: SYSTEM_PROCESSOR_INFORMATION @@ -323,7 +323,7 @@ typedef enum _SYSTEM_INFORMATION_CLASS SystemBreakOnContextUnwindFailureInformation, // ULONG (requires SeDebugPrivilege) SystemOslRamdiskInformation, // SYSTEM_OSL_RAMDISK_INFORMATION MaxSystemInfoClass -} SYSTEM_INFORMATION_CLASS; +}; #ifndef OS_WINDOWS typedef enum _TOKEN_INFORMATION_CLASS @@ -383,7 +383,7 @@ typedef enum _TOKEN_INFORMATION_CLASS #endif -typedef enum _PROCESSINFOCLASS +using PROCESSINFOCLASS = enum _PROCESSINFOCLASS { ProcessBasicInformation, // q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATION ProcessQuotaLimits, // qs: QUOTA_LIMITS, QUOTA_LIMITS_EX @@ -502,9 +502,9 @@ typedef enum _PROCESSINFOCLASS ProcessNetworkIoCounters, // q: PROCESS_NETWORK_COUNTERS ProcessFindFirstThreadByTebValue, // PROCESS_TEB_VALUE_INFORMATION MaxProcessInfoClass -} PROCESSINFOCLASS; +}; -typedef enum _PS_ATTRIBUTE_NUM +using PS_ATTRIBUTE_NUM = enum _PS_ATTRIBUTE_NUM { PsAttributeParentProcess, // in HANDLE PsAttributeDebugObject, // in HANDLE @@ -542,7 +542,7 @@ typedef enum _PS_ATTRIBUTE_NUM PsAttributeSupportedMachines, // since 24H2 PsAttributeSveVectorLength, // PPS_PROCESS_CREATION_SVE_VECTOR_LENGTH PsAttributeMax -} PS_ATTRIBUTE_NUM; +}; struct SYSTEM_PROCESSOR_INFORMATION64 { @@ -583,11 +583,11 @@ typedef struct _XMM_SAVE_AREA32 #endif -typedef struct _NEON128 +using NEON128 = struct _NEON128 { ULONGLONG Low; LONGLONG High; -} NEON128; +}; typedef struct DECLSPEC_ALIGN(16) _CONTEXT64 { @@ -768,6 +768,11 @@ struct TOKEN_USER64 SID_AND_ATTRIBUTES64 User; }; +struct TOKEN_OWNER64 +{ + EMULATOR_CAST(EmulatorTraits::PVOID, PSID) Owner; +}; + struct TOKEN_BNO_ISOLATION_INFORMATION64 { EmulatorTraits::PVOID IsolationPrefix; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 5ebaa267..7d65af2b 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -79,6 +79,9 @@ namespace syscalls NTSTATUS handle_NtQueryAttributesFile(const syscall_context& c, emulator_object>> object_attributes, emulator_object file_information); + NTSTATUS handle_NtQueryFullAttributesFile( + const syscall_context& c, emulator_object>> object_attributes, + emulator_object file_information); NTSTATUS handle_NtOpenFile(const syscall_context& c, emulator_object file_handle, ACCESS_MASK desired_access, emulator_object>> object_attributes, @@ -627,6 +630,29 @@ namespace syscalls { return 0; } + + NTSTATUS handle_NtCreateNamedPipeFile( + const syscall_context& c, const emulator_object file_handle, const ULONG desired_access, + const emulator_object>> object_attributes, + const emulator_object>> io_status_block, const ULONG share_access, + const ULONG create_disposition, const ULONG create_options, const ULONG named_pipe_type, const ULONG read_mode, + const ULONG completion_mode, const ULONG maximum_instances, const ULONG inbound_quota, + const ULONG outbound_quota, const emulator_object default_timeout) + { + file_handle.write(handle{.value = {.id = 1337, .type = handle_types::file, .is_pseudo = 1}}); + + return STATUS_SUCCESS; + } + + NTSTATUS handle_NtFsControlFile(const syscall_context& c, const handle event_handle, const uint64_t apc_routine, + const uint64_t app_context, + const emulator_object>> io_status_block, + const ULONG fs_control_code, const uint64_t input_buffer, + const ULONG input_buffer_length, const uint64_t output_buffer, + const ULONG output_buffer_length) + { + return STATUS_SUCCESS; + } } void syscall_dispatcher::add_handlers(std::map& handler_mapping) @@ -769,6 +795,9 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtQueueApcThreadEx); add_handler(NtQueueApcThread); add_handler(NtCreateUserProcess); + add_handler(NtCreateNamedPipeFile); + add_handler(NtFsControlFile); + add_handler(NtQueryFullAttributesFile); #undef add_handler -} \ No newline at end of file +} diff --git a/src/windows-emulator/syscalls/exception.cpp b/src/windows-emulator/syscalls/exception.cpp index dcaebb91..34f8cf5c 100644 --- a/src/windows-emulator/syscalls/exception.cpp +++ b/src/windows-emulator/syscalls/exception.cpp @@ -24,10 +24,9 @@ namespace syscalls return STATUS_SUCCESS; } - NTSTATUS handle_NtRaiseException(const syscall_context& c, - const emulator_object>> - /*exception_record*/, - const emulator_object thread_context, const BOOLEAN handle_exception) + NTSTATUS handle_NtRaiseException( + const syscall_context& c, const emulator_object>> exception_record, + const emulator_object thread_context, const BOOLEAN handle_exception) { if (handle_exception) { @@ -36,6 +35,13 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } + const auto& exception_data = exception_record.read(); + if (exception_data.ExceptionCode == 0xC0000602) // STATUS_FAIL_FAST_EXCEPTION + { + c.emu.stop(); + return STATUS_SUCCESS; + } + c.proc.exception_rip = thread_context.read().Rip; c.emu.stop(); diff --git a/src/windows-emulator/syscalls/file.cpp b/src/windows-emulator/syscalls/file.cpp index c2c675ef..0c4ad0d9 100644 --- a/src/windows-emulator/syscalls/file.cpp +++ b/src/windows-emulator/syscalls/file.cpp @@ -1,6 +1,7 @@ #include "../std_include.hpp" #include "../emulator_utils.hpp" #include "../syscall_utils.hpp" +#include "utils/io.hpp" #include #include @@ -660,6 +661,47 @@ namespace syscalls return STATUS_SUCCESS; } + NTSTATUS handle_NtQueryFullAttributesFile( + const syscall_context& c, const emulator_object>> object_attributes, + const emulator_object file_information) + { + if (!object_attributes) + { + return STATUS_INVALID_PARAMETER; + } + + const auto attributes = object_attributes.read(); + if (!attributes.ObjectName) + { + return STATUS_INVALID_PARAMETER; + } + + const auto filename = read_unicode_string( + c.emu, emulator_object>>{c.emu, attributes.ObjectName}); + + c.win_emu.log.print(color::dark_gray, "--> Querying file attributes: %s\n", u16_to_u8(filename).c_str()); + + const auto local_filename = c.win_emu.file_sys.translate(filename).string(); + + struct _stat64 file_stat{}; + if (_stat64(local_filename.c_str(), &file_stat) != 0) + { + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + file_information.access([&](FILE_NETWORK_OPEN_INFORMATION& info) { + info.CreationTime = utils::convert_unix_to_windows_time(file_stat.st_atime); + info.LastAccessTime = utils::convert_unix_to_windows_time(file_stat.st_atime); + info.LastWriteTime = utils::convert_unix_to_windows_time(file_stat.st_mtime); + info.AllocationSize.QuadPart = file_stat.st_size; + info.EndOfFile.QuadPart = file_stat.st_size; + info.ChangeTime = info.LastWriteTime; + info.FileAttributes = FILE_ATTRIBUTE_NORMAL; + }); + + return STATUS_SUCCESS; + } + NTSTATUS handle_NtQueryAttributesFile( const syscall_context& c, const emulator_object>> object_attributes, const emulator_object file_information) diff --git a/src/windows-emulator/syscalls/memory.cpp b/src/windows-emulator/syscalls/memory.cpp index 0fa59202..ef714196 100644 --- a/src/windows-emulator/syscalls/memory.cpp +++ b/src/windows-emulator/syscalls/memory.cpp @@ -28,9 +28,9 @@ namespace syscalls return_length.write(sizeof(EMU_MEMORY_BASIC_INFORMATION64)); } - if (memory_information_length != sizeof(EMU_MEMORY_BASIC_INFORMATION64)) + if (memory_information_length < sizeof(EMU_MEMORY_BASIC_INFORMATION64)) { - return STATUS_BUFFER_OVERFLOW; + return STATUS_BUFFER_TOO_SMALL; } const emulator_object info{c.emu, memory_information}; @@ -198,7 +198,7 @@ namespace syscalls const bool reserve = allocation_type & MEM_RESERVE; const bool commit = allocation_type & MEM_COMMIT; - if ((allocation_type & ~(MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN)) || (!commit && !reserve)) + if ((allocation_type & ~(MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN | MEM_WRITE_WATCH)) || (!commit && !reserve)) { throw std::runtime_error("Unsupported allocation type!"); } diff --git a/src/windows-emulator/syscalls/section.cpp b/src/windows-emulator/syscalls/section.cpp index 5cf2cc3b..119874f2 100644 --- a/src/windows-emulator/syscalls/section.cpp +++ b/src/windows-emulator/syscalls/section.cpp @@ -109,7 +109,7 @@ namespace syscalls const syscall_context& c, const handle section_handle, const handle process_handle, const emulator_object base_address, const EMULATOR_CAST(EmulatorTraits::ULONG_PTR, ULONG_PTR) /*zero_bits*/, - const EMULATOR_CAST(EmulatorTraits::SIZE_T, SIZE_T) /*commit_size*/, + const EMULATOR_CAST(EmulatorTraits::SIZE_T, SIZE_T) commit_size, const emulator_object /*section_offset*/, const emulator_object::SIZE_T, SIZE_T)> view_size, const SECTION_INHERIT /*inherit_disposition*/, const ULONG /*allocation_type*/, const ULONG /*win32_protect*/) @@ -225,10 +225,11 @@ namespace syscalls size = page_align_up(file_data.size()); } + const auto reserve_only = section_entry->allocation_attributes == SEC_RESERVE; const auto protection = map_nt_to_emulator_protection(section_entry->section_page_protection); - const auto address = c.win_emu.memory.allocate_memory(size, protection); + const auto address = c.win_emu.memory.allocate_memory(size, protection, reserve_only); - if (!file_data.empty()) + if (!reserve_only && !file_data.empty()) { c.emu.write_memory(address, file_data.data(), file_data.size()); } @@ -263,19 +264,24 @@ namespace syscalls } const auto* mod = c.win_emu.mod_manager.find_by_address(base_address); - if (!mod) + if (mod != nullptr) { - c.win_emu.log.error("Unmapping non-module section not supported!\n"); - c.emu.stop(); - return STATUS_NOT_SUPPORTED; + if (c.win_emu.mod_manager.unmap(base_address, c.win_emu.log)) + { + return STATUS_SUCCESS; + } + + return STATUS_INVALID_PARAMETER; } - if (c.win_emu.mod_manager.unmap(base_address, c.win_emu.log)) + if (c.win_emu.memory.release_memory(base_address, 0)) { return STATUS_SUCCESS; } - return STATUS_INVALID_PARAMETER; + c.win_emu.log.error("Unmapping non-module/non-memory section not supported!\n"); + c.emu.stop(); + return STATUS_NOT_SUPPORTED; } NTSTATUS handle_NtUnmapViewOfSectionEx(const syscall_context& c, const handle process_handle, diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 5a6b1f4c..70e442c5 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -578,8 +578,8 @@ namespace syscalls if (apc_flags) { c.win_emu.log.error("Unsupported APC flags: %X\n", apc_flags); - c.emu.stop(); - return STATUS_NOT_SUPPORTED; + // c.emu.stop(); + // return STATUS_NOT_SUPPORTED; } thread->pending_apcs.push_back({ @@ -590,7 +590,7 @@ namespace syscalls .apc_argument3 = apc_argument3, }); - return STATUS_NOT_SUPPORTED; + return STATUS_SUCCESS; } NTSTATUS handle_NtQueueApcThreadEx(const syscall_context& c, const handle thread_handle, diff --git a/src/windows-emulator/syscalls/token.cpp b/src/windows-emulator/syscalls/token.cpp index 70dbe135..52857c72 100644 --- a/src/windows-emulator/syscalls/token.cpp +++ b/src/windows-emulator/syscalls/token.cpp @@ -75,6 +75,24 @@ namespace syscalls return STATUS_SUCCESS; } + if (token_information_class == TokenOwner) + { + constexpr auto required_size = sizeof(sid) + sizeof(TOKEN_OWNER64); + return_length.write(required_size); + + if (required_size > token_information_length) + { + return STATUS_BUFFER_TOO_SMALL; + } + + TOKEN_OWNER64 owner{}; + owner.Owner = token_information + sizeof(TOKEN_OWNER64); + + emulator_object{c.emu, token_information}.write(owner); + c.emu.write_memory(token_information + sizeof(TOKEN_OWNER64), sid, sizeof(sid)); + return STATUS_SUCCESS; + } + if (token_information_class == TokenType) { constexpr auto required_size = sizeof(TOKEN_TYPE); From 6379370a50f777acc0d868ce8c5e6924f28d5152 Mon Sep 17 00:00:00 2001 From: RektInator <7003455+RektInator@users.noreply.github.com> Date: Mon, 14 Apr 2025 12:43:11 +0200 Subject: [PATCH 002/542] fix(syscalls): fix VirtualQuery return size, ignore unimplemented APC flags and log a warning. --- src/windows-emulator/syscalls.cpp | 38 +++++++++--------------- src/windows-emulator/syscalls/file.cpp | 27 +++++++++++++++++ src/windows-emulator/syscalls/memory.cpp | 2 +- src/windows-emulator/syscalls/thread.cpp | 2 +- 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 7d65af2b..431a50e4 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -96,6 +96,19 @@ namespace syscalls NTSTATUS handle_NtQuerySymbolicLinkObject(const syscall_context& c, handle link_handle, emulator_object>> link_target, emulator_object returned_length); + NTSTATUS handle_NtCreateNamedPipeFile(const syscall_context& c, emulator_object file_handle, + ULONG desired_access, + emulator_object>> object_attributes, + emulator_object>> io_status_block, + ULONG share_access, ULONG create_disposition, ULONG create_options, + ULONG named_pipe_type, ULONG read_mode, ULONG completion_mode, + ULONG maximum_instances, ULONG inbound_quota, ULONG outbound_quota, + emulator_object default_timeout); + NTSTATUS handle_NtFsControlFile(const syscall_context& c, handle event_handle, uint64_t apc_routine, + uint64_t app_context, + emulator_object>> io_status_block, + ULONG fs_control_code, uint64_t input_buffer, ULONG input_buffer_length, + uint64_t output_buffer, ULONG output_buffer_length); // syscalls/locale.cpp: NTSTATUS handle_NtInitializeNlsFiles(const syscall_context& c, emulator_object base_address, @@ -112,7 +125,7 @@ namespace syscalls // syscalls/memory.cpp: NTSTATUS handle_NtQueryVirtualMemory(const syscall_context& c, handle process_handle, uint64_t base_address, uint32_t info_class, uint64_t memory_information, - uint32_t memory_information_length, emulator_object return_length); + uint32_t memory_information_length, emulator_object return_length); NTSTATUS handle_NtProtectVirtualMemory(const syscall_context& c, handle process_handle, emulator_object base_address, emulator_object bytes_to_protect, uint32_t protection, @@ -630,29 +643,6 @@ namespace syscalls { return 0; } - - NTSTATUS handle_NtCreateNamedPipeFile( - const syscall_context& c, const emulator_object file_handle, const ULONG desired_access, - const emulator_object>> object_attributes, - const emulator_object>> io_status_block, const ULONG share_access, - const ULONG create_disposition, const ULONG create_options, const ULONG named_pipe_type, const ULONG read_mode, - const ULONG completion_mode, const ULONG maximum_instances, const ULONG inbound_quota, - const ULONG outbound_quota, const emulator_object default_timeout) - { - file_handle.write(handle{.value = {.id = 1337, .type = handle_types::file, .is_pseudo = 1}}); - - return STATUS_SUCCESS; - } - - NTSTATUS handle_NtFsControlFile(const syscall_context& c, const handle event_handle, const uint64_t apc_routine, - const uint64_t app_context, - const emulator_object>> io_status_block, - const ULONG fs_control_code, const uint64_t input_buffer, - const ULONG input_buffer_length, const uint64_t output_buffer, - const ULONG output_buffer_length) - { - return STATUS_SUCCESS; - } } void syscall_dispatcher::add_handlers(std::map& handler_mapping) diff --git a/src/windows-emulator/syscalls/file.cpp b/src/windows-emulator/syscalls/file.cpp index 0c4ad0d9..fbc968cd 100644 --- a/src/windows-emulator/syscalls/file.cpp +++ b/src/windows-emulator/syscalls/file.cpp @@ -820,4 +820,31 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } + + NTSTATUS handle_NtCreateNamedPipeFile( + const syscall_context& c, const emulator_object file_handle, const ULONG desired_access, + const emulator_object>> object_attributes, + const emulator_object>> io_status_block, const ULONG share_access, + const ULONG create_disposition, const ULONG create_options, const ULONG named_pipe_type, const ULONG read_mode, + const ULONG completion_mode, const ULONG maximum_instances, const ULONG inbound_quota, + const ULONG outbound_quota, const emulator_object default_timeout) + { + c.win_emu.log.error("Unimplemented syscall NtCreateNamedPipeFile!"); + c.emu.stop(); + + return STATUS_NOT_SUPPORTED; + } + + NTSTATUS handle_NtFsControlFile(const syscall_context& c, const handle event_handle, const uint64_t apc_routine, + const uint64_t app_context, + const emulator_object>> io_status_block, + const ULONG fs_control_code, const uint64_t input_buffer, + const ULONG input_buffer_length, const uint64_t output_buffer, + const ULONG output_buffer_length) + { + c.win_emu.log.error("Unimplemented syscall NtFsControlFile!"); + c.emu.stop(); + + return STATUS_NOT_SUPPORTED; + } } \ No newline at end of file diff --git a/src/windows-emulator/syscalls/memory.cpp b/src/windows-emulator/syscalls/memory.cpp index ef714196..dca1749d 100644 --- a/src/windows-emulator/syscalls/memory.cpp +++ b/src/windows-emulator/syscalls/memory.cpp @@ -9,7 +9,7 @@ namespace syscalls NTSTATUS handle_NtQueryVirtualMemory(const syscall_context& c, const handle process_handle, const uint64_t base_address, const uint32_t info_class, const uint64_t memory_information, const uint32_t memory_information_length, - const emulator_object return_length) + const emulator_object return_length) { if (process_handle != CURRENT_PROCESS) { diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 70e442c5..3cc470a7 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -577,7 +577,7 @@ namespace syscalls if (apc_flags) { - c.win_emu.log.error("Unsupported APC flags: %X\n", apc_flags); + c.win_emu.log.warn("Unsupported APC flags: %X\n", apc_flags); // c.emu.stop(); // return STATUS_NOT_SUPPORTED; } From 90256895c1a9fb2be4e2facc6b19308e079810c9 Mon Sep 17 00:00:00 2001 From: RektInator <7003455+RektInator@users.noreply.github.com> Date: Mon, 14 Apr 2025 12:51:02 +0200 Subject: [PATCH 003/542] fix: add missing #define for SEC_RESERVE --- src/common/platform/file_management.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/platform/file_management.hpp b/src/common/platform/file_management.hpp index 13e53053..60097940 100644 --- a/src/common/platform/file_management.hpp +++ b/src/common/platform/file_management.hpp @@ -73,6 +73,7 @@ #define SL_NO_CURSOR_UPDATE 0x10 #define SEC_IMAGE 0x01000000 +#define SEC_RESERVE 0x04000000 typedef enum _FSINFOCLASS { From e41a3be497ef197d363038abeb2e00f15c6a0fff Mon Sep 17 00:00:00 2001 From: RektInator <7003455+RektInator@users.noreply.github.com> Date: Mon, 14 Apr 2025 12:56:09 +0200 Subject: [PATCH 004/542] fix: comment unused parameter names --- src/windows-emulator/syscalls/file.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/windows-emulator/syscalls/file.cpp b/src/windows-emulator/syscalls/file.cpp index fbc968cd..0cad972c 100644 --- a/src/windows-emulator/syscalls/file.cpp +++ b/src/windows-emulator/syscalls/file.cpp @@ -822,12 +822,13 @@ namespace syscalls } NTSTATUS handle_NtCreateNamedPipeFile( - const syscall_context& c, const emulator_object file_handle, const ULONG desired_access, - const emulator_object>> object_attributes, - const emulator_object>> io_status_block, const ULONG share_access, - const ULONG create_disposition, const ULONG create_options, const ULONG named_pipe_type, const ULONG read_mode, - const ULONG completion_mode, const ULONG maximum_instances, const ULONG inbound_quota, - const ULONG outbound_quota, const emulator_object default_timeout) + const syscall_context& c, const emulator_object /*file_handle*/, const ULONG /*desired_access*/, + const emulator_object>> /*object_attributes*/, + const emulator_object>> /*io_status_block*/, const ULONG /*share_access*/, + const ULONG /*create_disposition*/, const ULONG /*create_options*/, const ULONG /*named_pipe_type*/, + const ULONG /*read_mode*/, const ULONG /*completion_mode*/, const ULONG /*maximum_instances*/, + const ULONG /*inbound_quota*/, const ULONG /*outbound_quota*/, + const emulator_object /*default_timeout*/) { c.win_emu.log.error("Unimplemented syscall NtCreateNamedPipeFile!"); c.emu.stop(); @@ -835,12 +836,12 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } - NTSTATUS handle_NtFsControlFile(const syscall_context& c, const handle event_handle, const uint64_t apc_routine, - const uint64_t app_context, - const emulator_object>> io_status_block, - const ULONG fs_control_code, const uint64_t input_buffer, - const ULONG input_buffer_length, const uint64_t output_buffer, - const ULONG output_buffer_length) + NTSTATUS handle_NtFsControlFile(const syscall_context& c, const handle /*event_handle*/, + const uint64_t /*apc_routine*/, const uint64_t /*app_context*/, + const emulator_object>> /*io_status_block*/, + const ULONG /*fs_control_code*/, const uint64_t /*input_buffer*/, + const ULONG /*input_buffer_length*/, const uint64_t /*output_buffer*/, + const ULONG /*output_buffer_length*/) { c.win_emu.log.error("Unimplemented syscall NtFsControlFile!"); c.emu.stop(); From af26c8de6114e581d076005924c029f4c17e84ff Mon Sep 17 00:00:00 2001 From: RektInator <7003455+RektInator@users.noreply.github.com> Date: Mon, 14 Apr 2025 13:00:36 +0200 Subject: [PATCH 005/542] fix: resolve comments, comment unused parameter in section.cpp --- src/windows-emulator/syscalls/exception.cpp | 10 ++-------- src/windows-emulator/syscalls/memory.cpp | 2 +- src/windows-emulator/syscalls/section.cpp | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/windows-emulator/syscalls/exception.cpp b/src/windows-emulator/syscalls/exception.cpp index 34f8cf5c..42362bc3 100644 --- a/src/windows-emulator/syscalls/exception.cpp +++ b/src/windows-emulator/syscalls/exception.cpp @@ -25,7 +25,8 @@ namespace syscalls } NTSTATUS handle_NtRaiseException( - const syscall_context& c, const emulator_object>> exception_record, + const syscall_context& c, + const emulator_object>> /*exception_record*/, const emulator_object thread_context, const BOOLEAN handle_exception) { if (handle_exception) @@ -35,13 +36,6 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } - const auto& exception_data = exception_record.read(); - if (exception_data.ExceptionCode == 0xC0000602) // STATUS_FAIL_FAST_EXCEPTION - { - c.emu.stop(); - return STATUS_SUCCESS; - } - c.proc.exception_rip = thread_context.read().Rip; c.emu.stop(); diff --git a/src/windows-emulator/syscalls/memory.cpp b/src/windows-emulator/syscalls/memory.cpp index dca1749d..7e12521d 100644 --- a/src/windows-emulator/syscalls/memory.cpp +++ b/src/windows-emulator/syscalls/memory.cpp @@ -8,7 +8,7 @@ namespace syscalls { NTSTATUS handle_NtQueryVirtualMemory(const syscall_context& c, const handle process_handle, const uint64_t base_address, const uint32_t info_class, - const uint64_t memory_information, const uint32_t memory_information_length, + const uint64_t memory_information, const uint64_t memory_information_length, const emulator_object return_length) { if (process_handle != CURRENT_PROCESS) diff --git a/src/windows-emulator/syscalls/section.cpp b/src/windows-emulator/syscalls/section.cpp index 119874f2..415206dd 100644 --- a/src/windows-emulator/syscalls/section.cpp +++ b/src/windows-emulator/syscalls/section.cpp @@ -109,7 +109,7 @@ namespace syscalls const syscall_context& c, const handle section_handle, const handle process_handle, const emulator_object base_address, const EMULATOR_CAST(EmulatorTraits::ULONG_PTR, ULONG_PTR) /*zero_bits*/, - const EMULATOR_CAST(EmulatorTraits::SIZE_T, SIZE_T) commit_size, + const EMULATOR_CAST(EmulatorTraits::SIZE_T, SIZE_T) /*commit_size*/, const emulator_object /*section_offset*/, const emulator_object::SIZE_T, SIZE_T)> view_size, const SECTION_INHERIT /*inherit_disposition*/, const ULONG /*allocation_type*/, const ULONG /*win32_protect*/) From acae9ff61040884ed404313d865c7fd157e1690b Mon Sep 17 00:00:00 2001 From: RektInator <7003455+RektInator@users.noreply.github.com> Date: Mon, 14 Apr 2025 13:05:40 +0200 Subject: [PATCH 006/542] fix: fix parameters for NtQueryVirtualMemory in syscalls.cpp --- src/windows-emulator/syscalls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 431a50e4..bf18f69c 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -125,7 +125,7 @@ namespace syscalls // syscalls/memory.cpp: NTSTATUS handle_NtQueryVirtualMemory(const syscall_context& c, handle process_handle, uint64_t base_address, uint32_t info_class, uint64_t memory_information, - uint32_t memory_information_length, emulator_object return_length); + uint64_t memory_information_length, emulator_object return_length); NTSTATUS handle_NtProtectVirtualMemory(const syscall_context& c, handle process_handle, emulator_object base_address, emulator_object bytes_to_protect, uint32_t protection, From a6dd9251b8f9234569dcab492261d063fa481b0e Mon Sep 17 00:00:00 2001 From: Maurice Heumann Date: Mon, 14 Apr 2025 12:13:38 +0200 Subject: [PATCH 007/542] Prepare 32 bit support --- src/CMakeLists.txt | 2 +- src/analyzer/object_watching.hpp | 5 ++-- src/analyzer/reflect_type_info.hpp | 9 +++++++ .../icicle-bridge/CMakeLists.txt | 4 ++- src/common/platform/compiler.hpp | 7 +++++ src/common/platform/kernel_mapped.hpp | 2 +- src/common/platform/memory.hpp | 4 +-- src/common/platform/process.hpp | 4 ++- src/emulator/serialization.hpp | 4 +-- src/fuzzer/main.cpp | 15 +++++------ src/windows-emulator-test/CMakeLists.txt | 2 +- src/windows-emulator-test/emulation_test.cpp | 2 +- .../emulation_test_utils.hpp | 6 ++--- src/windows-emulator/apiset/apiset.cpp | 2 +- src/windows-emulator/emulator_thread.cpp | 4 +-- src/windows-emulator/emulator_thread.hpp | 2 +- src/windows-emulator/emulator_utils.hpp | 3 ++- src/windows-emulator/exception_dispatch.cpp | 2 +- src/windows-emulator/kusd_mmio.cpp | 2 +- src/windows-emulator/memory_manager.cpp | 25 ++++++++--------- .../module/module_mapping.cpp | 25 ++++++++--------- src/windows-emulator/syscall_utils.hpp | 3 ++- src/windows-emulator/syscalls/file.cpp | 7 ++--- src/windows-emulator/syscalls/locale.cpp | 4 +-- src/windows-emulator/syscalls/memory.cpp | 27 +++++++++++-------- src/windows-emulator/syscalls/port.cpp | 5 ++-- src/windows-emulator/syscalls/section.cpp | 4 +-- src/windows-emulator/windows_emulator.cpp | 6 ++--- src/windows-gdb-stub/x64_gdb_stub_handler.hpp | 4 +-- 29 files changed, 111 insertions(+), 80 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5a8713ad..4492d05c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,7 +12,7 @@ if (NOT MOMO_BUILD_AS_LIBRARY) add_subdirectory(fuzzing-engine) add_subdirectory(fuzzer) add_subdirectory(windows-emulator-test) - if(WIN32) + if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) momo_add_subdirectory_and_get_targets("tools" TOOL_TARGETS) momo_targets_set_folder("tools" ${TOOL_TARGETS}) diff --git a/src/analyzer/object_watching.hpp b/src/analyzer/object_watching.hpp index df8ab871..4d700d4c 100644 --- a/src/analyzer/object_watching.hpp +++ b/src/analyzer/object_watching.hpp @@ -10,7 +10,7 @@ emulator_hook* watch_object(windows_emulator& emu, const std::set info{}; return emu.emu().hook_memory_read( - object.value(), object.size(), + object.value(), static_cast(object.size()), [i = std::move(info), object, &emu, cache_logging, modules](const uint64_t address, const void*, size_t) { const auto rip = emu.emu().read_instruction_pointer(); const auto* mod = emu.mod_manager.find_by_address(rip); @@ -33,6 +33,7 @@ emulator_hook* watch_object(windows_emulator& emu, const std::setname.c_str() : ""); + i.get_member_name(static_cast(offset)).c_str(), rip, + mod ? mod->name.c_str() : ""); }); } diff --git a/src/analyzer/reflect_type_info.hpp b/src/analyzer/reflect_type_info.hpp index 0bfd8075..0a6059a9 100644 --- a/src/analyzer/reflect_type_info.hpp +++ b/src/analyzer/reflect_type_info.hpp @@ -11,9 +11,18 @@ #pragma clang diagnostic ignored "-Wunused-private-field" #endif +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4308) +#endif + #include "reflect_extension.hpp" #include +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #if defined(__clang__) #pragma clang diagnostic pop #endif diff --git a/src/backends/icicle-emulator/icicle-bridge/CMakeLists.txt b/src/backends/icicle-emulator/icicle-bridge/CMakeLists.txt index 4836948a..73ea9f1d 100644 --- a/src/backends/icicle-emulator/icicle-bridge/CMakeLists.txt +++ b/src/backends/icicle-emulator/icicle-bridge/CMakeLists.txt @@ -13,7 +13,9 @@ endif() set(CARGO_TRIPLE) set(CARGO_OPTIONS) -if(CMAKE_SYSTEM_NAME STREQUAL "iOS") +if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4) + set(CARGO_TRIPLE "i686-pc-windows-msvc") +elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS") set(CARGO_TRIPLE "aarch64-apple-ios") elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") if(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") diff --git a/src/common/platform/compiler.hpp b/src/common/platform/compiler.hpp index 34c1cc4d..5cece6a8 100644 --- a/src/common/platform/compiler.hpp +++ b/src/common/platform/compiler.hpp @@ -2,6 +2,13 @@ #if defined(_WIN32) || defined(_WIN64) #define OS_WINDOWS + +#if defined(_WIN64) +#define OS_WINDOWS_64 +#else +#define OS_WINDOWS_32 +#endif + #elif defined(__APPLE__) || defined(__MACH__) #define OS_MAC #elif defined(__linux__) diff --git a/src/common/platform/kernel_mapped.hpp b/src/common/platform/kernel_mapped.hpp index 83dd548e..5dca4e62 100644 --- a/src/common/platform/kernel_mapped.hpp +++ b/src/common/platform/kernel_mapped.hpp @@ -583,7 +583,7 @@ typedef struct _TEB64 ARRAY_CONTAINER Rcu; } TEB64, *PTEB64; -#ifdef OS_WINDOWS +#if defined(OS_WINDOWS) && defined(_WIN64) inline TEB64* NtCurrentTeb64() { return reinterpret_cast(__readgsqword(FIELD_OFFSET(EMU_NT_TIB64, Self))); diff --git a/src/common/platform/memory.hpp b/src/common/platform/memory.hpp index bf213b91..44e37103 100644 --- a/src/common/platform/memory.hpp +++ b/src/common/platform/memory.hpp @@ -66,8 +66,8 @@ typedef enum _SECTION_INHERIT typedef struct DECLSPEC_ALIGN(16) _EMU_MEMORY_BASIC_INFORMATION64 { - void* BaseAddress; - void* AllocationBase; + uint64_t BaseAddress; + uint64_t AllocationBase; DWORD AllocationProtect; WORD PartitionId; std::int64_t RegionSize; diff --git a/src/common/platform/process.hpp b/src/common/platform/process.hpp index 85b1a1a0..e02d8f99 100644 --- a/src/common/platform/process.hpp +++ b/src/common/platform/process.hpp @@ -553,13 +553,15 @@ struct SYSTEM_PROCESSOR_INFORMATION64 ULONG ProcessorFeatureBits; }; -#ifndef OS_WINDOWS +#if !defined(OS_WINDOWS) || !defined(_WIN64) +#if !defined(OS_WINDOWS) typedef struct _M128A { ULONGLONG Low; LONGLONG High; } M128A, *PM128A; +#endif typedef struct _XMM_SAVE_AREA32 { diff --git a/src/emulator/serialization.hpp b/src/emulator/serialization.hpp index 981d3b51..8bf66af7 100644 --- a/src/emulator/serialization.hpp +++ b/src/emulator/serialization.hpp @@ -378,7 +378,7 @@ namespace utils { const auto size = this->read(); result.clear(); - result.reserve(size); + result.reserve(static_cast(size)); for (uint64_t i = 0; i < size; ++i) { @@ -447,7 +447,7 @@ namespace utils const auto size = this->read(); result.clear(); - result.reserve(size); + result.reserve(static_cast(size)); for (uint64_t i = 0; i < size; ++i) { diff --git a/src/fuzzer/main.cpp b/src/fuzzer/main.cpp index 237d357c..24f9fc9f 100644 --- a/src/fuzzer/main.cpp +++ b/src/fuzzer/main.cpp @@ -5,6 +5,10 @@ #include "utils/finally.hpp" +#ifdef _MSC_VER +#pragma warning(disable : 4702) +#endif + bool use_gdb = false; namespace @@ -63,12 +67,6 @@ namespace utils::buffer_deserializer deserializer{emulator_data}; emu.deserialize(deserializer); emu.save_snapshot(); - - const auto ret = emu.emu().read_stack(0); - - emu.emu().hook_memory_execution(ret, [&](uint64_t) { - emu.emu().stop(); // - }); } void restore_emulator() @@ -87,8 +85,9 @@ namespace restore_emulator(); - const auto memory = emu.memory.allocate_memory(page_align_up(std::max(data.size(), static_cast(1))), - memory_permission::read_write); + const auto memory = emu.memory.allocate_memory( + static_cast(page_align_up(std::max(data.size(), static_cast(1)))), + memory_permission::read_write); emu.emu().write_memory(memory, data.data(), data.size()); emu.emu().reg(x64_register::rcx, memory); diff --git a/src/windows-emulator-test/CMakeLists.txt b/src/windows-emulator-test/CMakeLists.txt index 7bf36845..8bea4b26 100644 --- a/src/windows-emulator-test/CMakeLists.txt +++ b/src/windows-emulator-test/CMakeLists.txt @@ -16,7 +16,7 @@ target_link_libraries(windows-emulator-test PRIVATE windows-emulator ) -if(WIN32) +if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) add_dependencies(windows-emulator-test test-sample) endif() diff --git a/src/windows-emulator-test/emulation_test.cpp b/src/windows-emulator-test/emulation_test.cpp index dc600f92..d5c60363 100644 --- a/src/windows-emulator-test/emulation_test.cpp +++ b/src/windows-emulator-test/emulation_test.cpp @@ -34,7 +34,7 @@ namespace test constexpr auto offset = 1; const auto instructionsToExecute = executedInstructions - offset; - new_emu.start(instructionsToExecute); + new_emu.start(static_cast(instructionsToExecute)); ASSERT_EQ(new_emu.get_executed_instructions(), instructionsToExecute); ASSERT_NOT_TERMINATED(new_emu); diff --git a/src/windows-emulator-test/emulation_test_utils.hpp b/src/windows-emulator-test/emulation_test_utils.hpp index 5437aec1..a399702c 100644 --- a/src/windows-emulator-test/emulation_test_utils.hpp +++ b/src/windows-emulator-test/emulation_test_utils.hpp @@ -155,7 +155,7 @@ namespace test return s1.get_diff(s2).has_value(); }; - if (!has_diff_after_count(limit)) + if (!has_diff_after_count(static_cast(limit))) { puts("Emulation has no diff"); } @@ -170,7 +170,7 @@ namespace test const auto diff = (upper_bound - lower_bound); const auto pivot = lower_bound + (diff / 2); - const auto has_diff = has_diff_after_count(pivot); + const auto has_diff = has_diff_after_count(static_cast(pivot)); auto* bound = has_diff ? &upper_bound : &lower_bound; *bound = pivot; @@ -178,7 +178,7 @@ namespace test printf("Bounds: %" PRIx64 " - %" PRIx64 "\n", lower_bound, upper_bound); } - (void)get_state_for_count(lower_bound); + (void)get_state_for_count(static_cast(lower_bound)); const auto rip = emu.emu().read_instruction_pointer(); diff --git a/src/windows-emulator/apiset/apiset.cpp b/src/windows-emulator/apiset/apiset.cpp index d0b32c7e..7006f4a7 100644 --- a/src/windows-emulator/apiset/apiset.cpp +++ b/src/windows-emulator/apiset/apiset.cpp @@ -57,7 +57,7 @@ namespace apiset { switch (location) { -#ifdef OS_WINDOWS +#ifdef OS_WINDOWS_64 case location::host: { const auto apiSetMap = reinterpret_cast(NtCurrentTeb64()->ProcessEnvironmentBlock->ApiSetMap); diff --git a/src/windows-emulator/emulator_thread.cpp b/src/windows-emulator/emulator_thread.cpp index 144bcbcd..6425a1d7 100644 --- a/src/windows-emulator/emulator_thread.cpp +++ b/src/windows-emulator/emulator_thread.cpp @@ -93,7 +93,7 @@ emulator_thread::emulator_thread(memory_manager& memory, const process_context& suspended(suspended), last_registers(context.default_register_set) { - this->stack_base = memory.allocate_memory(this->stack_size, memory_permission::read_write); + this->stack_base = memory.allocate_memory(static_cast(this->stack_size), memory_permission::read_write); this->gs_segment = emulator_allocator{ memory, @@ -214,7 +214,7 @@ void emulator_thread::setup_registers(x64_emulator& emu, const process_context& throw std::runtime_error("Missing GS segment"); } - setup_stack(emu, this->stack_base, this->stack_size); + setup_stack(emu, this->stack_base, static_cast(this->stack_size)); emu.set_segment_base(x64_register::gs, this->gs_segment->get_base()); CONTEXT64 ctx{}; diff --git a/src/windows-emulator/emulator_thread.hpp b/src/windows-emulator/emulator_thread.hpp index eab80c8c..07e3ed12 100644 --- a/src/windows-emulator/emulator_thread.hpp +++ b/src/windows-emulator/emulator_thread.hpp @@ -227,7 +227,7 @@ class emulator_thread : public ref_counted_object throw std::runtime_error("Emulator was never assigned!"); } - this->memory_ptr->release_memory(this->stack_base, this->stack_size); + this->memory_ptr->release_memory(this->stack_base, static_cast(this->stack_size)); this->stack_base = 0; } diff --git a/src/windows-emulator/emulator_utils.hpp b/src/windows-emulator/emulator_utils.hpp index b0e71ece..e32c803c 100644 --- a/src/windows-emulator/emulator_utils.hpp +++ b/src/windows-emulator/emulator_utils.hpp @@ -300,7 +300,8 @@ class emulator_allocator { if (this->address_ && this->size_) { - manager.release_memory(this->address_, this->size_); + // TODO: Make all sizes uint64_t + manager.release_memory(this->address_, static_cast(this->size_)); this->address_ = 0; this->size_ = 0; } diff --git a/src/windows-emulator/exception_dispatch.cpp b/src/windows-emulator/exception_dispatch.cpp index 96c5b9e8..3eae5182 100644 --- a/src/windows-emulator/exception_dispatch.cpp +++ b/src/windows-emulator/exception_dispatch.cpp @@ -109,7 +109,7 @@ namespace assert(total_size >= allocation_size); std::vector zero_memory{}; - zero_memory.resize(total_size, 0); + zero_memory.resize(static_cast(total_size), 0); emu.write_memory(new_sp, zero_memory.data(), zero_memory.size()); diff --git a/src/windows-emulator/kusd_mmio.cpp b/src/windows-emulator/kusd_mmio.cpp index 856d7187..95eadfa5 100644 --- a/src/windows-emulator/kusd_mmio.cpp +++ b/src/windows-emulator/kusd_mmio.cpp @@ -144,7 +144,7 @@ void kusd_mmio::read(const uint64_t addr, void* data, const size_t size) const auto real_size = valid_end - addr; const auto* kusd_buffer = reinterpret_cast(&this->kusd_); - memcpy(data, kusd_buffer + addr, real_size); + memcpy(data, kusd_buffer + addr, static_cast(real_size)); } uint64_t kusd_mmio::address() diff --git a/src/windows-emulator/memory_manager.cpp b/src/windows-emulator/memory_manager.cpp index 7c1a3de8..98268bef 100644 --- a/src/windows-emulator/memory_manager.cpp +++ b/src/windows-emulator/memory_manager.cpp @@ -22,9 +22,10 @@ namespace const auto first_length = split_point - i->first; const auto second_length = i->second.length - first_length; - i->second.length = first_length; + i->second.length = static_cast(first_length); - regions[split_point] = memory_manager::committed_region{second_length, i->second.permissions}; + regions[split_point] = + memory_manager::committed_region{static_cast(second_length), i->second.permissions}; } } } @@ -312,8 +313,8 @@ bool memory_manager::commit_memory(const uint64_t address, const size_t size, co if (map_length > 0) { - this->map_memory(map_start, map_length, permissions); - committed_regions[map_start] = committed_region{map_length, permissions}; + this->map_memory(map_start, static_cast(map_length), permissions); + committed_regions[map_start] = committed_region{static_cast(map_length), permissions}; } last_region_start = sub_region.first; @@ -326,8 +327,8 @@ bool memory_manager::commit_memory(const uint64_t address, const size_t size, co const auto map_start = last_region ? (last_region_start + last_region->length) : address; const auto map_length = end - map_start; - this->map_memory(map_start, map_length, permissions); - committed_regions[map_start] = committed_region{map_length, permissions}; + this->map_memory(map_start, static_cast(map_length), permissions); + committed_regions[map_start] = committed_region{static_cast(map_length), permissions}; } merge_regions(committed_regions); @@ -398,7 +399,7 @@ bool memory_manager::release_memory(const uint64_t address, size_t size) size = entry->second.length; } - size = page_align_up(size); + size = static_cast(page_align_up(size)); if (size > entry->second.length) { @@ -498,7 +499,7 @@ region_info memory_manager::get_region_info(const uint64_t address) { region_info result{}; result.start = MIN_ALLOCATION_ADDRESS; - result.length = MAX_ALLOCATION_ADDRESS - result.start; + result.length = static_cast(MAX_ALLOCATION_ADDRESS - result.start); result.permissions = memory_permission::none; result.initial_permissions = memory_permission::none; result.allocation_base = {}; @@ -514,7 +515,7 @@ region_info memory_manager::get_region_info(const uint64_t address) auto upper_bound = this->reserved_regions_.upper_bound(address); if (upper_bound == this->reserved_regions_.begin()) { - result.length = upper_bound->first - result.start; + result.length = static_cast(upper_bound->first - result.start); return result; } @@ -523,7 +524,7 @@ region_info memory_manager::get_region_info(const uint64_t address) if (lower_end <= address) { result.start = lower_end; - result.length = MAX_ALLOCATION_ADDRESS - result.start; + result.length = static_cast(MAX_ALLOCATION_ADDRESS - result.start); return result; } @@ -546,7 +547,7 @@ region_info memory_manager::get_region_info(const uint64_t address) auto committed_bound = committed_regions.upper_bound(address); if (committed_bound == committed_regions.begin()) { - result.length = committed_bound->first - result.start; + result.length = static_cast(committed_bound->first - result.start); return result; } @@ -555,7 +556,7 @@ region_info memory_manager::get_region_info(const uint64_t address) if (committed_lower_end <= address) { result.start = committed_lower_end; - result.length = lower_end - result.start; + result.length = static_cast(lower_end - result.start); return result; } diff --git a/src/windows-emulator/module/module_mapping.cpp b/src/windows-emulator/module/module_mapping.cpp index 939705e8..84c3ccd0 100644 --- a/src/windows-emulator/module/module_mapping.cpp +++ b/src/windows-emulator/module/module_mapping.cpp @@ -10,9 +10,9 @@ namespace uint64_t get_first_section_offset(const PENTHeaders_t& nt_headers, const uint64_t nt_headers_offset) { const auto* nt_headers_addr = reinterpret_cast(&nt_headers); - size_t optional_header_offset = + const size_t optional_header_offset = reinterpret_cast(&(nt_headers.OptionalHeader)) - reinterpret_cast(&nt_headers); - size_t optional_header_size = nt_headers.FileHeader.SizeOfOptionalHeader; + const size_t optional_header_size = nt_headers.FileHeader.SizeOfOptionalHeader; const auto* first_section_addr = nt_headers_addr + optional_header_offset + optional_header_size; const auto first_section_absolute = reinterpret_cast(first_section_addr); @@ -23,7 +23,7 @@ namespace std::vector read_mapped_memory(const memory_manager& memory, const mapped_module& binary) { std::vector mem{}; - mem.resize(binary.size_of_image); + mem.resize(static_cast(binary.size_of_image)); memory.read_memory(binary.image_base, mem.data(), mem.size()); return mem; @@ -73,7 +73,7 @@ namespace void apply_relocation(const utils::safe_buffer_accessor buffer, const uint64_t offset, const uint64_t delta) { - const auto obj = buffer.as(offset); + const auto obj = buffer.as(static_cast(offset)); const auto value = obj.get(); const auto new_value = value + static_cast(delta); obj.set(new_value); @@ -146,7 +146,7 @@ namespace const PENTHeaders_t& nt_headers, const uint64_t nt_headers_offset) { const auto first_section_offset = get_first_section_offset(nt_headers, nt_headers_offset); - const auto sections = buffer.as(first_section_offset); + const auto sections = buffer.as(static_cast(first_section_offset)); for (size_t i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) { @@ -179,11 +179,11 @@ namespace const auto size_of_section = page_align_up(std::max(section.SizeOfRawData, section.Misc.VirtualSize)); - memory.protect_memory(target_ptr, size_of_section, permissions, nullptr); + memory.protect_memory(target_ptr, static_cast(size_of_section), permissions, nullptr); mapped_section section_info{}; section_info.region.start = target_ptr; - section_info.region.length = size_of_section; + section_info.region.length = static_cast(size_of_section); section_info.region.permissions = permissions; for (size_t j = 0; j < sizeof(section.Name) && section.Name[j]; ++j) @@ -219,21 +219,22 @@ mapped_module map_module_from_data(memory_manager& memory, const std::span(binary.size_of_image), memory_permission::all)) { - binary.image_base = memory.find_free_allocation_base(binary.size_of_image); + binary.image_base = memory.find_free_allocation_base(static_cast(binary.size_of_image)); const auto is_dll = nt_headers.FileHeader.Characteristics & IMAGE_FILE_DLL; const auto has_dynamic_base = optional_header.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE; const auto is_relocatable = is_dll || has_dynamic_base; - if (!is_relocatable || !memory.allocate_memory(binary.image_base, binary.size_of_image, memory_permission::all)) + if (!is_relocatable || !memory.allocate_memory(binary.image_base, static_cast(binary.size_of_image), + memory_permission::all)) { throw std::runtime_error("Memory range not allocatable"); } } // TODO: Make sure to match kernel allocation patterns to attain correct initial permissions! - memory.protect_memory(binary.image_base, binary.size_of_image, memory_permission::read); + memory.protect_memory(binary.image_base, static_cast(binary.size_of_image), memory_permission::read); binary.entry_point = binary.image_base + optional_header.AddressOfEntryPoint; @@ -266,5 +267,5 @@ mapped_module map_module_from_file(memory_manager& memory, std::filesystem::path bool unmap_module(memory_manager& memory, const mapped_module& mod) { - return memory.release_memory(mod.image_base, mod.size_of_image); + return memory.release_memory(mod.image_base, static_cast(mod.size_of_image)); } diff --git a/src/windows-emulator/syscall_utils.hpp b/src/windows-emulator/syscall_utils.hpp index eb61b630..697e13e8 100644 --- a/src/windows-emulator/syscall_utils.hpp +++ b/src/windows-emulator/syscall_utils.hpp @@ -54,7 +54,8 @@ inline std::optional extract_syscall_id(const exported_symbol& symbol, const auto instruction_rva = symbol.rva + instruction_offset; - if (data.size() < (instruction_rva + instruction_size) || data[instruction_rva] != instruction_opcode) + if (data.size() < (instruction_rva + instruction_size) || + data[static_cast(instruction_rva)] != instruction_opcode) { return std::nullopt; } diff --git a/src/windows-emulator/syscalls/file.cpp b/src/windows-emulator/syscalls/file.cpp index 0cad972c..8b291a22 100644 --- a/src/windows-emulator/syscalls/file.cpp +++ b/src/windows-emulator/syscalls/file.cpp @@ -130,7 +130,7 @@ namespace syscalls auto& enum_state = *f->enumeration_state; - size_t current_offset{0}; + uint64_t current_offset{0}; emulator_object object{c.emu}; size_t current_index = enum_state.current_index; @@ -400,7 +400,8 @@ namespace syscalls std::cin.readsome(temp_buffer.data(), static_cast(temp_buffer.size())); const auto count = std::max(read_count, static_cast(0)); - commit_file_data(std::string_view(temp_buffer.data(), count), c.emu, io_status_block, buffer); + commit_file_data(std::string_view(temp_buffer.data(), static_cast(count)), c.emu, io_status_block, + buffer); return STATUS_SUCCESS; } @@ -848,4 +849,4 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } -} \ No newline at end of file +} diff --git a/src/windows-emulator/syscalls/locale.cpp b/src/windows-emulator/syscalls/locale.cpp index 418993bb..c1768148 100644 --- a/src/windows-emulator/syscalls/locale.cpp +++ b/src/windows-emulator/syscalls/locale.cpp @@ -17,7 +17,7 @@ namespace syscalls return STATUS_FILE_INVALID; } - const auto size = page_align_up(locale_file.size()); + const auto size = static_cast(page_align_up(locale_file.size())); const auto base = c.win_emu.memory.allocate_memory(size, memory_permission::read); c.emu.write_memory(base, locale_file.data(), locale_file.size()); @@ -58,4 +58,4 @@ namespace syscalls { return STATUS_NOT_SUPPORTED; } -} \ No newline at end of file +} diff --git a/src/windows-emulator/syscalls/memory.cpp b/src/windows-emulator/syscalls/memory.cpp index 7e12521d..26d3cb98 100644 --- a/src/windows-emulator/syscalls/memory.cpp +++ b/src/windows-emulator/syscalls/memory.cpp @@ -41,8 +41,8 @@ namespace syscalls assert(!region_info.is_committed || region_info.is_reserved); const auto state = region_info.is_reserved ? MEM_RESERVE : MEM_FREE; image_info.State = region_info.is_committed ? MEM_COMMIT : state; - image_info.BaseAddress = reinterpret_cast(region_info.start); - image_info.AllocationBase = reinterpret_cast(region_info.allocation_base); + image_info.BaseAddress = region_info.start; + image_info.AllocationBase = region_info.allocation_base; image_info.PartitionId = 0; image_info.RegionSize = static_cast(region_info.length); @@ -151,7 +151,8 @@ namespace syscalls try { - c.win_emu.memory.protect_memory(aligned_start, aligned_length, requested_protection, &old_protection_value); + c.win_emu.memory.protect_memory(aligned_start, static_cast(aligned_length), requested_protection, + &old_protection_value); } catch (...) { @@ -183,7 +184,7 @@ namespace syscalls auto potential_base = base_address.read(); if (!potential_base) { - potential_base = c.win_emu.memory.find_free_allocation_base(allocation_bytes); + potential_base = c.win_emu.memory.find_free_allocation_base(static_cast(allocation_bytes)); } if (!potential_base) @@ -203,7 +204,8 @@ namespace syscalls throw std::runtime_error("Unsupported allocation type!"); } - if (commit && !reserve && c.win_emu.memory.commit_memory(potential_base, allocation_bytes, protection)) + if (commit && !reserve && + c.win_emu.memory.commit_memory(potential_base, static_cast(allocation_bytes), protection)) { c.win_emu.log.print(color::dark_gray, "--> Committed 0x%" PRIx64 " - 0x%" PRIx64 "\n", potential_base, potential_base + allocation_bytes); @@ -214,7 +216,8 @@ namespace syscalls c.win_emu.log.print(color::dark_gray, "--> Allocated 0x%" PRIx64 " - 0x%" PRIx64 "\n", potential_base, potential_base + allocation_bytes); - return c.win_emu.memory.allocate_memory(potential_base, allocation_bytes, protection, !commit) + return c.win_emu.memory.allocate_memory(potential_base, static_cast(allocation_bytes), protection, + !commit) ? STATUS_SUCCESS : STATUS_MEMORY_NOT_ALLOCATED; } @@ -242,14 +245,16 @@ namespace syscalls if (free_type & MEM_RELEASE) { - return c.win_emu.memory.release_memory(allocation_base, allocation_size) ? STATUS_SUCCESS - : STATUS_MEMORY_NOT_ALLOCATED; + return c.win_emu.memory.release_memory(allocation_base, static_cast(allocation_size)) + ? STATUS_SUCCESS + : STATUS_MEMORY_NOT_ALLOCATED; } if (free_type & MEM_DECOMMIT) { - return c.win_emu.memory.decommit_memory(allocation_base, allocation_size) ? STATUS_SUCCESS - : STATUS_MEMORY_NOT_ALLOCATED; + return c.win_emu.memory.decommit_memory(allocation_base, static_cast(allocation_size)) + ? STATUS_SUCCESS + : STATUS_MEMORY_NOT_ALLOCATED; } throw std::runtime_error("Bad free type"); @@ -284,4 +289,4 @@ namespace syscalls { return STATUS_NOT_SUPPORTED; } -} \ No newline at end of file +} diff --git a/src/windows-emulator/syscalls/port.cpp b/src/windows-emulator/syscalls/port.cpp index 28e91e8b..bfade094 100644 --- a/src/windows-emulator/syscalls/port.cpp +++ b/src/windows-emulator/syscalls/port.cpp @@ -27,7 +27,8 @@ namespace syscalls } client_shared_memory.access([&](PORT_VIEW64& view) { - p.view_base = c.win_emu.memory.allocate_memory(view.ViewSize, memory_permission::read_write); + p.view_base = + c.win_emu.memory.allocate_memory(static_cast(view.ViewSize), memory_permission::read_write); view.ViewBase = p.view_base; view.ViewRemoteBase = view.ViewBase; }); @@ -76,4 +77,4 @@ namespace syscalls { return STATUS_NOT_SUPPORTED; } -} \ No newline at end of file +} diff --git a/src/windows-emulator/syscalls/section.cpp b/src/windows-emulator/syscalls/section.cpp index 415206dd..fab75901 100644 --- a/src/windows-emulator/syscalls/section.cpp +++ b/src/windows-emulator/syscalls/section.cpp @@ -227,7 +227,7 @@ namespace syscalls const auto reserve_only = section_entry->allocation_attributes == SEC_RESERVE; const auto protection = map_nt_to_emulator_protection(section_entry->section_page_protection); - const auto address = c.win_emu.memory.allocate_memory(size, protection, reserve_only); + const auto address = c.win_emu.memory.allocate_memory(static_cast(size), protection, reserve_only); if (!reserve_only && !file_data.empty()) { @@ -289,4 +289,4 @@ namespace syscalls { return handle_NtUnmapViewOfSection(c, process_handle, base_address); } -} \ No newline at end of file +} diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 30bf3123..4cb8b45f 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -341,8 +341,8 @@ void windows_emulator::setup_process(const application_settings& app_settings) this->process.setup(this->emu(), this->memory, app_settings, *executable, *ntdll, apiset_data); - const auto ntdll_data = emu.read_memory(ntdll->image_base, ntdll->size_of_image); - const auto win32u_data = emu.read_memory(win32u->image_base, win32u->size_of_image); + const auto ntdll_data = emu.read_memory(ntdll->image_base, static_cast(ntdll->size_of_image)); + const auto win32u_data = emu.read_memory(win32u->image_base, static_cast(win32u->size_of_image)); this->dispatcher.setup(ntdll->exports, ntdll_data, win32u->exports, win32u_data); @@ -601,7 +601,7 @@ void windows_emulator::start(size_t count) break; } - count = target_instructions - current_instructions; + count = static_cast(target_instructions - current_instructions); } } } diff --git a/src/windows-gdb-stub/x64_gdb_stub_handler.hpp b/src/windows-gdb-stub/x64_gdb_stub_handler.hpp index aa519acd..3fb33992 100644 --- a/src/windows-gdb-stub/x64_gdb_stub_handler.hpp +++ b/src/windows-gdb-stub/x64_gdb_stub_handler.hpp @@ -10,7 +10,7 @@ struct breakpoint_key { - size_t addr{}; + uint64_t addr{}; size_t size{}; gdb_stub::breakpoint_type type{}; @@ -25,7 +25,7 @@ struct std::hash { std::size_t operator()(const breakpoint_key& k) const noexcept { - return ((std::hash()(k.addr) ^ (std::hash()(k.size) << 1)) >> 1) ^ + return ((std::hash()(k.addr) ^ (std::hash()(k.size) << 1)) >> 1) ^ (std::hash()(static_cast(k.type)) << 1); } }; From 00084e8ad045835c02a8e417623eacc9eed5b029 Mon Sep 17 00:00:00 2001 From: Maurice Heumann Date: Mon, 14 Apr 2025 13:56:08 +0200 Subject: [PATCH 008/542] Some fixes --- src/backends/unicorn-emulator/unicorn_x64_emulator.cpp | 10 ++++++---- src/windows-emulator/module/module_manager.cpp | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp index cd8a94cb..b4b3ce47 100644 --- a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp +++ b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp @@ -207,8 +207,10 @@ namespace unicorn #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #endif - - uce(uc_ctl_set_tcg_buffer_size(this->uc_, 2 << 30 /* 2 gb */)); + if constexpr (sizeof(void*) >= 8) + { + uce(uc_ctl_set_tcg_buffer_size(this->uc_, 2 << 30 /* 2 gb */)); + } #ifndef OS_WINDOWS #pragma GCC diagnostic pop @@ -264,8 +266,8 @@ namespace unicorn struct msr_value { - uint32_t id; - uint64_t value; + uint64_t id{}; + uint64_t value{}; }; msr_value msr_val{ diff --git a/src/windows-emulator/module/module_manager.cpp b/src/windows-emulator/module/module_manager.cpp index 3c1b10d4..6e20e135 100644 --- a/src/windows-emulator/module/module_manager.cpp +++ b/src/windows-emulator/module/module_manager.cpp @@ -107,7 +107,7 @@ mapped_module* module_manager::map_module(const windows_path& file, const logger mapped_module* module_manager::map_local_module(const std::filesystem::path& file, const logger& logger, const bool is_static) { - auto local_file = canonical(absolute(file)); + auto local_file = weakly_canonical(absolute(file)); for (auto& mod : this->modules_ | std::views::values) { From 0e9cffa5cb616181639be04a955386fb411e99aa Mon Sep 17 00:00:00 2001 From: Maurice Heumann Date: Mon, 14 Apr 2025 14:15:58 +0200 Subject: [PATCH 009/542] More 32 bit fixes --- src/common/platform/kernel_mapped.hpp | 239 ++++++++++---------- src/common/platform/threading.hpp | 2 +- src/tools/dump-apiset/dump-apiset.cpp | 2 +- src/windows-emulator/apiset/apiset.cpp | 9 +- src/windows-emulator/emulator_thread.cpp | 8 +- src/windows-emulator/emulator_utils.hpp | 9 +- src/windows-emulator/exception_dispatch.cpp | 2 +- src/windows-emulator/process_context.cpp | 12 +- src/windows-emulator/syscalls/process.cpp | 15 +- src/windows-emulator/syscalls/thread.cpp | 6 +- 10 files changed, 152 insertions(+), 152 deletions(-) diff --git a/src/common/platform/kernel_mapped.hpp b/src/common/platform/kernel_mapped.hpp index 5dca4e62..c173f574 100644 --- a/src/common/platform/kernel_mapped.hpp +++ b/src/common/platform/kernel_mapped.hpp @@ -19,13 +19,13 @@ typedef struct _EMU_NT_TIB64 { - struct _EXCEPTION_REGISTRATION_RECORD* ExceptionList; - std::uint64_t* StackBase; - std::uint64_t* StackLimit; - std::uint64_t* SubSystemTib; - std::uint64_t* FibreData; - std::uint64_t* ArbitraryUserPointer; - struct _EMU_NT_TIB64* Self; + EMULATOR_CAST(std::uint64_t, struct _EXCEPTION_REGISTRATION_RECORD*) ExceptionList; + std::uint64_t StackBase; + std::uint64_t StackLimit; + std::uint64_t SubSystemTib; + std::uint64_t FibreData; + std::uint64_t ArbitraryUserPointer; + EMULATOR_CAST(std::uint64_t, struct _EMU_NT_TIB64*) Self; } EMU_NT_TIB64; typedef EMU_NT_TIB64* PEMU_NT_TIB64; @@ -65,17 +65,14 @@ typedef struct _PEB_LDR_DATA64 LIST_ENTRY64 InLoadOrderModuleList; LIST_ENTRY64 InMemoryOrderModuleList; LIST_ENTRY64 InInitializationOrderModuleList; - std::uint64_t* EntryInProgress; + std::uint64_t EntryInProgress; BOOLEAN ShutdownInProgress; EmulatorTraits::HANDLE ShutdownThreadId; } PEB_LDR_DATA64, *PPEB_LDR_DATA64; -typedef struct _STRING64 -{ - USHORT Length; - USHORT MaximumLength; - char16_t* Buffer; -} STRING64, *PSTRING64, ANSI_STRING64, *PANSI_STRING64, OEM_STRING64, *POEM_STRING64; +using STRING64 = UNICODE_STRING>; +using ANSI_STRING64 = STRING64; +using OEM_STRING64 = STRING64; typedef struct _RTL_DRIVE_LETTER_CURDIR64 { @@ -118,7 +115,7 @@ typedef struct _RTL_USER_PROCESS_PARAMETERS64 UNICODE_STRING> DllPath; UNICODE_STRING> ImagePathName; UNICODE_STRING> CommandLine; - std::uint64_t* Environment; + std::uint64_t Environment; ULONG StartingX; ULONG StartingY; @@ -136,21 +133,23 @@ typedef struct _RTL_USER_PROCESS_PARAMETERS64 UNICODE_STRING> RuntimeData; ARRAY_CONTAINER CurrentDirectories; - std::uint64_t* EnvironmentSize; - std::uint64_t* EnvironmentVersion; + std::uint64_t EnvironmentSize; + std::uint64_t EnvironmentVersion; - std::uint64_t* PackageDependencyData; + std::uint64_t PackageDependencyData; ULONG ProcessGroupId; ULONG LoaderThreads; UNICODE_STRING> RedirectionDllName; // REDSTONE4 UNICODE_STRING> HeapPartitionName; // 19H1 - std::uint64_t* DefaultThreadpoolCpuSetMasks; + std::uint64_t DefaultThreadpoolCpuSetMasks; ULONG DefaultThreadpoolCpuSetMaskCount; ULONG DefaultThreadpoolThreadMaximum; ULONG HeapMemoryTypeMask; // WIN11 } RTL_USER_PROCESS_PARAMETERS64, *PRTL_USER_PROCESS_PARAMETERS64; +static_assert(sizeof(RTL_USER_PROCESS_PARAMETERS64) == 0x448); + union PEB_CROSS_PROCESS_FLAGS_UNION { ULONG CrossProcessFlags; @@ -171,8 +170,8 @@ union PEB_CROSS_PROCESS_FLAGS_UNION union PEB_KERNEL_CALLBACK_TABLE_UNION64 { - void* KernelCallbackTable; - void* UserSharedInfoPtr; + std::uint64_t KernelCallbackTable; + std::uint64_t UserSharedInfoPtr; }; typedef struct _API_SET_NAMESPACE @@ -253,26 +252,26 @@ typedef struct _PEB64 EmulatorTraits::HANDLE Mutant; std::uint64_t ImageBaseAddress; - PPEB_LDR_DATA64 Ldr; - PRTL_USER_PROCESS_PARAMETERS64 ProcessParameters; - std::uint64_t* SubSystemData; - std::uint64_t* ProcessHeap; - EMULATOR_CAST(void*, PRTL_CRITICAL_SECTION) FastPebLock; - EMULATOR_CAST(void*, PSLIST_HEADER) AtlThunkSListPtr; - std::uint64_t* IFEOKey; + EMULATOR_CAST(std::uint64_t, PPEB_LDR_DATA64) Ldr; + EMULATOR_CAST(std::uint64_t, PRTL_USER_PROCESS_PARAMETERS64) ProcessParameters; + std::uint64_t SubSystemData; + std::uint64_t ProcessHeap; + EMULATOR_CAST(std::uint64_t, PRTL_CRITICAL_SECTION) FastPebLock; + EMULATOR_CAST(std::uint64_t, PSLIST_HEADER) AtlThunkSListPtr; + std::uint64_t IFEOKey; PEB_CROSS_PROCESS_FLAGS_UNION CrossProcessFlags; PEB_KERNEL_CALLBACK_TABLE_UNION64 KernelCallbackTable; ULONG SystemReserved; ULONG AtlThunkSListPtr32; - PAPI_SET_NAMESPACE ApiSetMap; + EMULATOR_CAST(std::uint64_t, PAPI_SET_NAMESPACE) ApiSetMap; ULONG TlsExpansionCounter; - EMULATOR_CAST(void*, PRTL_BITMAP) TlsBitmap; + EMULATOR_CAST(std::uint64_t, PRTL_BITMAP) TlsBitmap; ARRAY_CONTAINER TlsBitmapBits; // TLS_MINIMUM_AVAILABLE - void* ReadOnlySharedMemoryBase; - EMULATOR_CAST(void*, PSILO_USER_SHARED_DATA) SharedData; // HotpatchInformation - std::uint64_t** ReadOnlyStaticServerData; + std::uint64_t ReadOnlySharedMemoryBase; + EMULATOR_CAST(std::uint64_t, PSILO_USER_SHARED_DATA) SharedData; // HotpatchInformation + std::uint64_t ReadOnlyStaticServerData; EMULATOR_CAST(EmulatorTraits::PVOID, PCPTABLEINFO) AnsiCodePageData; // PCPTABLEINFO EMULATOR_CAST(EmulatorTraits::PVOID, PCPTABLEINFO) OemCodePageData; // PCPTABLEINFO @@ -289,13 +288,13 @@ typedef struct _PEB64 ULONG NumberOfHeaps; ULONG MaximumNumberOfHeaps; - std::uint64_t** ProcessHeaps; // PHEAP + std::uint64_t ProcessHeaps; // PHEAP std::uint64_t GdiSharedHandleTable; // PGDI_SHARED_MEMORY - std::uint64_t* ProcessStarterHelper; + std::uint64_t ProcessStarterHelper; ULONG GdiDCAttributeList; - EMULATOR_CAST(void*, PRTL_CRITICAL_SECTION) LoaderLock; + EMULATOR_CAST(std::uint64_t, PRTL_CRITICAL_SECTION) LoaderLock; ULONG OSMajorVersion; ULONG OSMinorVersion; @@ -307,30 +306,30 @@ typedef struct _PEB64 ULONG ImageSubsystemMinorVersion; EMULATOR_CAST(std::uint64_t, KAFFINITY) ActiveProcessAffinityMask; ARRAY_CONTAINER GdiHandleBuffer; - std::uint64_t* PostProcessInitRoutine; + std::uint64_t PostProcessInitRoutine; - EMULATOR_CAST(void*, PRTL_BITMAP) TlsExpansionBitmap; + EMULATOR_CAST(std::uint64_t, PRTL_BITMAP) TlsExpansionBitmap; ARRAY_CONTAINER TlsExpansionBitmapBits; // TLS_EXPANSION_SLOTS ULONG SessionId; ULARGE_INTEGER AppCompatFlags; // KACF_* ULARGE_INTEGER AppCompatFlagsUser; - std::uint64_t* pShimData; - std::uint64_t* AppCompatInfo; // APPCOMPAT_EXE_DATA + std::uint64_t pShimData; + std::uint64_t AppCompatInfo; // APPCOMPAT_EXE_DATA UNICODE_STRING> CSDVersion; - EMULATOR_CAST(void*, PACTIVATION_CONTEXT_DATA) ActivationContextData; - EMULATOR_CAST(void*, PASSEMBLY_STORAGE_MAP) ProcessAssemblyStorageMap; - EMULATOR_CAST(void*, PACTIVATION_CONTEXT_DATA) SystemDefaultActivationContextData; - EMULATOR_CAST(void*, PASSEMBLY_STORAGE_MAP) SystemAssemblyStorageMap; + EMULATOR_CAST(std::uint64_t, PACTIVATION_CONTEXT_DATA) ActivationContextData; + EMULATOR_CAST(std::uint64_t, PASSEMBLY_STORAGE_MAP) ProcessAssemblyStorageMap; + EMULATOR_CAST(std::uint64_t, PACTIVATION_CONTEXT_DATA) SystemDefaultActivationContextData; + EMULATOR_CAST(std::uint64_t, PASSEMBLY_STORAGE_MAP) SystemAssemblyStorageMap; - EMULATOR_CAST(std::int64_t, SIZE_T) MinimumStackCommit; + EMULATOR_CAST(std::uint64_t, SIZE_T) MinimumStackCommit; - ARRAY_CONTAINER SparePointers; // 19H1 (previously FlsCallback to FlsHighIndex) - std::uint64_t* PatchLoaderData; - std::uint64_t* ChpeV2ProcessInfo; // _CHPEV2_PROCESS_INFO + ARRAY_CONTAINER SparePointers; // 19H1 (previously FlsCallback to FlsHighIndex) + std::uint64_t PatchLoaderData; + std::uint64_t ChpeV2ProcessInfo; // _CHPEV2_PROCESS_INFO ULONG AppModelFeatureState; ARRAY_CONTAINER SpareUlongs; @@ -340,40 +339,42 @@ typedef struct _PEB64 USHORT UseCaseMapping; USHORT UnusedNlsField; - std::uint64_t* WerRegistrationData; - std::uint64_t* WerShipAssertPtr; + std::uint64_t WerRegistrationData; + std::uint64_t WerShipAssertPtr; PEB_CONTEXT_DATA_UNION64 ContextData; - std::uint64_t* pImageHeaderHash; + std::uint64_t pImageHeaderHash; PEB_TRACING_FLAGS_UNION TracingFlags; ULONGLONG CsrServerReadOnlySharedMemoryBase; - EMULATOR_CAST(void*, PRTL_CRITICAL_SECTION) TppWorkerpListLock; + EMULATOR_CAST(std::uint64_t, PRTL_CRITICAL_SECTION) TppWorkerpListLock; LIST_ENTRY64 TppWorkerpList; - ARRAY_CONTAINER WaitOnAddressHashTable; - EMULATOR_CAST(void*, PTELEMETRY_COVERAGE_HEADER) TelemetryCoverageHeader; // REDSTONE3 + ARRAY_CONTAINER WaitOnAddressHashTable; + EMULATOR_CAST(std::uint64_t, PTELEMETRY_COVERAGE_HEADER) TelemetryCoverageHeader; // REDSTONE3 ULONG CloudFileFlags; ULONG CloudFileDiagFlags; // REDSTONE4 CHAR PlaceholderCompatibilityMode; ARRAY_CONTAINER PlaceholderCompatibilityModeReserved; - EMULATOR_CAST(void*, PLEAP_SECOND_DATA) LeapSecondData; // REDSTONE5 + EMULATOR_CAST(std::uint64_t, PLEAP_SECOND_DATA) LeapSecondData; // REDSTONE5 PEB_LEAP_SECONDS_FLAG_UNION LeapSecondFlags; ULONG NtGlobalFlag2; ULONGLONG ExtendedFeatureDisableMask; // since WIN11 } PEB64, *PPEB64; +static_assert(sizeof(PEB64) == 0x7D0); + typedef struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME64 { struct _RTL_ACTIVATION_CONTEXT_STACK_FRAME* Previous; - EMULATOR_CAST(void*, ACTIVATION_CONTEXT) ActivationContext; + EMULATOR_CAST(std::uint64_t, ACTIVATION_CONTEXT) ActivationContext; ULONG Flags; // RTL_ACTIVATION_CONTEXT_STACK_FRAME_FLAG_* } RTL_ACTIVATION_CONTEXT_STACK_FRAME64, *PRTL_ACTIVATION_CONTEXT_STACK_FRAME64; typedef struct _ACTIVATION_CONTEXT_STACK64 { - PRTL_ACTIVATION_CONTEXT_STACK_FRAME64 ActiveFrame; + EMULATOR_CAST(std::uint64_t, PRTL_ACTIVATION_CONTEXT_STACK_FRAME64) ActiveFrame; LIST_ENTRY64 FrameListCache; ULONG Flags; // ACTIVATION_CONTEXT_STACK_FLAG_* ULONG NextCookieSequenceNumber; @@ -383,7 +384,7 @@ typedef struct _ACTIVATION_CONTEXT_STACK64 typedef struct _GDI_TEB_BATCH64 { ULONG Offset; - std::uint64_t* HDC; + std::uint64_t HDC; ULONG Buffer[GDI_BATCH_BUFFER_SIZE]; } GDI_TEB_BATCH64, *PGDI_TEB_BATCH64; @@ -458,25 +459,25 @@ typedef struct _TEB64 { EMU_NT_TIB64 NtTib; - std::uint64_t* EnvironmentPointer; + std::uint64_t EnvironmentPointer; CLIENT_ID64 ClientId; - std::uint64_t* ActiveRpcHandle; - std::uint64_t* ThreadLocalStoragePointer; - PPEB64 ProcessEnvironmentBlock; + std::uint64_t ActiveRpcHandle; + std::uint64_t ThreadLocalStoragePointer; + EMULATOR_CAST(std::uint64_t, PPEB64) ProcessEnvironmentBlock; ULONG LastErrorValue; ULONG CountOfOwnedCriticalSections; - std::uint64_t* CsrClientThread; - std::uint64_t* Win32ThreadInfo; + std::uint64_t CsrClientThread; + std::uint64_t Win32ThreadInfo; ARRAY_CONTAINER User32Reserved; ARRAY_CONTAINER UserReserved; - std::uint64_t* WOW32Reserved; + std::uint64_t WOW32Reserved; LCID CurrentLocale; ULONG FpSoftwareStatusRegister; - ARRAY_CONTAINER ReservedForDebuggerInstrumentation; - ARRAY_CONTAINER SystemReserved1; - std::uint64_t* HeapFlsData; - ARRAY_CONTAINER RngState; + ARRAY_CONTAINER ReservedForDebuggerInstrumentation; + ARRAY_CONTAINER SystemReserved1; + std::uint64_t HeapFlsData; + ARRAY_CONTAINER RngState; CHAR PlaceholderCompatibilityMode; BOOLEAN PlaceholderHydrationAlwaysExplicit; ARRAY_CONTAINER PlaceholderReserved; @@ -488,10 +489,10 @@ typedef struct _TEB64 NTSTATUS ExceptionCode; - PACTIVATION_CONTEXT_STACK64 ActivationContextStackPointer; - std::uint64_t* InstrumentationCallbackSp; - std::uint64_t* InstrumentationCallbackPreviousPc; - std::uint64_t* InstrumentationCallbackPreviousSp; + EMULATOR_CAST(std::uint64_t, PACTIVATION_CONTEXT_STACK64) ActivationContextStackPointer; + std::uint64_t InstrumentationCallbackSp; + std::uint64_t InstrumentationCallbackPreviousPc; + std::uint64_t InstrumentationCallbackPreviousSp; ULONG TxFsContext; BOOLEAN InstrumentationCallbackDisabled; BOOLEAN UnalignedLoadStoreExceptions; @@ -500,89 +501,91 @@ typedef struct _TEB64 EmulatorTraits::HANDLE GdiCachedProcessHandle; ULONG GdiClientPID; ULONG GdiClientTID; - std::uint64_t* GdiThreadLocalInfo; - ARRAY_CONTAINER Win32ClientInfo; + std::uint64_t GdiThreadLocalInfo; + ARRAY_CONTAINER Win32ClientInfo; - ARRAY_CONTAINER glDispatchTable; - ARRAY_CONTAINER glReserved1; - std::uint64_t* glReserved2; - std::uint64_t* glSectionInfo; - std::uint64_t* glSection; - std::uint64_t* glTable; - std::uint64_t* glCurrentRC; - std::uint64_t* glContext; + ARRAY_CONTAINER glDispatchTable; + ARRAY_CONTAINER glReserved1; + std::uint64_t glReserved2; + std::uint64_t glSectionInfo; + std::uint64_t glSection; + std::uint64_t glTable; + std::uint64_t glCurrentRC; + std::uint64_t glContext; NTSTATUS LastStatusValue; UNICODE_STRING> StaticUnicodeString; ARRAY_CONTAINER StaticUnicodeBuffer; - std::uint64_t* DeallocationStack; + std::uint64_t DeallocationStack; - ARRAY_CONTAINER TlsSlots; + ARRAY_CONTAINER TlsSlots; LIST_ENTRY64 TlsLinks; - std::uint64_t* Vdm; - std::uint64_t* ReservedForNtRpc; - ARRAY_CONTAINER DbgSsReserved; + std::uint64_t Vdm; + std::uint64_t ReservedForNtRpc; + ARRAY_CONTAINER DbgSsReserved; ULONG HardErrorMode; - ARRAY_CONTAINER Instrumentation; + ARRAY_CONTAINER Instrumentation; GUID ActivityId; - std::uint64_t* SubProcessTag; - std::uint64_t* PerflibData; - std::uint64_t* EtwTraceData; - std::uint64_t* WinSockData; + std::uint64_t SubProcessTag; + std::uint64_t PerflibData; + std::uint64_t EtwTraceData; + std::uint64_t WinSockData; ULONG GdiBatchCount; TEB_CURRENT_IDEAL_PROCESSOR_UNION CurrentIdealProcessor; ULONG GuaranteedStackBytes; - std::uint64_t* ReservedForPerf; - std::uint64_t* ReservedForOle; // tagSOleTlsData + std::uint64_t ReservedForPerf; + std::uint64_t ReservedForOle; // tagSOleTlsData ULONG WaitingOnLoaderLock; - std::uint64_t* SavedPriorityState; - std::uint64_t* ReservedForCodeCoverage; - std::uint64_t* ThreadPoolData; - std::uint64_t** TlsExpansionSlots; - std::uint64_t* ChpeV2CpuAreaInfo; // CHPEV2_CPUAREA_INFO // previously DeallocationBStore - std::uint64_t* Unused; // previously BStoreLimit + std::uint64_t SavedPriorityState; + std::uint64_t ReservedForCodeCoverage; + std::uint64_t ThreadPoolData; + std::uint64_t TlsExpansionSlots; + std::uint64_t ChpeV2CpuAreaInfo; // CHPEV2_CPUAREA_INFO // previously DeallocationBStore + std::uint64_t Unused; // previously BStoreLimit ULONG MuiGeneration; ULONG IsImpersonating; - std::uint64_t* NlsCache; - std::uint64_t* pShimData; + std::uint64_t NlsCache; + std::uint64_t pShimData; ULONG HeapData; EmulatorTraits::HANDLE CurrentTransactionHandle; - EMULATOR_CAST(void*, PTEB_ACTIVE_FRAME) ActiveFrame; - std::uint64_t* FlsData; + EMULATOR_CAST(std::uint64_t, PTEB_ACTIVE_FRAME) ActiveFrame; + std::uint64_t FlsData; - std::uint64_t* PreferredLanguages; - std::uint64_t* UserPrefLanguages; - std::uint64_t* MergedPrefLanguages; + std::uint64_t PreferredLanguages; + std::uint64_t UserPrefLanguages; + std::uint64_t MergedPrefLanguages; ULONG MuiImpersonation; TEB_CROSS_TEB_FLAGS_UNION CrossTebFlags; TEB_SAME_TEB_FLAGS_UNION SameTebFlags; - std::uint64_t* TxnScopeEnterCallback; - std::uint64_t* TxnScopeExitCallback; - std::uint64_t* TxnScopeContext; + std::uint64_t TxnScopeEnterCallback; + std::uint64_t TxnScopeExitCallback; + std::uint64_t TxnScopeContext; ULONG LockCount; LONG WowTebOffset; - std::uint64_t* ResourceRetValue; - std::uint64_t* ReservedForWdf; + std::uint64_t ResourceRetValue; + std::uint64_t ReservedForWdf; ULONGLONG ReservedForCrt; GUID EffectiveContainerId; ULONGLONG LastSleepCounter; // Win11 ULONG SpinCallCount; ULONGLONG ExtendedFeatureDisableMask; - std::uint64_t* SchedulerSharedDataSlot; // 24H2 - std::uint64_t* HeapWalkContext; + std::uint64_t SchedulerSharedDataSlot; // 24H2 + std::uint64_t HeapWalkContext; EMU_GROUP_AFFINITY64 PrimaryGroupAffinity; ARRAY_CONTAINER Rcu; } TEB64, *PTEB64; +static_assert(sizeof(TEB64) == 0x1878); + #if defined(OS_WINDOWS) && defined(_WIN64) inline TEB64* NtCurrentTeb64() { @@ -838,7 +841,7 @@ struct PS_ATTRIBUTE typename Traits::PVOID ValuePtr; }; - typename Traits::SIZE_T* ReturnLength; + EMULATOR_CAST(uint64_t, typename Traits::SIZE_T*) ReturnLength; }; template @@ -862,7 +865,7 @@ typedef struct _SYSTEM_TIMEOFDAY_INFORMATION64 typedef struct _PROCESS_BASIC_INFORMATION64 { NTSTATUS ExitStatus; - PPEB64 PebBaseAddress; + EMULATOR_CAST(uint64_t, PPEB64) PebBaseAddress; EMULATOR_CAST(std::uint64_t, KAFFINITY) AffinityMask; EMULATOR_CAST(std::uint32_t, KPRIORITY) BasePriority; EMULATOR_CAST(std::uint64_t, HANDLE) UniqueProcessId; @@ -883,7 +886,7 @@ struct THREAD_TLS_INFO union { - EmulatorTraits::PVOID* TlsVector; + EmulatorTraits::PVOID TlsVector; EmulatorTraits::PVOID TlsModulePointer; }; diff --git a/src/common/platform/threading.hpp b/src/common/platform/threading.hpp index de430220..4fe7deaf 100644 --- a/src/common/platform/threading.hpp +++ b/src/common/platform/threading.hpp @@ -76,7 +76,7 @@ struct THREAD_NAME_INFORMATION typedef struct _THREAD_BASIC_INFORMATION64 { NTSTATUS ExitStatus; - PTEB64 TebBaseAddress; + EMULATOR_CAST(uint64_t, PTEB64) TebBaseAddress; CLIENT_ID64 ClientId; EMULATOR_CAST(std::uint64_t, KAFFINITY) AffinityMask; EMULATOR_CAST(std::uint32_t, KPRIORITY) Priority; diff --git a/src/tools/dump-apiset/dump-apiset.cpp b/src/tools/dump-apiset/dump-apiset.cpp index 548e0bec..8b18741f 100644 --- a/src/tools/dump-apiset/dump-apiset.cpp +++ b/src/tools/dump-apiset/dump-apiset.cpp @@ -22,7 +22,7 @@ int main() printf("------------\n\n"); const auto peb = static_cast(GetCurrentProcessPeb()); - const auto api_set_map = peb->ApiSetMap; + const auto api_set_map = reinterpret_cast(peb->ApiSetMap); printf("APISET: 0x%p\n", api_set_map); printf("Version: %d\n", api_set_map->Version); diff --git a/src/windows-emulator/apiset/apiset.cpp b/src/windows-emulator/apiset/apiset.cpp index 7006f4a7..53492205 100644 --- a/src/windows-emulator/apiset/apiset.cpp +++ b/src/windows-emulator/apiset/apiset.cpp @@ -59,10 +59,11 @@ namespace apiset { #ifdef OS_WINDOWS_64 case location::host: { - const auto apiSetMap = - reinterpret_cast(NtCurrentTeb64()->ProcessEnvironmentBlock->ApiSetMap); - const auto* dataPtr = reinterpret_cast(apiSetMap); - return {dataPtr, dataPtr + apiSetMap->Size}; + const auto* teb = NtCurrentTeb64(); + const auto* peb = reinterpret_cast(teb->ProcessEnvironmentBlock); + const auto* api_set_map = reinterpret_cast(peb->ApiSetMap); + const auto* data_ptr = reinterpret_cast(api_set_map); + return {data_ptr, data_ptr + api_set_map->Size}; } #else case location::host: diff --git a/src/windows-emulator/emulator_thread.cpp b/src/windows-emulator/emulator_thread.cpp index 6425a1d7..c3f86a34 100644 --- a/src/windows-emulator/emulator_thread.cpp +++ b/src/windows-emulator/emulator_thread.cpp @@ -111,10 +111,10 @@ emulator_thread::emulator_thread(memory_manager& memory, const process_context& teb_obj.ClientId.UniqueProcess = 1ul; teb_obj.ClientId.UniqueThread = static_cast(this->id); - teb_obj.NtTib.StackLimit = reinterpret_cast(this->stack_base); - teb_obj.NtTib.StackBase = reinterpret_cast(this->stack_base + this->stack_size); - teb_obj.NtTib.Self = &this->teb->ptr()->NtTib; - teb_obj.ProcessEnvironmentBlock = context.peb.ptr(); + teb_obj.NtTib.StackLimit = this->stack_base; + teb_obj.NtTib.StackBase = this->stack_base + this->stack_size; + teb_obj.NtTib.Self = this->teb->value(); + teb_obj.ProcessEnvironmentBlock = context.peb.value(); }); } diff --git a/src/windows-emulator/emulator_utils.hpp b/src/windows-emulator/emulator_utils.hpp index e32c803c..ef7b9459 100644 --- a/src/windows-emulator/emulator_utils.hpp +++ b/src/windows-emulator/emulator_utils.hpp @@ -95,11 +95,6 @@ class emulator_object return this->value() + this->size(); } - T* ptr() const - { - return reinterpret_cast(this->address_); - } - explicit operator bool() const { return this->address_ != 0; @@ -221,11 +216,11 @@ class emulator_allocator return emulator_object(*this->memory_, potential_start); } - char16_t* copy_string(const std::u16string_view str) + uint64_t copy_string(const std::u16string_view str) { UNICODE_STRING> uc_str{}; this->make_unicode_string(uc_str, str); - return reinterpret_cast(uc_str.Buffer); + return uc_str.Buffer; } void make_unicode_string(UNICODE_STRING>& result, const std::u16string_view str, diff --git a/src/windows-emulator/exception_dispatch.cpp b/src/windows-emulator/exception_dispatch.cpp index 3eae5182..b3bd0b3f 100644 --- a/src/windows-emulator/exception_dispatch.cpp +++ b/src/windows-emulator/exception_dispatch.cpp @@ -33,7 +33,7 @@ namespace } record_obj.access([&](exception_record& r) { - r.ExceptionRecord = reinterpret_cast::PVOID>(nested_record_obj.ptr()); + r.ExceptionRecord = nested_record_obj.value(); // }); } diff --git a/src/windows-emulator/process_context.cpp b/src/windows-emulator/process_context.cpp index bb720ec4..b8d22677 100644 --- a/src/windows-emulator/process_context.cpp +++ b/src/windows-emulator/process_context.cpp @@ -65,7 +65,7 @@ void process_context::setup(x64_emulator& emu, memory_manager& memory, const app proc_params.StandardInput = STDIN_HANDLE.h; proc_params.StandardError = proc_params.StandardOutput; - proc_params.Environment = reinterpret_cast(allocator.copy_string(u"=::=::\\")); + proc_params.Environment = allocator.copy_string(u"=::=::\\"); allocator.copy_string(u"EMULATOR=1"); allocator.copy_string(u"COMPUTERNAME=momo"); allocator.copy_string(u"SystemRoot=C:\\WINDOWS"); @@ -95,11 +95,11 @@ void process_context::setup(x64_emulator& emu, memory_manager& memory, const app this->peb.access([&](PEB64& p) { p.BeingDebugged = 0; p.ImageBaseAddress = executable.image_base; - p.ProcessParameters = this->process_params.ptr(); - p.ApiSetMap = apiset::clone(emu, allocator, apiset_container).ptr(); + p.ProcessParameters = this->process_params.value(); + p.ApiSetMap = apiset::clone(emu, allocator, apiset_container).value(); - p.ProcessHeap = nullptr; - p.ProcessHeaps = nullptr; + p.ProcessHeap = 0; + p.ProcessHeaps = 0; p.HeapSegmentReserve = 0x0000000000100000; // TODO: Read from executable p.HeapSegmentCommit = 0x0000000000002000; p.HeapDeCommitTotalFreeThreshold = 0x0000000000010000; @@ -235,4 +235,4 @@ handle process_context::create_thread(memory_manager& memory, const uint64_t sta auto [h, thr] = this->threads.store_and_get(std::move(t)); this->callbacks_->on_create_thread(h, *thr); return h; -} \ No newline at end of file +} diff --git a/src/windows-emulator/syscalls/process.cpp b/src/windows-emulator/syscalls/process.cpp index 7075fa34..dc648a8d 100644 --- a/src/windows-emulator/syscalls/process.cpp +++ b/src/windows-emulator/syscalls/process.cpp @@ -212,7 +212,7 @@ namespace syscalls const emulator_object info{c.emu, process_information}; info.access([&](PROCESS_BASIC_INFORMATION64& basic_info) { - basic_info.PebBaseAddress = c.proc.peb.ptr(); + basic_info.PebBaseAddress = c.proc.peb.value(); basic_info.UniqueProcessId = 1; }); @@ -306,11 +306,12 @@ namespace syscalls thread_iterator->second.teb->access([&](TEB64& teb) { entry.ThreadId = teb.ClientId.UniqueThread; - auto* tls_vector = teb.ThreadLocalStoragePointer; + const auto tls_vector = teb.ThreadLocalStoragePointer; + constexpr auto ptr_size = sizeof(EmulatorTraits::PVOID); if (tls_info.TlsRequest == ProcessTlsReplaceIndex) { - auto* tls_entry_ptr = tls_vector + tls_info.TlsIndex; + const auto tls_entry_ptr = tls_vector + (tls_info.TlsIndex * ptr_size); const auto old_entry = c.emu.read_memory::PVOID>(tls_entry_ptr); c.emu.write_memory::PVOID>(tls_entry_ptr, entry.TlsModulePointer); @@ -319,12 +320,12 @@ namespace syscalls } else if (tls_info.TlsRequest == ProcessTlsReplaceVector) { - auto* new_tls_vector = entry.TlsVector; + const auto new_tls_vector = entry.TlsVector; for (uint32_t index = 0; index < tls_info.TlsVectorLength; ++index) { - auto* old_entry = c.emu.read_memory(tls_vector + index); - c.emu.write_memory(new_tls_vector + index, old_entry); + const auto old_entry = c.emu.read_memory(tls_vector + index * ptr_size); + c.emu.write_memory(new_tls_vector + index * ptr_size, old_entry); } teb.ThreadLocalStoragePointer = new_tls_vector; @@ -386,4 +387,4 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } -} \ No newline at end of file +} diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 3cc470a7..827dc0e1 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -73,7 +73,7 @@ namespace syscalls t.teb->access([&](TEB64& teb) { if (tls_cell < TLS_MINIMUM_AVAILABLE) { - teb.TlsSlots.arr[tls_cell] = nullptr; + teb.TlsSlots.arr[tls_cell] = 0; } else if (teb.TlsExpansionSlots) { @@ -136,7 +136,7 @@ namespace syscalls const emulator_object info{c.emu, thread_information}; info.access([&](THREAD_BASIC_INFORMATION64& i) { - i.TebBaseAddress = thread->teb->ptr(); + i.TebBaseAddress = thread->teb->value(); i.ClientId = thread->teb->read().ClientId; }); @@ -542,7 +542,7 @@ namespace syscalls } else if (type == PsAttributeTebAddress) { - write_attribute(c.emu, attribute, thread->teb->ptr()); + write_attribute(c.emu, attribute, thread->teb->value()); } else { From f3caea91ded284b4c8e636a5d1c17a0f4dbc0516 Mon Sep 17 00:00:00 2001 From: Maurice Heumann Date: Mon, 14 Apr 2025 14:50:38 +0200 Subject: [PATCH 010/542] Fix remaining structs --- src/common/platform/kernel_mapped.hpp | 22 +++++++++++----------- src/common/platform/memory.hpp | 4 ++-- src/windows-emulator/syscalls/memory.cpp | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/common/platform/kernel_mapped.hpp b/src/common/platform/kernel_mapped.hpp index c173f574..931c5d35 100644 --- a/src/common/platform/kernel_mapped.hpp +++ b/src/common/platform/kernel_mapped.hpp @@ -51,8 +51,8 @@ union PEB_BITFIELD_UNION typedef struct _LIST_ENTRY64 { - struct _LIST_ENTRY* Flink; - struct _LIST_ENTRY* Blink; + ULONGLONG Flink; + ULONGLONG Blink; } LIST_ENTRY64, *PLIST_ENTRY64, *RESTRICTED_POINTER PRLIST_ENTRY64; #endif @@ -187,9 +187,9 @@ typedef struct _API_SET_NAMESPACE union PEB_CONTEXT_DATA_UNION64 { - void* pContextData; // WIN7 - void* pUnused; // WIN10 - void* EcCodeBitMap; // WIN11 + std::uint64_t pContextData; // WIN7 + std::uint64_t pUnused; // WIN10 + std::uint64_t EcCodeBitMap; // WIN11 }; union PEB_TRACING_FLAGS_UNION @@ -228,18 +228,18 @@ typedef struct _CPTABLEINFO USHORT TransUniDefaultChar; USHORT DBCSCodePage; UCHAR LeadByte[MAXIMUM_LEADBYTES]; - USHORT* MultiByteTable; - void* WideCharTable; - USHORT* DBCSRanges; - USHORT* DBCSOffsets; + EMULATOR_CAST(uint64_t, USHORT*) MultiByteTable; + EMULATOR_CAST(uint64_t, void*) WideCharTable; + EMULATOR_CAST(uint64_t, USHORT*) DBCSRanges; + EMULATOR_CAST(uint64_t, USHORT*) DBCSOffsets; } CPTABLEINFO, *PCPTABLEINFO; typedef struct _NLSTABLEINFO { CPTABLEINFO OemTableInfo; CPTABLEINFO AnsiTableInfo; - USHORT* UpperCaseTable; - USHORT* LowerCaseTable; + EMULATOR_CAST(uint64_t, USHORT*) UpperCaseTable; + EMULATOR_CAST(uint64_t, USHORT*) LowerCaseTable; } NLSTABLEINFO, *PNLSTABLEINFO; typedef struct _PEB64 diff --git a/src/common/platform/memory.hpp b/src/common/platform/memory.hpp index 44e37103..8f6023cd 100644 --- a/src/common/platform/memory.hpp +++ b/src/common/platform/memory.hpp @@ -78,7 +78,7 @@ typedef struct DECLSPEC_ALIGN(16) _EMU_MEMORY_BASIC_INFORMATION64 typedef struct _MEMORY_IMAGE_INFORMATION64 { - void* ImageBase; + uint64_t ImageBase; std::int64_t SizeOfImage; union @@ -98,7 +98,7 @@ typedef struct _MEMORY_IMAGE_INFORMATION64 typedef struct _MEMORY_REGION_INFORMATION { - void* AllocationBase; + uint64_t AllocationBase; ULONG AllocationProtect; union diff --git a/src/windows-emulator/syscalls/memory.cpp b/src/windows-emulator/syscalls/memory.cpp index 26d3cb98..768f6791 100644 --- a/src/windows-emulator/syscalls/memory.cpp +++ b/src/windows-emulator/syscalls/memory.cpp @@ -76,7 +76,7 @@ namespace syscalls const emulator_object info{c.emu, memory_information}; info.access([&](MEMORY_IMAGE_INFORMATION64& image_info) { - image_info.ImageBase = reinterpret_cast(mod->image_base); + image_info.ImageBase = mod->image_base; image_info.SizeOfImage = static_cast(mod->size_of_image); image_info.ImageFlags = 0; }); @@ -107,7 +107,7 @@ namespace syscalls info.access([&](MEMORY_REGION_INFORMATION64& image_info) { memset(&image_info, 0, sizeof(image_info)); - image_info.AllocationBase = reinterpret_cast(region_info.allocation_base); + image_info.AllocationBase = region_info.allocation_base; image_info.AllocationProtect = map_emulator_to_nt_protection(region_info.initial_permissions); // image_info.PartitionId = 0; image_info.RegionSize = static_cast(region_info.allocation_length); From 91c7ce43a11dfbae173bac8b7dba7ba53897b851 Mon Sep 17 00:00:00 2001 From: Maurice Heumann Date: Mon, 14 Apr 2025 15:03:16 +0200 Subject: [PATCH 011/542] Fix reading unicode strings --- src/windows-emulator/emulator_utils.hpp | 2 +- src/windows-emulator/syscalls/event.cpp | 8 +++----- src/windows-emulator/syscalls/file.cpp | 9 +++------ src/windows-emulator/syscalls/registry.cpp | 5 ++--- src/windows-emulator/syscalls/section.cpp | 6 ++---- src/windows-emulator/syscalls/semaphore.cpp | 5 ++--- 6 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/windows-emulator/emulator_utils.hpp b/src/windows-emulator/emulator_utils.hpp index ef7b9459..ba0932be 100644 --- a/src/windows-emulator/emulator_utils.hpp +++ b/src/windows-emulator/emulator_utils.hpp @@ -352,7 +352,7 @@ inline std::u16string read_unicode_string(const emulator& emu, return read_unicode_string(emu, ucs); } -inline std::u16string read_unicode_string(emulator& emu, const UNICODE_STRING>* uc_string) +inline std::u16string read_unicode_string(emulator& emu, const uint64_t uc_string) { return read_unicode_string(emu, emulator_object>>{emu, uc_string}); } diff --git a/src/windows-emulator/syscalls/event.cpp b/src/windows-emulator/syscalls/event.cpp index 64aa329a..2f1f90fd 100644 --- a/src/windows-emulator/syscalls/event.cpp +++ b/src/windows-emulator/syscalls/event.cpp @@ -62,8 +62,7 @@ namespace syscalls const auto attributes = object_attributes.read(); if (attributes.ObjectName) { - name = read_unicode_string( - c.emu, reinterpret_cast>*>(attributes.ObjectName)); + name = read_unicode_string(c.emu, attributes.ObjectName); } } @@ -99,8 +98,7 @@ namespace syscalls const emulator_object>> object_attributes) { const auto attributes = object_attributes.read(); - const auto name = - read_unicode_string(c.emu, reinterpret_cast>*>(attributes.ObjectName)); + const auto name = read_unicode_string(c.emu, attributes.ObjectName); c.win_emu.log.print(color::dark_gray, "--> Event name: %s\n", u16_to_u8(name).c_str()); if (name == u"\\KernelObjects\\SystemErrorPortReady") @@ -133,4 +131,4 @@ namespace syscalls return STATUS_NOT_FOUND; } -} \ No newline at end of file +} diff --git a/src/windows-emulator/syscalls/file.cpp b/src/windows-emulator/syscalls/file.cpp index 8b291a22..006e3ba2 100644 --- a/src/windows-emulator/syscalls/file.cpp +++ b/src/windows-emulator/syscalls/file.cpp @@ -552,8 +552,7 @@ namespace syscalls uint64_t ea_buffer, ULONG ea_length) { const auto attributes = object_attributes.read(); - auto filename = - read_unicode_string(c.emu, reinterpret_cast>*>(attributes.ObjectName)); + auto filename = read_unicode_string(c.emu, attributes.ObjectName); auto printer = utils::finally([&] { c.win_emu.log.print(color::dark_gray, "--> Opening file: %s\n", u16_to_u8(filename).c_str()); // @@ -757,8 +756,7 @@ namespace syscalls const emulator_object>> object_attributes) { const auto attributes = object_attributes.read(); - const auto object_name = - read_unicode_string(c.emu, reinterpret_cast>*>(attributes.ObjectName)); + const auto object_name = read_unicode_string(c.emu, attributes.ObjectName); if (object_name == u"\\KnownDlls") { @@ -780,8 +778,7 @@ namespace syscalls const emulator_object>> object_attributes) { const auto attributes = object_attributes.read(); - const auto object_name = - read_unicode_string(c.emu, reinterpret_cast>*>(attributes.ObjectName)); + const auto object_name = read_unicode_string(c.emu, attributes.ObjectName); if (object_name == u"KnownDllPath") { diff --git a/src/windows-emulator/syscalls/registry.cpp b/src/windows-emulator/syscalls/registry.cpp index 2dcaf779..d383335a 100644 --- a/src/windows-emulator/syscalls/registry.cpp +++ b/src/windows-emulator/syscalls/registry.cpp @@ -11,8 +11,7 @@ namespace syscalls const emulator_object>> object_attributes) { const auto attributes = object_attributes.read(); - auto key = - read_unicode_string(c.emu, reinterpret_cast>*>(attributes.ObjectName)); + auto key = read_unicode_string(c.emu, attributes.ObjectName); if (attributes.RootDirectory) { @@ -247,4 +246,4 @@ namespace syscalls { return STATUS_NOT_SUPPORTED; } -} \ No newline at end of file +} diff --git a/src/windows-emulator/syscalls/section.cpp b/src/windows-emulator/syscalls/section.cpp index fab75901..3bcaaa90 100644 --- a/src/windows-emulator/syscalls/section.cpp +++ b/src/windows-emulator/syscalls/section.cpp @@ -29,8 +29,7 @@ namespace syscalls const auto attributes = object_attributes.read(); if (attributes.ObjectName) { - auto name = read_unicode_string( - c.emu, reinterpret_cast>*>(attributes.ObjectName)); + auto name = read_unicode_string(c.emu, attributes.ObjectName); c.win_emu.log.print(color::dark_gray, "--> Section with name %s\n", u16_to_u8(name).c_str()); s.name = std::move(name); } @@ -60,8 +59,7 @@ namespace syscalls { const auto attributes = object_attributes.read(); - auto filename = - read_unicode_string(c.emu, reinterpret_cast>*>(attributes.ObjectName)); + auto filename = read_unicode_string(c.emu, attributes.ObjectName); c.win_emu.log.print(color::dark_gray, "--> Opening section: %s\n", u16_to_u8(filename).c_str()); if (filename == u"\\Windows\\SharedSection") diff --git a/src/windows-emulator/syscalls/semaphore.cpp b/src/windows-emulator/syscalls/semaphore.cpp index 6b482a17..8e27e607 100644 --- a/src/windows-emulator/syscalls/semaphore.cpp +++ b/src/windows-emulator/syscalls/semaphore.cpp @@ -78,8 +78,7 @@ namespace syscalls const auto attributes = object_attributes.read(); if (attributes.ObjectName) { - s.name = read_unicode_string( - c.emu, reinterpret_cast>*>(attributes.ObjectName)); + s.name = read_unicode_string(c.emu, attributes.ObjectName); } } @@ -101,4 +100,4 @@ namespace syscalls return STATUS_SUCCESS; } -} \ No newline at end of file +} From b62d4cfc4c35bd1b1fb9f2d50a84269222786231 Mon Sep 17 00:00:00 2001 From: Maurice Heumann Date: Mon, 14 Apr 2025 15:15:59 +0200 Subject: [PATCH 012/542] Fix unicorn for 32 bit --- src/backends/unicorn-emulator/unicorn_x64_emulator.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp index b4b3ce47..955fca63 100644 --- a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp +++ b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp @@ -207,10 +207,8 @@ namespace unicorn #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wconversion" #endif - if constexpr (sizeof(void*) >= 8) - { - uce(uc_ctl_set_tcg_buffer_size(this->uc_, 2 << 30 /* 2 gb */)); - } + constexpr auto is_64_bit = sizeof(void*) >= 8; + uce(uc_ctl_set_tcg_buffer_size(this->uc_, (is_64_bit ? 2 : 1) << 30 /* 2 gb */)); #ifndef OS_WINDOWS #pragma GCC diagnostic pop From d206f9fdbf5812d8f2acdda417a5ebc8a07a11cf Mon Sep 17 00:00:00 2001 From: Maurice Heumann Date: Mon, 14 Apr 2025 15:22:59 +0200 Subject: [PATCH 013/542] Build and test win-x86 --- .github/workflows/build.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 97a6d831..5f06e03d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -151,6 +151,7 @@ jobs: fail-fast: false matrix: platform: + - Windows x86 - Windows x86_64 - Linux x86_64 GCC - Linux x86_64 GCC Sanitizer @@ -168,8 +169,12 @@ jobs: preset: debug - configuration: Release preset: release + - platform: Windows x86 + runner: windows-latest + devcmd_arch: x86 - platform: Windows x86_64 runner: windows-latest + devcmd_arch: x64 - platform: Linux x86_64 GCC Sanitizer runner: ubuntu-24.04 cmake-options: "-DMOMO_ENABLE_SANITIZER=On" @@ -224,6 +229,8 @@ jobs: - name: Enable Developer Command Prompt uses: ilammy/msvc-dev-cmd@v1.13.0 if: ${{ startsWith(matrix.platform, 'Windows') }} + with: + arch: ${{ matrix.devcmd_arch }} - uses: nttld/setup-ndk@v1 id: setup-ndk @@ -262,6 +269,7 @@ jobs: fail-fast: false matrix: platform: + - Windows x86 - Windows x86_64 - Linux x86_64 GCC - Linux x86_64 GCC Sanitizer @@ -283,6 +291,8 @@ jobs: preset: debug - configuration: Release preset: release + - platform: Windows x86 + runner: windows-latest - platform: Windows x86_64 runner: windows-latest - platform: Linux x86_64 GCC @@ -336,6 +346,7 @@ jobs: run: cp build/${{matrix.preset}}/artifacts/test-sample.exe build/${{matrix.preset}}/artifacts/root/filesys/c/ - name: CMake Test + if: ${{ matrix.emulator != 'Icicle' || matrix.platform != 'Windows x86' }} run: cd build/${{matrix.preset}} && ctest --verbose -j env: EMULATOR_ROOT: ${{github.workspace}}/build/${{matrix.preset}}/artifacts/root From 88d94f7065bade4eae7f11a23b2bedd7c2a5e115 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 14 Apr 2025 18:34:34 +0200 Subject: [PATCH 014/542] Fix hook sizes --- src/backends/icicle-emulator/icicle_x64_emulator.cpp | 4 ++-- src/backends/unicorn-emulator/unicorn_x64_emulator.cpp | 10 +++++----- src/emulator/hook_interface.hpp | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/backends/icicle-emulator/icicle_x64_emulator.cpp b/src/backends/icicle-emulator/icicle_x64_emulator.cpp index 06622586..d6296d76 100644 --- a/src/backends/icicle-emulator/icicle_x64_emulator.cpp +++ b/src/backends/icicle-emulator/icicle_x64_emulator.cpp @@ -326,7 +326,7 @@ namespace icicle return wrap_hook(id); } - emulator_hook* hook_memory_read(const uint64_t address, const size_t size, + emulator_hook* hook_memory_read(const uint64_t address, const uint64_t size, memory_access_hook_callback callback) override { auto obj = make_function_object(std::move(callback)); @@ -342,7 +342,7 @@ namespace icicle return wrap_hook(id); } - emulator_hook* hook_memory_write(const uint64_t address, const size_t size, + emulator_hook* hook_memory_write(const uint64_t address, const uint64_t size, memory_access_hook_callback callback) override { auto obj = make_function_object(std::move(callback)); diff --git a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp index 955fca63..6c449b13 100644 --- a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp +++ b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp @@ -556,16 +556,16 @@ namespace unicorn return this->hook_memory_execution(address, 1, std::move(callback)); } - emulator_hook* hook_memory_read(const uint64_t address, const size_t size, + emulator_hook* hook_memory_read(const uint64_t address, const uint64_t size, memory_access_hook_callback callback) override { auto read_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type, - const uint64_t address, const int size, + const uint64_t address, const int length, const uint64_t value) { const auto operation = map_memory_operation(type); - if (operation == memory_operation::read && size > 0) + if (operation == memory_operation::read && length > 0) { - c(address, &value, std::min(static_cast(size), sizeof(value))); + c(address, &value, std::min(static_cast(length), sizeof(value))); } }; @@ -581,7 +581,7 @@ namespace unicorn return container->as_opaque_hook(); } - emulator_hook* hook_memory_write(const uint64_t address, const size_t size, + emulator_hook* hook_memory_write(const uint64_t address, const uint64_t size, memory_access_hook_callback callback) override { auto write_wrapper = [c = std::move(callback)](uc_engine*, const uc_mem_type type, const uint64_t addr, diff --git a/src/emulator/hook_interface.hpp b/src/emulator/hook_interface.hpp index 96ae6195..3d268bbb 100644 --- a/src/emulator/hook_interface.hpp +++ b/src/emulator/hook_interface.hpp @@ -55,8 +55,8 @@ class hook_interface virtual emulator_hook* hook_memory_execution(memory_execution_hook_callback callback) = 0; virtual emulator_hook* hook_memory_execution(uint64_t address, memory_execution_hook_callback callback) = 0; - virtual emulator_hook* hook_memory_read(uint64_t address, size_t size, memory_access_hook_callback callback) = 0; - virtual emulator_hook* hook_memory_write(uint64_t address, size_t size, memory_access_hook_callback callback) = 0; + virtual emulator_hook* hook_memory_read(uint64_t address, uint64_t size, memory_access_hook_callback callback) = 0; + virtual emulator_hook* hook_memory_write(uint64_t address, uint64_t size, memory_access_hook_callback callback) = 0; virtual emulator_hook* hook_instruction(int instruction_type, instruction_hook_callback callback) = 0; From d9fc5457bc436394551a819a6742141e0af0e3de Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 14 Apr 2025 19:47:48 +0200 Subject: [PATCH 015/542] Emscripten tests --- cmake/toolchain/emscripten.cmake | 40 ++ .../icicle-emulator/icicle-bridge/Cargo.lock | 420 +----------------- .../icicle-emulator/icicle-bridge/Cargo.toml | 6 +- 3 files changed, 56 insertions(+), 410 deletions(-) create mode 100644 cmake/toolchain/emscripten.cmake diff --git a/cmake/toolchain/emscripten.cmake b/cmake/toolchain/emscripten.cmake new file mode 100644 index 00000000..761a920a --- /dev/null +++ b/cmake/toolchain/emscripten.cmake @@ -0,0 +1,40 @@ +set(CMAKE_SYSTEM_NAME Emscripten) +set(CMAKE_SYSTEM_VERSION 1) + +# Specify the cross-compilers +set(CMAKE_C_COMPILER emcc) +set(CMAKE_CXX_COMPILER em++) + +# Set the Emscripten root directory +set(EMSCRIPTEN_ROOT_PATH $ENV{EMSDK}/upstream/emscripten) + +# Set the Emscripten toolchain file +set(CMAKE_SYSROOT ${EMSCRIPTEN_ROOT_PATH}/system) + +# Set the Emscripten include directories +set(CMAKE_FIND_ROOT_PATH ${EMSCRIPTEN_ROOT_PATH}/system/include) + +# Set the Emscripten library directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# Set the Emscripten linker +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_SDL=2 -s USE_SDL_MIXER=2") + +# Set the Emscripten runtime +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --shell-file shell_minimal.html") + +# Set the Emscripten optimization flags +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") + +# Set the Emscripten debug flags +set(CMAKE_BUILD_TYPE Release) + +# Set the Emscripten output format +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -o .html") + +# Set the Emscripten file extensions +set(CMAKE_EXECUTABLE_SUFFIX ".js") + +# Set the Emscripten runtime options +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORTED_FUNCTIONS='[_main]' -s EXPORTED_RUNTIME_METHODS='[\"cwrap\"]'") \ No newline at end of file diff --git a/src/backends/icicle-emulator/icicle-bridge/Cargo.lock b/src/backends/icicle-emulator/icicle-bridge/Cargo.lock index 0cd2e74e..b507e56c 100644 --- a/src/backends/icicle-emulator/icicle-bridge/Cargo.lock +++ b/src/backends/icicle-emulator/icicle-bridge/Cargo.lock @@ -43,30 +43,12 @@ version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" -[[package]] -name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.9.0" @@ -86,12 +68,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bumpalo" -version = "3.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" - [[package]] name = "bytemuck" version = "1.22.0" @@ -113,148 +89,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "cranelift" -version = "0.113.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27711210499725bafe52c320a988e27283e6cf477ee8edac57e8275bef8ea550" -dependencies = [ - "cranelift-codegen", - "cranelift-frontend", - "cranelift-module", -] - -[[package]] -name = "cranelift-bforest" -version = "0.113.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "540b193ff98b825a1f250a75b3118911af918a734154c69d80bcfcf91e7e9522" -dependencies = [ - "cranelift-entity", -] - -[[package]] -name = "cranelift-bitset" -version = "0.113.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7cb269598b9557ab942d687d3c1086d77c4b50dcf35813f3a65ba306fd42279" - -[[package]] -name = "cranelift-codegen" -version = "0.113.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46566d7c83a8bff4150748d66020f4c7224091952aa4b4df1ec4959c39d937a1" -dependencies = [ - "bumpalo", - "cranelift-bforest", - "cranelift-bitset", - "cranelift-codegen-meta", - "cranelift-codegen-shared", - "cranelift-control", - "cranelift-entity", - "cranelift-isle", - "gimli", - "hashbrown 0.14.5", - "log", - "regalloc2", - "rustc-hash", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-codegen-meta" -version = "0.113.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2df8a86a34236cc75a8a6a271973da779c2aeb36c43b6e14da474cf931317082" -dependencies = [ - "cranelift-codegen-shared", -] - -[[package]] -name = "cranelift-codegen-shared" -version = "0.113.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf75340b6a57b7c7c1b74f10d3d90883ee6d43a554be8131a4046c2ebcf5eb65" - -[[package]] -name = "cranelift-control" -version = "0.113.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e84495bc5d23d86aad8c86f8ade4af765b94882af60d60e271d3153942f1978" -dependencies = [ - "arbitrary", -] - -[[package]] -name = "cranelift-entity" -version = "0.113.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963c17147b80df351965e57c04d20dbedc85bcaf44c3436780a59a3f1ff1b1c2" -dependencies = [ - "cranelift-bitset", -] - -[[package]] -name = "cranelift-frontend" -version = "0.113.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727f02acbc4b4cb2ba38a6637101d579db50190df1dd05168c68e762851a3dd5" -dependencies = [ - "cranelift-codegen", - "log", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-isle" -version = "0.113.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b00cc2e03c748f2531eea01c871f502b909d30295fdcad43aec7bf5c5b4667" - -[[package]] -name = "cranelift-jit" -version = "0.113.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f74630af581f32b99c8f4e06ee45799383ecc0795e3ff8aa86b7584bb2d643fd" -dependencies = [ - "anyhow", - "cranelift-codegen", - "cranelift-control", - "cranelift-entity", - "cranelift-module", - "cranelift-native", - "libc", - "log", - "region", - "target-lexicon", - "wasmtime-jit-icache-coherence", - "windows-sys 0.59.0", -] - -[[package]] -name = "cranelift-module" -version = "0.113.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aaa16c4f18a15be310df221ea544f516acc42fc58ca96e09a3d08651744efa1" -dependencies = [ - "anyhow", - "cranelift-codegen", - "cranelift-control", -] - -[[package]] -name = "cranelift-native" -version = "0.113.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbeaf978dc7c1a2de8bbb9162510ed218eb156697bc45590b8fbdd69bb08e8de" -dependencies = [ - "cranelift-codegen", - "libc", - "target-lexicon", -] - [[package]] name = "crc32fast" version = "1.4.2" @@ -276,16 +110,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "errno" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "fallible-iterator" version = "0.3.0" @@ -340,15 +164,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.15.2" @@ -370,12 +185,12 @@ dependencies = [ [[package]] name = "icicle-cpu" version = "0.1.0" -source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a" +source = "git+https://github.com/momo5502/icicle-emu#70c9ed338d931da705a4a7a8ec2110cc677d970c" dependencies = [ "addr2line", "ahash", "anyhow", - "bitflags 2.9.0", + "bitflags", "bytemuck", "gimli", "half", @@ -387,30 +202,12 @@ dependencies = [ "tracing", ] -[[package]] -name = "icicle-jit" -version = "0.2.0" -source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a" -dependencies = [ - "cranelift", - "cranelift-codegen", - "cranelift-jit", - "cranelift-module", - "cranelift-native", - "icicle-cpu", - "memoffset", - "pcode", - "target-lexicon", - "tracing", - "wasmtime-jit-debug", -] - [[package]] name = "icicle-linux" version = "0.1.0" -source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a" +source = "git+https://github.com/momo5502/icicle-emu#70c9ed338d931da705a4a7a8ec2110cc677d970c" dependencies = [ - "bitflags 2.9.0", + "bitflags", "bstr", "bytemuck", "icicle-cpu", @@ -424,7 +221,7 @@ dependencies = [ [[package]] name = "icicle-mem" version = "0.3.0" -source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a" +source = "git+https://github.com/momo5502/icicle-emu#70c9ed338d931da705a4a7a8ec2110cc677d970c" dependencies = [ "ahash", "tracing", @@ -433,11 +230,10 @@ dependencies = [ [[package]] name = "icicle-vm" version = "0.2.0" -source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a" +source = "git+https://github.com/momo5502/icicle-emu#70c9ed338d931da705a4a7a8ec2110cc677d970c" dependencies = [ "anyhow", "icicle-cpu", - "icicle-jit", "icicle-linux", "ihex", "object", @@ -464,7 +260,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown", ] [[package]] @@ -473,27 +269,12 @@ version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "log" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" -[[package]] -name = "mach2" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" -dependencies = [ - "libc", -] - [[package]] name = "memchr" version = "2.7.4" @@ -509,15 +290,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "miniz_oxide" version = "0.8.8" @@ -535,7 +307,7 @@ checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", "flate2", - "hashbrown 0.15.2", + "hashbrown", "indexmap", "memchr", "ruzstd", @@ -550,7 +322,7 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "pcode" version = "0.2.0" -source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a" +source = "git+https://github.com/momo5502/icicle-emu#70c9ed338d931da705a4a7a8ec2110cc677d970c" [[package]] name = "pin-project-lite" @@ -576,31 +348,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "regalloc2" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12908dbeb234370af84d0579b9f68258a0f67e201412dd9a2814e6f45b2fc0f0" -dependencies = [ - "hashbrown 0.14.5", - "log", - "rustc-hash", - "slice-group-by", - "smallvec", -] - -[[package]] -name = "region" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7" -dependencies = [ - "bitflags 1.3.2", - "libc", - "mach2", - "windows-sys 0.52.0", -] - [[package]] name = "ron" version = "0.8.1" @@ -608,7 +355,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64", - "bitflags 2.9.0", + "bitflags", "serde", "serde_derive", ] @@ -619,25 +366,6 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.9.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.59.0", -] - [[package]] name = "ruzstd" version = "0.7.3" @@ -682,7 +410,7 @@ dependencies = [ [[package]] name = "sleigh-compile" version = "0.3.0" -source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a" +source = "git+https://github.com/momo5502/icicle-emu#70c9ed338d931da705a4a7a8ec2110cc677d970c" dependencies = [ "pcode", "serde", @@ -695,23 +423,17 @@ dependencies = [ [[package]] name = "sleigh-parse" version = "0.3.0" -source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a" +source = "git+https://github.com/momo5502/icicle-emu#70c9ed338d931da705a4a7a8ec2110cc677d970c" [[package]] name = "sleigh-runtime" version = "0.1.0" -source = "git+https://github.com/icicle-emu/icicle-emu#fe930922ea1ec39578ee545ccced61649f3da00a" +source = "git+https://github.com/momo5502/icicle-emu#70c9ed338d931da705a4a7a8ec2110cc677d970c" dependencies = [ "pcode", "sleigh-parse", ] -[[package]] -name = "slice-group-by" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" - [[package]] name = "smallvec" version = "1.15.0" @@ -817,122 +539,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasmtime-jit-debug" -version = "26.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02a0118d471de665565ed200bc56673eaa10cc8e223dfe2cef5d50ed0d9d143" -dependencies = [ - "object", - "rustix", - "wasmtime-versioned-export-macros", -] - -[[package]] -name = "wasmtime-jit-icache-coherence" -version = "26.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da47fba49af72581bc0dc67c8faaf5ee550e6f106e285122a184a675193701a5" -dependencies = [ - "anyhow", - "cfg-if", - "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "wasmtime-versioned-export-macros" -version = "26.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8efb877c9e5e67239d4553bb44dd2a34ae5cfb728f3cf2c5e64439c6ca6ee7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "xml-rs" version = "0.8.25" diff --git a/src/backends/icicle-emulator/icicle-bridge/Cargo.toml b/src/backends/icicle-emulator/icicle-bridge/Cargo.toml index 8019c2b2..f74bf4cf 100644 --- a/src/backends/icicle-emulator/icicle-bridge/Cargo.toml +++ b/src/backends/icicle-emulator/icicle-bridge/Cargo.toml @@ -7,6 +7,6 @@ edition = "2024" crate-type = ["staticlib"] [dependencies] -icicle-vm = { git = "https://github.com/icicle-emu/icicle-emu" } -icicle-cpu = { git = "https://github.com/icicle-emu/icicle-emu" } -pcode = { git = "https://github.com/icicle-emu/icicle-emu" } +icicle-vm = { git = "https://github.com/momo5502/icicle-emu" } +icicle-cpu = { git = "https://github.com/momo5502/icicle-emu" } +pcode = { git = "https://github.com/momo5502/icicle-emu" } From 68022ef501fbb6d7087b5672cf222d5b0ed8e146 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 14 Apr 2025 20:01:00 +0200 Subject: [PATCH 016/542] Fixes --- deps/CMakeLists.txt | 7 ++++--- src/backends/CMakeLists.txt | 4 +++- src/backends/icicle-emulator/icicle-bridge/CMakeLists.txt | 4 +++- src/common/platform/compiler.hpp | 2 ++ src/windows-emulator/CMakeLists.txt | 2 ++ src/windows-emulator/windows_emulator.cpp | 8 +++++++- 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 19217d54..eaca04f9 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -1,6 +1,7 @@ -set(UNICORN_ARCH "x86" CACHE STRING "") - -add_subdirectory(unicorn) +if (NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") + set(UNICORN_ARCH "x86" CACHE STRING "") + add_subdirectory(unicorn) +endif() ########################################## diff --git a/src/backends/CMakeLists.txt b/src/backends/CMakeLists.txt index 12d192df..49734038 100644 --- a/src/backends/CMakeLists.txt +++ b/src/backends/CMakeLists.txt @@ -1,4 +1,6 @@ -add_subdirectory(unicorn-emulator) +if (NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") + add_subdirectory(unicorn-emulator) +endif() if (MOMO_ENABLE_RUST_CODE) add_subdirectory(icicle-emulator) diff --git a/src/backends/icicle-emulator/icicle-bridge/CMakeLists.txt b/src/backends/icicle-emulator/icicle-bridge/CMakeLists.txt index 73ea9f1d..d64c0d21 100644 --- a/src/backends/icicle-emulator/icicle-bridge/CMakeLists.txt +++ b/src/backends/icicle-emulator/icicle-bridge/CMakeLists.txt @@ -13,7 +13,9 @@ endif() set(CARGO_TRIPLE) set(CARGO_OPTIONS) -if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4) +if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten") + set(CARGO_TRIPLE "wasm32-unknown-emscripten") +elseif(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 4) set(CARGO_TRIPLE "i686-pc-windows-msvc") elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS") set(CARGO_TRIPLE "aarch64-apple-ios") diff --git a/src/common/platform/compiler.hpp b/src/common/platform/compiler.hpp index 5cece6a8..07fc82c2 100644 --- a/src/common/platform/compiler.hpp +++ b/src/common/platform/compiler.hpp @@ -13,6 +13,8 @@ #define OS_MAC #elif defined(__linux__) #define OS_LINUX +#elif defined(__EMSCRIPTEN__) +#define OS_EMSCRIPTEN #else #error "Unsupported platform" #endif diff --git a/src/windows-emulator/CMakeLists.txt b/src/windows-emulator/CMakeLists.txt index 16167e84..94ec8aa2 100644 --- a/src/windows-emulator/CMakeLists.txt +++ b/src/windows-emulator/CMakeLists.txt @@ -14,9 +14,11 @@ if(NOT MOMO_ENABLE_CLANG_TIDY) target_precompile_headers(windows-emulator PRIVATE std_include.hpp) endif() +if (NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") target_link_libraries(windows-emulator PRIVATE unicorn-emulator ) +endif() if (MOMO_ENABLE_RUST_CODE) target_link_libraries(windows-emulator PRIVATE diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 4cb8b45f..372d1d19 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -3,7 +3,9 @@ #include "cpu_context.hpp" +#ifndef OS_EMSCRIPTEN #include +#endif #if MOMO_ENABLE_RUST_CODE #include @@ -263,6 +265,10 @@ namespace std::unique_ptr create_default_x64_emulator() { +#ifdef OS_EMSCRIPTEN + return icicle::create_x64_emulator(); +#else + #if MOMO_ENABLE_RUST_CODE const auto* env = getenv("EMULATOR_ICICLE"); if (env && (env == "1"sv || env == "true"sv)) @@ -270,8 +276,8 @@ std::unique_ptr create_default_x64_emulator() return icicle::create_x64_emulator(); } #endif - return unicorn::create_x64_emulator(); +#endif } windows_emulator::windows_emulator(application_settings app_settings, const emulator_settings& settings, From a35745ba905399bdd9b7100557441566aec3c826 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 14 Apr 2025 20:16:30 +0200 Subject: [PATCH 017/542] Fix functions --- src/backends/icicle-emulator/icicle_x64_emulator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backends/icicle-emulator/icicle_x64_emulator.cpp b/src/backends/icicle-emulator/icicle_x64_emulator.cpp index d6296d76..46276f22 100644 --- a/src/backends/icicle-emulator/icicle_x64_emulator.cpp +++ b/src/backends/icicle-emulator/icicle_x64_emulator.cpp @@ -25,8 +25,8 @@ extern "C" int32_t icicle_unmap_memory(icicle_emulator*, uint64_t address, uint64_t length); int32_t icicle_read_memory(icicle_emulator*, uint64_t address, void* data, size_t length); int32_t icicle_write_memory(icicle_emulator*, uint64_t address, const void* data, size_t length); - int32_t icicle_save_registers(icicle_emulator*, data_accessor_func* accessor, void* accessor_data); - int32_t icicle_restore_registers(icicle_emulator*, const void* data, size_t length); + void icicle_save_registers(icicle_emulator*, data_accessor_func* accessor, void* accessor_data); + void icicle_restore_registers(icicle_emulator*, const void* data, size_t length); uint32_t icicle_add_syscall_hook(icicle_emulator*, raw_func* callback, void* data); uint32_t icicle_add_interrupt_hook(icicle_emulator*, interrupt_func* callback, void* data); uint32_t icicle_add_execution_hook(icicle_emulator*, uint64_t address, ptr_func* callback, void* data); From 1d47e06253336feee31641cfee05a616bce2409d Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 14 Apr 2025 20:25:38 +0200 Subject: [PATCH 018/542] More fixes --- cmake/compiler-env.cmake | 5 ++++- cmake/utils.cmake | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cmake/compiler-env.cmake b/cmake/compiler-env.cmake index 9238fee4..a6602ecd 100644 --- a/cmake/compiler-env.cmake +++ b/cmake/compiler-env.cmake @@ -22,7 +22,10 @@ cmake_policy(SET CMP0069 NEW) set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) + +if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten") + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) +endif() ########################################## diff --git a/cmake/utils.cmake b/cmake/utils.cmake index 4c9dc09b..9b96e990 100644 --- a/cmake/utils.cmake +++ b/cmake/utils.cmake @@ -335,7 +335,7 @@ function(momo_strip_target target) return() endif() - if(MSVC OR MOMO_ENABLE_SANITIZER OR CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "Android") + if(MSVC OR MOMO_ENABLE_SANITIZER OR CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "Android" OR CMAKE_SYSTEM_NAME MATCHES "Emscripten") return() endif() From 1959f94c39d006b02ff48d758760db3ad71997a9 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 17 Apr 2025 18:54:16 +0200 Subject: [PATCH 019/542] Support logging for emscripten --- src/windows-emulator/logger.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/windows-emulator/logger.cpp b/src/windows-emulator/logger.cpp index ea4dc2ee..72e18404 100644 --- a/src/windows-emulator/logger.cpp +++ b/src/windows-emulator/logger.cpp @@ -6,16 +6,19 @@ namespace { #ifdef _WIN32 -#define COLOR(win, posix) win +#define COLOR(win, posix, web) win using color_type = WORD; +#elif defined(__EMSCRIPTEN__) +#define COLOR(win, posix, web) web + using color_type = const char*; #else -#define COLOR(win, posix) posix +#define COLOR(win, posix, web) posix using color_type = const char*; #endif color_type get_reset_color() { - return COLOR(7, "\033[0m"); + return COLOR(7, "\033[0m", ""); } color_type get_color_type(const color c) @@ -25,23 +28,23 @@ namespace switch (c) { case black: - return COLOR(0x8, "\033[0;90m"); + return COLOR(0x8, "\033[0;90m", ""); case red: - return COLOR(0xC, "\033[0;91m"); + return COLOR(0xC, "\033[0;91m", ""); case green: - return COLOR(0xA, "\033[0;92m"); + return COLOR(0xA, "\033[0;92m", ""); case yellow: - return COLOR(0xE, "\033[0;93m"); + return COLOR(0xE, "\033[0;93m", ""); case blue: - return COLOR(0x9, "\033[0;94m"); + return COLOR(0x9, "\033[0;94m", ""); case cyan: - return COLOR(0xB, "\033[0;96m"); + return COLOR(0xB, "\033[0;96m", ""); case pink: - return COLOR(0xD, "\033[0;95m"); + return COLOR(0xD, "\033[0;95m", ""); case white: - return COLOR(0xF, "\033[0;97m"); + return COLOR(0xF, "\033[0;97m", ""); case dark_gray: - return COLOR(0x8, "\033[0;97m"); + return COLOR(0x8, "\033[0;97m", ""); case gray: default: return get_reset_color(); From cd348e009582c450ac1ba2e1ddf3402340205846 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 17 Apr 2025 18:54:46 +0200 Subject: [PATCH 020/542] Add padding --- src/common/platform/kernel_mapped.hpp | 1 + src/common/platform/unicode.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/common/platform/kernel_mapped.hpp b/src/common/platform/kernel_mapped.hpp index 931c5d35..221545fb 100644 --- a/src/common/platform/kernel_mapped.hpp +++ b/src/common/platform/kernel_mapped.hpp @@ -883,6 +883,7 @@ typedef struct _KERNEL_USER_TIMES struct THREAD_TLS_INFO { ULONG Flags; + uint32_t _Padding; union { diff --git a/src/common/platform/unicode.hpp b/src/common/platform/unicode.hpp index f76bac36..f9cf9561 100644 --- a/src/common/platform/unicode.hpp +++ b/src/common/platform/unicode.hpp @@ -8,6 +8,7 @@ struct UNICODE_STRING { USHORT Length; USHORT MaximumLength; + uint32_t _Padding; EMULATOR_CAST(typename Traits::PVOID, char16_t*) Buffer; }; From c1553ad2ecf527c178a22e84400ae4ae71b7812a Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 17 Apr 2025 18:57:17 +0200 Subject: [PATCH 021/542] Fixes for emscripten --- .github/workflows/build.yml | 8 ++ .gitmodules | 2 +- cmake/compiler-env.cmake | 21 +++++ deps/CMakeLists.txt | 6 +- deps/unicorn | 2 +- src/backends/CMakeLists.txt | 4 +- .../unicorn-emulator/function_wrapper2.hpp | 88 +++++++++++++++++++ .../unicorn-emulator/unicorn_x64_emulator.cpp | 39 +++++--- src/windows-emulator/CMakeLists.txt | 2 - src/windows-emulator/windows_emulator.cpp | 8 +- 10 files changed, 152 insertions(+), 28 deletions(-) create mode 100644 src/backends/unicorn-emulator/function_wrapper2.hpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5f06e03d..aee2a841 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -161,6 +161,7 @@ jobs: - iOS arm64 - Android x86_64 - Android arm64-v8a + - Emscripten configuration: - Debug - Release @@ -201,6 +202,9 @@ jobs: abi: arm64-v8a rust-target: aarch64-linux-android cmake-options: "-DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/cmake/toolchain/android-ndk.cmake" + - platform: Emscripten + runner: ubuntu-24.04 + cmake-options: "-DMOMO_ENABLE_RUST_CODE=Off -DCMAKE_TOOLCHAIN_FILE=$(dirname $(which emcc))/cmake/Modules/Platform/Emscripten.cmake" steps: - name: Checkout Source uses: actions/checkout@v4 @@ -214,6 +218,10 @@ jobs: if: "${{ matrix.rust-target }}" run: rustup target add ${{ matrix.rust-target }} + - name: Install Emscripten + if: "${{ matrix.platform == 'Emscripten' }}" + uses: mymindstorm/setup-emsdk@v14 + - name: Install Clang if: "${{ matrix.platform == 'Linux x86_64 Clang' }}" run: | diff --git a/.gitmodules b/.gitmodules index 9c5a000b..ce0fdeaf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,7 +2,7 @@ path = deps/unicorn url = ../unicorn.git shallow = true - branch = dev + branch = wasm [submodule "deps/reflect"] path = deps/reflect url = https://github.com/qlibs/reflect.git diff --git a/cmake/compiler-env.cmake b/cmake/compiler-env.cmake index a6602ecd..5f610950 100644 --- a/cmake/compiler-env.cmake +++ b/cmake/compiler-env.cmake @@ -23,6 +23,8 @@ set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) set(CMAKE_POSITION_INDEPENDENT_CODE ON) +########################################## + if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten") set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) endif() @@ -49,6 +51,9 @@ if(UNIX) momo_add_c_and_cxx_compile_options( -fvisibility=hidden -ftrivial-auto-var-init=zero + #-Wbad-function-cast + #-Wcast-function-type + -Wno-int-conversion ) endif() @@ -86,6 +91,22 @@ endif() ########################################## +if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") + add_link_options( + -sALLOW_MEMORY_GROWTH=1 + -sASSERTIONS + -sWASM_BIGINT + -sENVIRONMENT=web + -sUSE_OFFSET_CONVERTER + -sEXCEPTION_CATCHING_ALLOWED=[..] + -sEXIT_RUNTIME + #-lnodefs.js -sNODERAWFS=1 + #-sASYNCIFY +) +endif() + +########################################## + if(MSVC) string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") string(REPLACE "/EHs" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index eaca04f9..5ed0c395 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -1,7 +1,5 @@ -if (NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") - set(UNICORN_ARCH "x86" CACHE STRING "") - add_subdirectory(unicorn) -endif() +set(UNICORN_ARCH "x86" CACHE STRING "") +add_subdirectory(unicorn) ########################################## diff --git a/deps/unicorn b/deps/unicorn index 73be28b6..b29e3445 160000 --- a/deps/unicorn +++ b/deps/unicorn @@ -1 +1 @@ -Subproject commit 73be28b6509d0cbf3333071aec4efbb9be1f1e59 +Subproject commit b29e3445a4694e3a1061c910906b57d7d2a05ed9 diff --git a/src/backends/CMakeLists.txt b/src/backends/CMakeLists.txt index 49734038..12d192df 100644 --- a/src/backends/CMakeLists.txt +++ b/src/backends/CMakeLists.txt @@ -1,6 +1,4 @@ -if (NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") - add_subdirectory(unicorn-emulator) -endif() +add_subdirectory(unicorn-emulator) if (MOMO_ENABLE_RUST_CODE) add_subdirectory(icicle-emulator) diff --git a/src/backends/unicorn-emulator/function_wrapper2.hpp b/src/backends/unicorn-emulator/function_wrapper2.hpp new file mode 100644 index 00000000..f9628bda --- /dev/null +++ b/src/backends/unicorn-emulator/function_wrapper2.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include +#include + +#include + +uint32_t resolve_indexed_argument_part(uint32_t* args, size_t& index) +{ + return args[index++]; +} + +template +T resolve_indexed_argument_internal(uint32_t* args, size_t& index) +{ + const auto a1 = resolve_indexed_argument_part(args, index); + + if(sizeof(T) <= sizeof(a1)) { + return (T)a1; + } + + const auto a2 = resolve_indexed_argument_part(args, index); + + const auto arg = (a1 | ((uint64_t)a2 << 32)); + return (T)arg; +} + +template +T resolve_indexed_argument(uint32_t* args, size_t& index) +{ + auto arg = resolve_indexed_argument_internal(args, index); + return arg; +} + +template +class function_wrapper2 : public utils::object +{ + public: + using user_data_pointer = void*; + using c_function_type = ReturnType(Args..., user_data_pointer); + using functor_type = std::function; + + function_wrapper2() = default; + + function_wrapper2(functor_type functor) + : functor_(std::make_unique(std::move(functor))) + { + } + + c_function_type* get_c_function() const + { + return (c_function_type*)(void*)+[](uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4,uint32_t a5,uint32_t a6,uint32_t a7,uint32_t a8,uint32_t a9,uint32_t a10,uint32_t a11,uint32_t a12)-> uint64_t { + + uint32_t real_args[] { + a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12 + }; + + const auto lambda = +[](Args... args, user_data_pointer user_data) -> ReturnType { + return (*static_cast(user_data))(std::forward(args)...); + }; + + size_t index = 0; + std::tuple func_args{resolve_indexed_argument>>(real_args, index)..., resolve_indexed_argument(real_args, index)}; + + (void)index; + + if constexpr(!std::is_void_v){ + return (uint64_t)std::apply(lambda, std::move(func_args)); + } + + std::apply(lambda, std::move(func_args)); + return 0; + }; + } + + void* get_function() const + { + return reinterpret_cast(this->get_c_function()); + } + + user_data_pointer get_user_data() const + { + return this->functor_.get(); + } + + private: + std::unique_ptr functor_{}; +}; diff --git a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp index 6c449b13..97cd07c3 100644 --- a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp +++ b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp @@ -7,6 +7,7 @@ #include "unicorn_hook.hpp" #include "function_wrapper.hpp" +#include "function_wrapper2.hpp" #include namespace unicorn @@ -385,10 +386,7 @@ namespace unicorn emulator_hook* hook_instruction(const int instruction_type, instruction_hook_callback callback) override { - function_wrapper wrapper([c = std::move(callback)](uc_engine*) { - return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0; - }); - + unicorn_hook hook{*this}; auto container = std::make_unique(); @@ -396,18 +394,39 @@ namespace unicorn if (inst_type == x64_hookable_instructions::invalid) { + function_wrapper wrapper([c = std::move(callback)](uc_engine*) { + return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0; + }); + uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN_INVALID, wrapper.get_function(), wrapper.get_user_data(), 0, std::numeric_limits::max())); - } - else - { + container->add(std::move(wrapper), std::move(hook)); + } + else if(inst_type == x64_hookable_instructions::syscall){ + function_wrapper wrapper([c = std::move(callback)](uc_engine*) { + c(); + }); + const auto uc_instruction = map_hookable_instruction(inst_type); uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(), wrapper.get_user_data(), 0, std::numeric_limits::max(), uc_instruction)); - } - container->add(std::move(wrapper), std::move(hook)); + container->add(std::move(wrapper), std::move(hook)); + } + else + { + function_wrapper wrapper([c = std::move(callback)](uc_engine*) { + return (c() == instruction_hook_continuation::skip_instruction) ? 1 : 0; + }); + + const auto uc_instruction = map_hookable_instruction(inst_type); + uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(), + wrapper.get_user_data(), 0, std::numeric_limits::max(), + uc_instruction)); + + container->add(std::move(wrapper), std::move(hook)); + } auto* result = container->as_opaque_hook(); @@ -533,7 +552,7 @@ namespace unicorn c(address); // }; - function_wrapper wrapper(std::move(exec_wrapper)); + function_wrapper2 wrapper(std::move(exec_wrapper)); unicorn_hook hook{*this}; diff --git a/src/windows-emulator/CMakeLists.txt b/src/windows-emulator/CMakeLists.txt index 94ec8aa2..16167e84 100644 --- a/src/windows-emulator/CMakeLists.txt +++ b/src/windows-emulator/CMakeLists.txt @@ -14,11 +14,9 @@ if(NOT MOMO_ENABLE_CLANG_TIDY) target_precompile_headers(windows-emulator PRIVATE std_include.hpp) endif() -if (NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten") target_link_libraries(windows-emulator PRIVATE unicorn-emulator ) -endif() if (MOMO_ENABLE_RUST_CODE) target_link_libraries(windows-emulator PRIVATE diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 372d1d19..4cb8b45f 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -3,9 +3,7 @@ #include "cpu_context.hpp" -#ifndef OS_EMSCRIPTEN #include -#endif #if MOMO_ENABLE_RUST_CODE #include @@ -265,10 +263,6 @@ namespace std::unique_ptr create_default_x64_emulator() { -#ifdef OS_EMSCRIPTEN - return icicle::create_x64_emulator(); -#else - #if MOMO_ENABLE_RUST_CODE const auto* env = getenv("EMULATOR_ICICLE"); if (env && (env == "1"sv || env == "true"sv)) @@ -276,8 +270,8 @@ std::unique_ptr create_default_x64_emulator() return icicle::create_x64_emulator(); } #endif + return unicorn::create_x64_emulator(); -#endif } windows_emulator::windows_emulator(application_settings app_settings, const emulator_settings& settings, From 7d7336e8d5b1015f1fc3ccc3bc49a0acf8a89a29 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 17 Apr 2025 19:52:18 +0200 Subject: [PATCH 022/542] Deploy page --- .github/workflows/build.yml | 26 +++ page/bigboiii.webp | Bin 0 -> 51370 bytes page/emulator.js | 35 +++ page/index.html | 431 ++++++++++++++++++++++++++++++++++++ page/noise.webp | Bin 0 -> 37576 bytes page/why-eclipse.svg | 20 ++ page/why-eclipse2.svg | 20 ++ 7 files changed, 532 insertions(+) create mode 100644 page/bigboiii.webp create mode 100644 page/emulator.js create mode 100644 page/index.html create mode 100644 page/noise.webp create mode 100644 page/why-eclipse.svg create mode 100644 page/why-eclipse2.svg diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aee2a841..0c2bc13f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -481,6 +481,32 @@ jobs: arch: ${{matrix.architecture}} script: "adb push build/${{matrix.preset}}/artifacts/* /data/local/tmp && adb shell \"cd /data/local/tmp && export LD_LIBRARY_PATH=. && chmod +x ./analyzer && EMULATOR_ICICLE=${{ matrix.emulator == 'Icicle' }} ./analyzer -e ./root c:/test-sample.exe\"" + page: + name: Page + runs-on: ubuntu-latest + #needs: [build] + permissions: + contents: read + pages: write + id-token: write + steps: + - name: Checkout Source + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./page + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + summary: name: Pipeline Summary runs-on: ubuntu-24.04 diff --git a/page/bigboiii.webp b/page/bigboiii.webp new file mode 100644 index 0000000000000000000000000000000000000000..15089347cf42833e160b7a42568e7b188d8ed2b6 GIT binary patch literal 51370 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 Y1B#d!=mnm1VcPX!{fi6nzIVA901n?YPyhe` literal 0 HcmV?d00001 diff --git a/page/emulator.js b/page/emulator.js new file mode 100644 index 00000000..7a007d5e --- /dev/null +++ b/page/emulator.js @@ -0,0 +1,35 @@ +onmessage = async (event) => { + const data = event.data; + if (data.message == "run") { + runEmulation(data.data); + } +}; + +function logLine(text) { + postMessage(text); +} + +function runEmulation(filesystem) { + globalThis.Module = { + arguments: ["-b", "-c", "-e", "./root", "c:/lul.exe",], + onRuntimeInitialized: function () { + filesystem.forEach(e => { + if (e.name.endsWith("/")) { + FS.mkdir(e.name.slice(0, -1)); + } else { + const dirs = e.name.split("/") + const file = dirs.pop(); + const buffer = new Uint8Array(e.data); + FS.createDataFile("/" + dirs.join('/'), file, buffer, true, true); + } + }) + }, + print: logLine, + printErr: logLine, + postRun: () => { + self.close(); + }, + }; + + importScripts('./analyzer.js?1'); +} diff --git a/page/index.html b/page/index.html new file mode 100644 index 00000000..0256f824 --- /dev/null +++ b/page/index.html @@ -0,0 +1,431 @@ + + + + + + + Windows User Space Emulator + + + + + + +
+ + + +
+
+
+
+
+
+
+
+ Windows User Space Emulator +
+
+
+
+
+ +
+ + + \ No newline at end of file diff --git a/page/noise.webp b/page/noise.webp new file mode 100644 index 0000000000000000000000000000000000000000..50fa598461e833741b635f25457513460578983d GIT binary patch literal 37576 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 literal 0 HcmV?d00001 diff --git a/page/why-eclipse.svg b/page/why-eclipse.svg new file mode 100644 index 00000000..e12b0893 --- /dev/null +++ b/page/why-eclipse.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/page/why-eclipse2.svg b/page/why-eclipse2.svg new file mode 100644 index 00000000..99d0621b --- /dev/null +++ b/page/why-eclipse2.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + From 4e836cef49a5b5bfaef0e5817f6e9973324813bb Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 17 Apr 2025 20:05:39 +0200 Subject: [PATCH 023/542] Maybe fix unicorn --- deps/unicorn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/unicorn b/deps/unicorn index b29e3445..6011de1c 160000 --- a/deps/unicorn +++ b/deps/unicorn @@ -1 +1 @@ -Subproject commit b29e3445a4694e3a1061c910906b57d7d2a05ed9 +Subproject commit 6011de1c1e31a4e4a10f4fe82a4ac063b71b55ed From 00353e9a162763b5644cab0115bd82c177d1253e Mon Sep 17 00:00:00 2001 From: momo5502 Date: Thu, 17 Apr 2025 20:31:33 +0200 Subject: [PATCH 024/542] Fix argument adapters --- deps/unicorn | 2 +- .../unicorn-emulator/function_wrapper2.hpp | 53 +++++++++---------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/deps/unicorn b/deps/unicorn index 6011de1c..6f2156df 160000 --- a/deps/unicorn +++ b/deps/unicorn @@ -1 +1 @@ -Subproject commit 6011de1c1e31a4e4a10f4fe82a4ac063b71b55ed +Subproject commit 6f2156dfac3abad316a4f02bf609ec1bb700d02e diff --git a/src/backends/unicorn-emulator/function_wrapper2.hpp b/src/backends/unicorn-emulator/function_wrapper2.hpp index f9628bda..e7471580 100644 --- a/src/backends/unicorn-emulator/function_wrapper2.hpp +++ b/src/backends/unicorn-emulator/function_wrapper2.hpp @@ -5,28 +5,26 @@ #include -uint32_t resolve_indexed_argument_part(uint32_t* args, size_t& index) -{ - return args[index++]; -} - template -T resolve_indexed_argument_internal(uint32_t* args, size_t& index) +T resolve_indexed_argument_internal(size_t* args, size_t& index) { - const auto a1 = resolve_indexed_argument_part(args, index); + const auto a1 = args[index++]; - if(sizeof(T) <= sizeof(a1)) { - return (T)a1; + if constexpr (sizeof(T) <= sizeof(a1) || sizeof(size_t) > 4) + { + return (T)(a1); } + else + { + const auto a2 = args[index++]; - const auto a2 = resolve_indexed_argument_part(args, index); - - const auto arg = (a1 | ((uint64_t)a2 << 32)); - return (T)arg; + const auto arg = (a1 | (static_cast(a2) << 32)); + return (T)(arg); + } } template -T resolve_indexed_argument(uint32_t* args, size_t& index) +T resolve_indexed_argument(size_t* args, size_t& index) { auto arg = resolve_indexed_argument_internal(args, index); return arg; @@ -49,27 +47,28 @@ class function_wrapper2 : public utils::object c_function_type* get_c_function() const { - return (c_function_type*)(void*)+[](uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4,uint32_t a5,uint32_t a6,uint32_t a7,uint32_t a8,uint32_t a9,uint32_t a10,uint32_t a11,uint32_t a12)-> uint64_t { - - uint32_t real_args[] { - a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12 - }; + return (c_function_type*)(void*)+[](size_t a1, size_t a2, size_t a3, size_t a4, size_t a5, size_t a6, size_t a7, + size_t a8, size_t a9, size_t a10, size_t a11, size_t a12) -> uint64_t { + size_t real_args[]{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12}; const auto lambda = +[](Args... args, user_data_pointer user_data) -> ReturnType { return (*static_cast(user_data))(std::forward(args)...); }; size_t index = 0; - std::tuple func_args{resolve_indexed_argument>>(real_args, index)..., resolve_indexed_argument(real_args, index)}; + std::tuple func_args{ + resolve_indexed_argument>>(real_args, index)..., + resolve_indexed_argument(real_args, index)}; - (void)index; + (void)index; - if constexpr(!std::is_void_v){ - return (uint64_t)std::apply(lambda, std::move(func_args)); - } - - std::apply(lambda, std::move(func_args)); - return 0; + if constexpr (!std::is_void_v) + { + return (uint64_t)std::apply(lambda, std::move(func_args)); + } + + std::apply(lambda, std::move(func_args)); + return 0; }; } From b170dac195523d8581af29ade7702813abece281 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 06:52:21 +0200 Subject: [PATCH 025/542] Fix warnings --- ..._wrapper2.hpp => function_wrapper_tcg.hpp} | 36 ++++++++++--------- .../unicorn-emulator/unicorn_x64_emulator.cpp | 20 +++++------ 2 files changed, 29 insertions(+), 27 deletions(-) rename src/backends/unicorn-emulator/{function_wrapper2.hpp => function_wrapper_tcg.hpp} (56%) diff --git a/src/backends/unicorn-emulator/function_wrapper2.hpp b/src/backends/unicorn-emulator/function_wrapper_tcg.hpp similarity index 56% rename from src/backends/unicorn-emulator/function_wrapper2.hpp rename to src/backends/unicorn-emulator/function_wrapper_tcg.hpp index e7471580..4cdaed8a 100644 --- a/src/backends/unicorn-emulator/function_wrapper2.hpp +++ b/src/backends/unicorn-emulator/function_wrapper_tcg.hpp @@ -1,55 +1,57 @@ #pragma once +#include #include #include #include -template -T resolve_indexed_argument_internal(size_t* args, size_t& index) +template +T resolve_indexed_argument_internal(const std::array& args, size_t& index) { const auto a1 = args[index++]; if constexpr (sizeof(T) <= sizeof(a1) || sizeof(size_t) > 4) { - return (T)(a1); + return T(a1); } else { const auto a2 = args[index++]; const auto arg = (a1 | (static_cast(a2) << 32)); - return (T)(arg); + return T(arg); } } -template -T resolve_indexed_argument(size_t* args, size_t& index) +template +T resolve_indexed_argument(const std::array& args, size_t& index) { - auto arg = resolve_indexed_argument_internal(args, index); + auto arg = resolve_indexed_argument_internal(args, index); return arg; } template -class function_wrapper2 : public utils::object +class function_wrapper_tcg : public utils::object { public: using user_data_pointer = void*; using c_function_type = ReturnType(Args..., user_data_pointer); using functor_type = std::function; - function_wrapper2() = default; + function_wrapper_tcg() = default; - function_wrapper2(functor_type functor) + function_wrapper_tcg(functor_type functor) : functor_(std::make_unique(std::move(functor))) { } c_function_type* get_c_function() const { - return (c_function_type*)(void*)+[](size_t a1, size_t a2, size_t a3, size_t a4, size_t a5, size_t a6, size_t a7, - size_t a8, size_t a9, size_t a10, size_t a11, size_t a12) -> uint64_t { - size_t real_args[]{a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12}; + auto* func = +[](const size_t a1, const size_t a2, const size_t a3, const size_t a4, const size_t a5, + const size_t a6, const size_t a7, const size_t a8, const size_t a9, const size_t a10, + const size_t a11, const size_t a12) -> uint64_t { + const std::array arguments = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12}; const auto lambda = +[](Args... args, user_data_pointer user_data) -> ReturnType { return (*static_cast(user_data))(std::forward(args)...); @@ -57,19 +59,21 @@ class function_wrapper2 : public utils::object size_t index = 0; std::tuple func_args{ - resolve_indexed_argument>>(real_args, index)..., - resolve_indexed_argument(real_args, index)}; + resolve_indexed_argument>>(arguments, index)..., + resolve_indexed_argument(arguments, index)}; (void)index; if constexpr (!std::is_void_v) { - return (uint64_t)std::apply(lambda, std::move(func_args)); + return uint64_t(std::apply(lambda, std::move(func_args))); } std::apply(lambda, std::move(func_args)); return 0; }; + + return reinterpret_cast(reinterpret_cast(func)); } void* get_function() const diff --git a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp index 97cd07c3..5be03b90 100644 --- a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp +++ b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp @@ -7,7 +7,7 @@ #include "unicorn_hook.hpp" #include "function_wrapper.hpp" -#include "function_wrapper2.hpp" +#include "function_wrapper_tcg.hpp" #include namespace unicorn @@ -386,7 +386,6 @@ namespace unicorn emulator_hook* hook_instruction(const int instruction_type, instruction_hook_callback callback) override { - unicorn_hook hook{*this}; auto container = std::make_unique(); @@ -400,19 +399,18 @@ namespace unicorn uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN_INVALID, wrapper.get_function(), wrapper.get_user_data(), 0, std::numeric_limits::max())); - container->add(std::move(wrapper), std::move(hook)); - } - else if(inst_type == x64_hookable_instructions::syscall){ - function_wrapper wrapper([c = std::move(callback)](uc_engine*) { - c(); - }); + container->add(std::move(wrapper), std::move(hook)); + } + else if (inst_type == x64_hookable_instructions::syscall) + { + function_wrapper wrapper([c = std::move(callback)](uc_engine*) { c(); }); const auto uc_instruction = map_hookable_instruction(inst_type); uce(uc_hook_add(*this, hook.make_reference(), UC_HOOK_INSN, wrapper.get_function(), wrapper.get_user_data(), 0, std::numeric_limits::max(), uc_instruction)); - container->add(std::move(wrapper), std::move(hook)); + container->add(std::move(wrapper), std::move(hook)); } else { @@ -425,7 +423,7 @@ namespace unicorn wrapper.get_user_data(), 0, std::numeric_limits::max(), uc_instruction)); - container->add(std::move(wrapper), std::move(hook)); + container->add(std::move(wrapper), std::move(hook)); } auto* result = container->as_opaque_hook(); @@ -552,7 +550,7 @@ namespace unicorn c(address); // }; - function_wrapper2 wrapper(std::move(exec_wrapper)); + function_wrapper_tcg wrapper(std::move(exec_wrapper)); unicorn_hook hook{*this}; From 8fd6e4fe9e613796447bc367c3ca0b9f21b4ee05 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 07:16:40 +0200 Subject: [PATCH 026/542] Upload page --- .github/workflows/build.yml | 60 ++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0c2bc13f..99ca26d8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -481,10 +481,10 @@ jobs: arch: ${{matrix.architecture}} script: "adb push build/${{matrix.preset}}/artifacts/* /data/local/tmp && adb shell \"cd /data/local/tmp && export LD_LIBRARY_PATH=. && chmod +x ./analyzer && EMULATOR_ICICLE=${{ matrix.emulator == 'Icicle' }} ./analyzer -e ./root c:/test-sample.exe\"" - page: - name: Page + build-page: + name: Build Page runs-on: ubuntu-latest - #needs: [build] + needs: [create-emulation-root, build] permissions: contents: read pages: write @@ -495,6 +495,58 @@ jobs: with: submodules: recursive + - name: Download Emscripten Artifacts + uses: pyTooling/download-artifact@v4 + with: + name: Emscripten Release Artifacts + path: build/release/artifacts + + - name: Download Windows Artifacts + uses: pyTooling/download-artifact@v4 + with: + name: Windows x86_64 Release Artifacts + path: build/release/artifacts + + - name: Download Emulation Root + uses: pyTooling/download-artifact@v4 + with: + name: Windows 2022 Emulation Root + path: build/release/artifacts/root + + - name: Copy Sample + run: cp ./build/release/artifacts/test-sample.exe build/release/artifacts/root/filesys/c/ + + - name: Create Emulation Root zip + run: zip -r ./page/root.zip ./build/release/artifacts/test-sample.exe build/release/artifacts/root + + - name: Copy Files + run: | + cp ./build/release/artifacts/analyzer.js ./page/ + cp ./build/release/artifacts/analyzer.wasm ./page/ + + - name: Upload Page Artifacts + uses: pyTooling/upload-artifact@v4 + with: + name: Page Artifacts + working-directory: page/ + path: "*" + + deploy-page: + name: Deploy Page + runs-on: ubuntu-latest + needs: [build-page] + #if: github.repository_owner == 'momo5502' && github.event_name == 'push' && github.ref == 'refs/heads/main' + permissions: + contents: read + pages: write + id-token: write + steps: + - name: Download Page Artifacts + uses: pyTooling/download-artifact@v4 + with: + name: Page Artifacts + path: ./page/ + - name: Setup Pages uses: actions/configure-pages@v5 @@ -510,7 +562,7 @@ jobs: summary: name: Pipeline Summary runs-on: ubuntu-24.04 - needs: [clang-tidy, build-apiset-dumper, smoke-test-android, create-emulation-root, build, test, win-test, verify-formatting] + needs: [build-page, clang-tidy, build-apiset-dumper, smoke-test-android, create-emulation-root, build, test, win-test, verify-formatting] if: always() steps: - uses: geekyeggo/delete-artifact@v5 From baa2fc5fa6ee2808ade9a17a13c338e5db2c3417 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 07:57:30 +0200 Subject: [PATCH 027/542] Fix page --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 99ca26d8..af27a526 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -517,7 +517,7 @@ jobs: run: cp ./build/release/artifacts/test-sample.exe build/release/artifacts/root/filesys/c/ - name: Create Emulation Root zip - run: zip -r ./page/root.zip ./build/release/artifacts/test-sample.exe build/release/artifacts/root + run: cd ./build/release/artifacts && zip -r "${{github.workspace}}/page/root.zip" ./root - name: Copy Files run: | From 4ac8d853a6b5715fe95900f679305e4d5d4c0312 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 08:15:17 +0200 Subject: [PATCH 028/542] Always use static socket factory for emscripten --- src/windows-emulator-test/emulation_test_utils.hpp | 2 +- .../network}/static_socket_factory.cpp | 0 .../network}/static_socket_factory.hpp | 2 +- src/windows-emulator/windows_emulator.cpp | 6 ++++++ 4 files changed, 8 insertions(+), 2 deletions(-) rename src/{windows-emulator-test => windows-emulator/network}/static_socket_factory.cpp (100%) rename src/{windows-emulator-test => windows-emulator/network}/static_socket_factory.hpp (73%) diff --git a/src/windows-emulator-test/emulation_test_utils.hpp b/src/windows-emulator-test/emulation_test_utils.hpp index a399702c..d972de71 100644 --- a/src/windows-emulator-test/emulation_test_utils.hpp +++ b/src/windows-emulator-test/emulation_test_utils.hpp @@ -4,7 +4,7 @@ #include #include -#include "static_socket_factory.hpp" +#include #define ASSERT_NOT_TERMINATED(win_emu) \ do \ diff --git a/src/windows-emulator-test/static_socket_factory.cpp b/src/windows-emulator/network/static_socket_factory.cpp similarity index 100% rename from src/windows-emulator-test/static_socket_factory.cpp rename to src/windows-emulator/network/static_socket_factory.cpp diff --git a/src/windows-emulator-test/static_socket_factory.hpp b/src/windows-emulator/network/static_socket_factory.hpp similarity index 73% rename from src/windows-emulator-test/static_socket_factory.hpp rename to src/windows-emulator/network/static_socket_factory.hpp index 6f162869..5b27faef 100644 --- a/src/windows-emulator-test/static_socket_factory.hpp +++ b/src/windows-emulator/network/static_socket_factory.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include "socket_factory.hpp" namespace network { diff --git a/src/windows-emulator/windows_emulator.cpp b/src/windows-emulator/windows_emulator.cpp index 4cb8b45f..99aef9cf 100644 --- a/src/windows-emulator/windows_emulator.cpp +++ b/src/windows-emulator/windows_emulator.cpp @@ -16,6 +16,8 @@ #include "exception_dispatch.hpp" #include "apiset/apiset.hpp" +#include "network/static_socket_factory.hpp" + constexpr auto MAX_INSTRUCTIONS_PER_TIME_SLICE = 100000; namespace @@ -257,7 +259,11 @@ namespace return std::move(interfaces.socket_factory); } +#ifdef OS_EMSCRIPTEN + return network::create_static_socket_factory(); +#else return std::make_unique(); +#endif } } From 31906fef2d896790d5436caeb5394e8700ce8eb8 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 08:30:41 +0200 Subject: [PATCH 029/542] Throw detailed exception --- src/windows-emulator/module/module_mapping.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows-emulator/module/module_mapping.cpp b/src/windows-emulator/module/module_mapping.cpp index 84c3ccd0..8f433809 100644 --- a/src/windows-emulator/module/module_mapping.cpp +++ b/src/windows-emulator/module/module_mapping.cpp @@ -259,7 +259,7 @@ mapped_module map_module_from_file(memory_manager& memory, std::filesystem::path const auto data = utils::io::read_file(file); if (data.empty()) { - throw std::runtime_error("Bad file data"); + throw std::runtime_error("Bad file data: " + file.string()); } return map_module_from_data(memory, data, std::move(file)); From 11a19593d98da6a2cd9158226e3971cb6376157e Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 08:40:22 +0200 Subject: [PATCH 030/542] Load correct sample --- page/emulator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/page/emulator.js b/page/emulator.js index 7a007d5e..04ebf177 100644 --- a/page/emulator.js +++ b/page/emulator.js @@ -11,7 +11,7 @@ function logLine(text) { function runEmulation(filesystem) { globalThis.Module = { - arguments: ["-b", "-c", "-e", "./root", "c:/lul.exe",], + arguments: ["-b", "-c", "-e", "./root", "c:/test-sample.exe",], onRuntimeInitialized: function () { filesystem.forEach(e => { if (e.name.endsWith("/")) { From dda99b4d705f253929e548003cc52216a5690a2e Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 08:56:13 +0200 Subject: [PATCH 031/542] Set min height of the track --- page/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/page/index.html b/page/index.html index 0256f824..0b0f2fae 100644 --- a/page/index.html +++ b/page/index.html @@ -216,6 +216,7 @@ ::-webkit-scrollbar-thumb { background-color: rgba(97, 97, 97, 0.4); border-radius: 20px; + min-height: 50px; border: 6px solid transparent; background-clip: content-box; transition: all 0.1s linear; From 5147e258128f10f3325be26765ea880f8ca1d9ce Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 09:14:57 +0200 Subject: [PATCH 032/542] Cleanup compilation --- cmake/compiler-env.cmake | 3 --- deps/unicorn | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/cmake/compiler-env.cmake b/cmake/compiler-env.cmake index 5f610950..c5b49138 100644 --- a/cmake/compiler-env.cmake +++ b/cmake/compiler-env.cmake @@ -51,9 +51,6 @@ if(UNIX) momo_add_c_and_cxx_compile_options( -fvisibility=hidden -ftrivial-auto-var-init=zero - #-Wbad-function-cast - #-Wcast-function-type - -Wno-int-conversion ) endif() diff --git a/deps/unicorn b/deps/unicorn index 6f2156df..41980a72 160000 --- a/deps/unicorn +++ b/deps/unicorn @@ -1 +1 @@ -Subproject commit 6f2156dfac3abad316a4f02bf609ec1bb700d02e +Subproject commit 41980a72746ae2c995454b465236bcf9ce7c1b71 From 9a2a6de16bc0ed52873811c38b7007359eea23aa Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 09:16:11 +0200 Subject: [PATCH 033/542] Only deploy page on main branch --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index af27a526..a6860204 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -534,8 +534,8 @@ jobs: deploy-page: name: Deploy Page runs-on: ubuntu-latest - needs: [build-page] - #if: github.repository_owner == 'momo5502' && github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: [build-page, summary] + if: github.repository_owner == 'momo5502' && github.event_name == 'push' && github.ref == 'refs/heads/main' permissions: contents: read pages: write From be89ba0c97f869d4999e43bc1dd7e6ba3daed051 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 09:51:57 +0200 Subject: [PATCH 034/542] Fix unicorn --- deps/unicorn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/unicorn b/deps/unicorn index 41980a72..6b329651 160000 --- a/deps/unicorn +++ b/deps/unicorn @@ -1 +1 @@ -Subproject commit 41980a72746ae2c995454b465236bcf9ce7c1b71 +Subproject commit 6b32965144d4ff91eb303beed0ec938ad467f33b From baca3ce0f973747b8b66ab6d14d527389fd786f2 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 11:29:40 +0200 Subject: [PATCH 035/542] Always print errors --- src/analyzer/main.cpp | 18 ++++++++++++------ src/fuzzer/main.cpp | 3 +-- src/windows-emulator/logger.cpp | 18 +++++++++--------- src/windows-emulator/logger.hpp | 2 +- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 6dd04785..6333ee87 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -153,26 +153,32 @@ namespace } catch (const std::exception& e) { - win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 " - %s\n", - win_emu.emu().read_instruction_pointer(), e.what()); + win_emu.log.error("Emulation failed at: 0x%" PRIx64 " - %s\n", win_emu.emu().read_instruction_pointer(), + e.what()); throw; } catch (...) { - win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n", - win_emu.emu().read_instruction_pointer()); + win_emu.log.error("Emulation failed at: 0x%" PRIx64 "\n", win_emu.emu().read_instruction_pointer()); throw; } const auto exit_status = win_emu.process.exit_status; if (!exit_status.has_value()) { - win_emu.log.print(color::red, "Emulation terminated without status!\n"); + win_emu.log.error("Emulation terminated without status!\n"); return false; } const auto success = *exit_status == STATUS_SUCCESS; - win_emu.log.print(success ? color::green : color::red, "Emulation terminated with status: %X\n", *exit_status); + + if (!options.silent) + { + win_emu.log.disable_output(false); + win_emu.log.print(success ? color::green : color::red, "Emulation terminated with status: %X\n", + *exit_status); + } + return success; } diff --git a/src/fuzzer/main.cpp b/src/fuzzer/main.cpp index 24f9fc9f..0184a3af 100644 --- a/src/fuzzer/main.cpp +++ b/src/fuzzer/main.cpp @@ -28,8 +28,7 @@ namespace catch (...) { win_emu.log.disable_output(false); - win_emu.log.print(color::red, "Emulation failed at: 0x%" PRIx64 "\n", - win_emu.emu().read_instruction_pointer()); + win_emu.log.error("Emulation failed at: 0x%" PRIx64 "\n", win_emu.emu().read_instruction_pointer()); throw; } diff --git a/src/windows-emulator/logger.cpp b/src/windows-emulator/logger.cpp index 72e18404..292bdfb6 100644 --- a/src/windows-emulator/logger.cpp +++ b/src/windows-emulator/logger.cpp @@ -96,7 +96,7 @@ namespace va_list ap; \ va_start(ap, msg); \ const auto str = format(&ap, msg); \ - va_end(ap); + va_end(ap) void print_colored(const std::string_view& line, const color_type base_color) { @@ -106,9 +106,9 @@ namespace } } -void logger::print(const color c, const std::string_view message) const +void logger::print_message(const color c, const std::string_view message, const bool force) const { - if (this->disable_output_) + if (!force && this->disable_output_) { return; } @@ -120,40 +120,40 @@ void logger::print(const color c, const std::string_view message) const void logger::print(const color c, const char* message, ...) const { format_to_string(message, data); - this->print(c, data); + this->print_message(c, data); } // NOLINTNEXTLINE(cert-dcl50-cpp) void logger::info(const char* message, ...) const { format_to_string(message, data); - this->print(color::cyan, data); + this->print_message(color::cyan, data); } // NOLINTNEXTLINE(cert-dcl50-cpp) void logger::warn(const char* message, ...) const { format_to_string(message, data); - this->print(color::yellow, data); + this->print_message(color::yellow, data); } // NOLINTNEXTLINE(cert-dcl50-cpp) void logger::error(const char* message, ...) const { format_to_string(message, data); - this->print(color::red, data); + this->print_message(color::red, data, true); } // NOLINTNEXTLINE(cert-dcl50-cpp) void logger::success(const char* message, ...) const { format_to_string(message, data); - this->print(color::green, data); + this->print_message(color::green, data); } // NOLINTNEXTLINE(cert-dcl50-cpp) void logger::log(const char* message, ...) const { format_to_string(message, data); - this->print(color::gray, data); + this->print_message(color::gray, data); } diff --git a/src/windows-emulator/logger.hpp b/src/windows-emulator/logger.hpp index 2e1c3af2..aafcbb9c 100644 --- a/src/windows-emulator/logger.hpp +++ b/src/windows-emulator/logger.hpp @@ -23,7 +23,6 @@ enum class color class logger { public: - void print(color c, std::string_view message) const; void print(color c, const char* message, ...) const FORMAT_ATTRIBUTE(3, 4); void info(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3); void warn(const char* message, ...) const FORMAT_ATTRIBUTE(2, 3); @@ -43,4 +42,5 @@ class logger private: bool disable_output_{false}; + void print_message(color c, std::string_view message, bool force = false) const; }; From f641ebbfd5edb0f840689dd37c73be0be0c3b0a2 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 11:37:34 +0200 Subject: [PATCH 036/542] Support own exe files --- page/emulator.js | 13 ++++--- page/index.html | 93 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 94 insertions(+), 12 deletions(-) diff --git a/page/emulator.js b/page/emulator.js index 04ebf177..d78ea051 100644 --- a/page/emulator.js +++ b/page/emulator.js @@ -1,17 +1,18 @@ onmessage = async (event) => { const data = event.data; if (data.message == "run") { - runEmulation(data.data); + const payload = data.data; + runEmulation(payload.filesystem, payload.file); } }; function logLine(text) { - postMessage(text); + postMessage({ message: "log", data: text }); } -function runEmulation(filesystem) { +function runEmulation(filesystem, file) { globalThis.Module = { - arguments: ["-b", "-c", "-e", "./root", "c:/test-sample.exe",], + arguments: ["-b", /*"-c",*/ "-e", "./root", file], onRuntimeInitialized: function () { filesystem.forEach(e => { if (e.name.endsWith("/")) { @@ -20,6 +21,9 @@ function runEmulation(filesystem) { const dirs = e.name.split("/") const file = dirs.pop(); const buffer = new Uint8Array(e.data); + if (FS.analyzePath(e.name).exists) { + FS.unlink(e.name); + } FS.createDataFile("/" + dirs.join('/'), file, buffer, true, true); } }) @@ -27,6 +31,7 @@ function runEmulation(filesystem) { print: logLine, printErr: logLine, postRun: () => { + postMessage({ message: "end", data: null }); self.close(); }, }; diff --git a/page/index.html b/page/index.html index 0b0f2fae..f650e201 100644 --- a/page/index.html +++ b/page/index.html @@ -343,6 +343,40 @@ return cacheAndUseData("emulator-filesystem-2", fetchFilesystem); } + function selectAndReadFile() { + return new Promise((resolve, reject) => { + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = '.exe'; + + fileInput.addEventListener('change', function (event) { + const file = event.target.files[0]; + if (file) { + const reader = new FileReader(); + + reader.onload = function (e) { + const arrayBuffer = e.target.result; + resolve({ + name: file.name, + data: arrayBuffer + }); + }; + + reader.onerror = function (e) { + reject(new Error('Error reading file: ' + e.target.error)); + }; + + reader.readAsArrayBuffer(file); + } else { + reject(new Error('No file selected')); + } + }); + + // Programmatically click the file input element to open the file picker + fileInput.click(); + }); + } + function printText(lines) { if (lines.length == 0) { return; @@ -362,6 +396,13 @@ outputDiv.scrollTop = outputDiv.scrollHeight; } + function clearLog() { + var outputDiv = document.getElementById('output'); + if (outputDiv) { + outputDiv.innerHTML = ""; + } + } + function flushLines() { const lines = globalThis.logLines; globalThis.logLines = []; @@ -373,24 +414,59 @@ globalThis.logLines.push(text); } - async function startEmulation() { - if (window.emulationStarted) { + function stopEmulation() { + if (window.emulator) { + window.emulator.terminate(); + window.emulator = undefined; + } + } + + async function selectAndStartEmulation() { + const file = await selectAndReadFile(); + return await startEmulation(file); + } + + async function startEmulation(fileData) { + if (window.emulator) { return; } - window.emulationStarted = true; + clearLog(); + + const worker = new Worker("./emulator.js"); + window.emulator = worker; const filesystem = await getFilesystem(); logLine("Starting emulation..."); - const worker = new Worker("./emulator.js?4"); + var file = "c:/test-sample.exe"; + if (fileData) { + const filename = fileData.name.split("/").pop().split("\\").pop(); + const canonicalName = filename.toLowerCase() + file = "c:/" + canonicalName; + filesystem.push({ + name: "root/filesys/c/" + canonicalName, + data: fileData.data, + }); + } + worker.onmessage = (event) => { - logLine(event.data); + if (event.data.message == "log") { + logLine(event.data.data); + } + + if (event.data.message == "end") { + window.emulator = undefined; + } }; + worker.postMessage({ message: "run", - data: filesystem, + data: { + filesystem, + file, + } }); } @@ -423,8 +499,9 @@ From bf23eac588ac749feb23644a255fe80986a526f8 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 11:46:48 +0200 Subject: [PATCH 037/542] Always perform concise executable read logging without verbose logging --- src/analyzer/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 6333ee87..1b9ce5a4 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -265,7 +265,7 @@ namespace const auto& exe = *win_emu->mod_manager.executable; - const auto concise_logging = options.concise_logging; + const auto concise_logging = !options.verbose_logging; for (const auto& section : exe.sections) { From fe4ce3d379855a4faef3066b437ceb64a680223a Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 12:23:51 +0200 Subject: [PATCH 038/542] Fix layout --- page/index.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/page/index.html b/page/index.html index f650e201..8564196d 100644 --- a/page/index.html +++ b/page/index.html @@ -30,9 +30,11 @@ .buttons { z-index: 201; + margin: 20px; margin-top: 10px; margin-bottom: 15px; display: flex; + flex-wrap: wrap; gap: 15px; } @@ -116,7 +118,7 @@ display: flex; flex-direction: column; z-index: 200; - margin: 15px; + margin: 20px; } .terminal-header { From c866380fe017557fdfdd06fbd5d879bb3c552db1 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 12:40:26 +0200 Subject: [PATCH 039/542] Fix exceptions --- cmake/compiler-env.cmake | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmake/compiler-env.cmake b/cmake/compiler-env.cmake index c5b49138..77951879 100644 --- a/cmake/compiler-env.cmake +++ b/cmake/compiler-env.cmake @@ -89,13 +89,18 @@ endif() ########################################## if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") + add_compile_options( + -fexceptions + ) + add_link_options( + -fexceptions -sALLOW_MEMORY_GROWTH=1 -sASSERTIONS -sWASM_BIGINT -sENVIRONMENT=web -sUSE_OFFSET_CONVERTER - -sEXCEPTION_CATCHING_ALLOWED=[..] + #-sEXCEPTION_CATCHING_ALLOWED=[..] -sEXIT_RUNTIME #-lnodefs.js -sNODERAWFS=1 #-sASYNCIFY From ef72596436bdf090d82738adb15f6212c6025961 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 16:45:27 +0200 Subject: [PATCH 040/542] Add nodejs support --- CMakeLists.txt | 1 + cmake/compiler-env.cmake | 20 +++- cmake/misc/node-pre-script.js | 3 + deps/unicorn | 2 +- .../unicorn-emulator/function_wrapper_tcg.hpp | 91 ------------------- .../unicorn-emulator/unicorn_x64_emulator.cpp | 3 +- 6 files changed, 22 insertions(+), 98 deletions(-) create mode 100644 cmake/misc/node-pre-script.js delete mode 100644 src/backends/unicorn-emulator/function_wrapper_tcg.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f430a56c..ec3e355d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ option(MOMO_ENABLE_AVX2 "Enable AVX2 support" ON) option(MOMO_ENABLE_SANITIZER "Enable sanitizer" OFF) option(MOMO_ENABLE_CLANG_TIDY "Enable clang-tidy checks" OFF) option(MOMO_ENABLE_RUST_CODE "Enable code parts written in rust" ON) +option(MOMO_EMSCRIPTEN_SUPPORT_NODEJS "Enable Node.js filesystem for emscripten compilation" OFF) option(MOMO_BUILD_AS_LIBRARY "Configure and Build the emulator as a shared library (without the samples and tests)" OFF) set(MOMO_REFLECTION_LEVEL "0" CACHE STRING "Reflection level for the build") diff --git a/cmake/compiler-env.cmake b/cmake/compiler-env.cmake index 77951879..d9d09c5c 100644 --- a/cmake/compiler-env.cmake +++ b/cmake/compiler-env.cmake @@ -89,8 +89,9 @@ endif() ########################################## if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") - add_compile_options( + momo_add_c_and_cxx_compile_options( -fexceptions + -ftrivial-auto-var-init=zero ) add_link_options( @@ -98,13 +99,24 @@ if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") -sALLOW_MEMORY_GROWTH=1 -sASSERTIONS -sWASM_BIGINT - -sENVIRONMENT=web -sUSE_OFFSET_CONVERTER #-sEXCEPTION_CATCHING_ALLOWED=[..] -sEXIT_RUNTIME - #-lnodefs.js -sNODERAWFS=1 #-sASYNCIFY -) + ) + + if(MOMO_EMSCRIPTEN_SUPPORT_NODEJS) + add_link_options( + -lnodefs.js -sNODERAWFS=1 + -sENVIRONMENT=node + -sMAXIMUM_MEMORY=4gb + --pre-js ${CMAKE_CURRENT_LIST_DIR}/misc/node-pre-script.js + ) + else() + add_link_options( + -sENVIRONMENT=worker + ) + endif() endif() ########################################## diff --git a/cmake/misc/node-pre-script.js b/cmake/misc/node-pre-script.js new file mode 100644 index 00000000..ee8eb4ee --- /dev/null +++ b/cmake/misc/node-pre-script.js @@ -0,0 +1,3 @@ +Module['preRun'] = () => { + ENV = process.env; +}; diff --git a/deps/unicorn b/deps/unicorn index 6b329651..843eb15b 160000 --- a/deps/unicorn +++ b/deps/unicorn @@ -1 +1 @@ -Subproject commit 6b32965144d4ff91eb303beed0ec938ad467f33b +Subproject commit 843eb15bcd01571a5062fa0f3937fc8e49a40cdc diff --git a/src/backends/unicorn-emulator/function_wrapper_tcg.hpp b/src/backends/unicorn-emulator/function_wrapper_tcg.hpp deleted file mode 100644 index 4cdaed8a..00000000 --- a/src/backends/unicorn-emulator/function_wrapper_tcg.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -template -T resolve_indexed_argument_internal(const std::array& args, size_t& index) -{ - const auto a1 = args[index++]; - - if constexpr (sizeof(T) <= sizeof(a1) || sizeof(size_t) > 4) - { - return T(a1); - } - else - { - const auto a2 = args[index++]; - - const auto arg = (a1 | (static_cast(a2) << 32)); - return T(arg); - } -} - -template -T resolve_indexed_argument(const std::array& args, size_t& index) -{ - auto arg = resolve_indexed_argument_internal(args, index); - return arg; -} - -template -class function_wrapper_tcg : public utils::object -{ - public: - using user_data_pointer = void*; - using c_function_type = ReturnType(Args..., user_data_pointer); - using functor_type = std::function; - - function_wrapper_tcg() = default; - - function_wrapper_tcg(functor_type functor) - : functor_(std::make_unique(std::move(functor))) - { - } - - c_function_type* get_c_function() const - { - auto* func = +[](const size_t a1, const size_t a2, const size_t a3, const size_t a4, const size_t a5, - const size_t a6, const size_t a7, const size_t a8, const size_t a9, const size_t a10, - const size_t a11, const size_t a12) -> uint64_t { - const std::array arguments = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12}; - - const auto lambda = +[](Args... args, user_data_pointer user_data) -> ReturnType { - return (*static_cast(user_data))(std::forward(args)...); - }; - - size_t index = 0; - std::tuple func_args{ - resolve_indexed_argument>>(arguments, index)..., - resolve_indexed_argument(arguments, index)}; - - (void)index; - - if constexpr (!std::is_void_v) - { - return uint64_t(std::apply(lambda, std::move(func_args))); - } - - std::apply(lambda, std::move(func_args)); - return 0; - }; - - return reinterpret_cast(reinterpret_cast(func)); - } - - void* get_function() const - { - return reinterpret_cast(this->get_c_function()); - } - - user_data_pointer get_user_data() const - { - return this->functor_.get(); - } - - private: - std::unique_ptr functor_{}; -}; diff --git a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp index 5be03b90..d542ed8f 100644 --- a/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp +++ b/src/backends/unicorn-emulator/unicorn_x64_emulator.cpp @@ -7,7 +7,6 @@ #include "unicorn_hook.hpp" #include "function_wrapper.hpp" -#include "function_wrapper_tcg.hpp" #include namespace unicorn @@ -550,7 +549,7 @@ namespace unicorn c(address); // }; - function_wrapper_tcg wrapper(std::move(exec_wrapper)); + function_wrapper wrapper(std::move(exec_wrapper)); unicorn_hook hook{*this}; From 67534393ff364fb537812a9be19f8f5fdd009d73 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 16:59:13 +0200 Subject: [PATCH 041/542] Build and test nodejs --- .github/workflows/build.yml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6860204..cd1b0a94 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -161,7 +161,8 @@ jobs: - iOS arm64 - Android x86_64 - Android arm64-v8a - - Emscripten + - Emscripten Web + - Emscripten Node.js configuration: - Debug - Release @@ -202,9 +203,12 @@ jobs: abi: arm64-v8a rust-target: aarch64-linux-android cmake-options: "-DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/cmake/toolchain/android-ndk.cmake" - - platform: Emscripten + - platform: Emscripten Web runner: ubuntu-24.04 cmake-options: "-DMOMO_ENABLE_RUST_CODE=Off -DCMAKE_TOOLCHAIN_FILE=$(dirname $(which emcc))/cmake/Modules/Platform/Emscripten.cmake" + - platform: Emscripten Node.js + runner: ubuntu-24.04 + cmake-options: "-DMOMO_EMSCRIPTEN_SUPPORT_NODEJS=On -DMOMO_ENABLE_RUST_CODE=Off -DCMAKE_TOOLCHAIN_FILE=$(dirname $(which emcc))/cmake/Modules/Platform/Emscripten.cmake" steps: - name: Checkout Source uses: actions/checkout@v4 @@ -219,7 +223,7 @@ jobs: run: rustup target add ${{ matrix.rust-target }} - name: Install Emscripten - if: "${{ matrix.platform == 'Emscripten' }}" + if: "${{ startsWith(matrix.platform, 'Emscripten') }}" uses: mymindstorm/setup-emsdk@v14 - name: Install Clang @@ -284,6 +288,7 @@ jobs: - Linux x86_64 Clang - macOS arm64 - macOS x86_64 + - Emscripten Node.js emulator: - Unicorn - Icicle @@ -313,6 +318,8 @@ jobs: runner: macos-latest - platform: macOS x86_64 runner: macos-13 + - platform: Emscripten Node.js + runner: ubuntu-24.04 steps: - name: Checkout Source uses: actions/checkout@v4 @@ -354,7 +361,7 @@ jobs: run: cp build/${{matrix.preset}}/artifacts/test-sample.exe build/${{matrix.preset}}/artifacts/root/filesys/c/ - name: CMake Test - if: ${{ matrix.emulator != 'Icicle' || matrix.platform != 'Windows x86' }} + if: ${{ matrix.emulator != 'Icicle' || (matrix.platform != 'Windows x86' && !startsWith(matrix.platform, 'Emscripten')) }} run: cd build/${{matrix.preset}} && ctest --verbose -j env: EMULATOR_ROOT: ${{github.workspace}}/build/${{matrix.preset}}/artifacts/root @@ -495,10 +502,10 @@ jobs: with: submodules: recursive - - name: Download Emscripten Artifacts + - name: Download Emscripten Web Artifacts uses: pyTooling/download-artifact@v4 with: - name: Emscripten Release Artifacts + name: Emscripten Web Release Artifacts path: build/release/artifacts - name: Download Windows Artifacts From ff050ed74ce1863fe0944a125beaa46443eeb42d Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 17:17:15 +0200 Subject: [PATCH 042/542] Fix node tests --- src/analyzer/CMakeLists.txt | 8 +++++++- src/analyzer/test.py | 9 +++++++++ src/windows-emulator-test/CMakeLists.txt | 8 +++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/analyzer/CMakeLists.txt b/src/analyzer/CMakeLists.txt index 0cfffb61..7e40d529 100644 --- a/src/analyzer/CMakeLists.txt +++ b/src/analyzer/CMakeLists.txt @@ -32,6 +32,12 @@ set(ENV_PREFIX "%") set(ENV_SUFFIX "%") endif() +set(ANYLZER_TEST_ENVIRONMENT "default") + +if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") + set(ANYLZER_TEST_ENVIRONMENT "node") +endif() + add_test(NAME analyzer-test - COMMAND "${PYTHON3_EXE}" "${CMAKE_CURRENT_LIST_DIR}/test.py" + COMMAND "${PYTHON3_EXE}" "${CMAKE_CURRENT_LIST_DIR}/test.py" "${ANYLZER_TEST_ENVIRONMENT}" WORKING_DIRECTORY "$") diff --git a/src/analyzer/test.py b/src/analyzer/test.py index fd907dc4..671c54e0 100644 --- a/src/analyzer/test.py +++ b/src/analyzer/test.py @@ -1,4 +1,5 @@ import os +import sys import subprocess emulator_root = os.getenv('EMULATOR_ROOT') @@ -7,7 +8,12 @@ virtual_sample = 'C:/analysis-sample.exe' application = 'analyzer' +is_node = len(sys.argv) > 1 and sys.argv == "node" + def make_app(app): + if is_node: + return app + ".js" + if os.name == 'nt': return app + ".exe" @@ -21,6 +27,9 @@ command = [ virtual_sample ] +if is_node: + command = ["node"] + command + result = subprocess.run(command, cwd=os.getcwd()) exit(result.returncode) diff --git a/src/windows-emulator-test/CMakeLists.txt b/src/windows-emulator-test/CMakeLists.txt index 8bea4b26..91ed881d 100644 --- a/src/windows-emulator-test/CMakeLists.txt +++ b/src/windows-emulator-test/CMakeLists.txt @@ -20,8 +20,14 @@ if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) add_dependencies(windows-emulator-test test-sample) endif() +set(TEST_COMMAND "./windows-emulator-test") + +if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") + set(TEST_COMMAND node "./windows-emulator-test.js") +endif() + add_test(NAME windows-emulator-test - COMMAND "${PYTHON3_EXE}" "${PROJECT_SOURCE_DIR}/deps/gtest-parallel/gtest_parallel.py" ./windows-emulator-test + COMMAND "${PYTHON3_EXE}" "${PROJECT_SOURCE_DIR}/deps/gtest-parallel/gtest_parallel.py" ${TEST_COMMAND} WORKING_DIRECTORY "$") momo_targets_set_folder("tests" windows-emulator-test) From 59f700e1cd6a10b9d3750d6545e5ab41c35e4ae3 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 17:28:42 +0200 Subject: [PATCH 043/542] Revert "Fix node tests" This reverts commit ff050ed74ce1863fe0944a125beaa46443eeb42d. --- src/analyzer/CMakeLists.txt | 8 +------- src/analyzer/test.py | 9 --------- src/windows-emulator-test/CMakeLists.txt | 8 +------- 3 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/analyzer/CMakeLists.txt b/src/analyzer/CMakeLists.txt index 7e40d529..0cfffb61 100644 --- a/src/analyzer/CMakeLists.txt +++ b/src/analyzer/CMakeLists.txt @@ -32,12 +32,6 @@ set(ENV_PREFIX "%") set(ENV_SUFFIX "%") endif() -set(ANYLZER_TEST_ENVIRONMENT "default") - -if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") - set(ANYLZER_TEST_ENVIRONMENT "node") -endif() - add_test(NAME analyzer-test - COMMAND "${PYTHON3_EXE}" "${CMAKE_CURRENT_LIST_DIR}/test.py" "${ANYLZER_TEST_ENVIRONMENT}" + COMMAND "${PYTHON3_EXE}" "${CMAKE_CURRENT_LIST_DIR}/test.py" WORKING_DIRECTORY "$") diff --git a/src/analyzer/test.py b/src/analyzer/test.py index 671c54e0..fd907dc4 100644 --- a/src/analyzer/test.py +++ b/src/analyzer/test.py @@ -1,5 +1,4 @@ import os -import sys import subprocess emulator_root = os.getenv('EMULATOR_ROOT') @@ -8,12 +7,7 @@ virtual_sample = 'C:/analysis-sample.exe' application = 'analyzer' -is_node = len(sys.argv) > 1 and sys.argv == "node" - def make_app(app): - if is_node: - return app + ".js" - if os.name == 'nt': return app + ".exe" @@ -27,9 +21,6 @@ command = [ virtual_sample ] -if is_node: - command = ["node"] + command - result = subprocess.run(command, cwd=os.getcwd()) exit(result.returncode) diff --git a/src/windows-emulator-test/CMakeLists.txt b/src/windows-emulator-test/CMakeLists.txt index 91ed881d..8bea4b26 100644 --- a/src/windows-emulator-test/CMakeLists.txt +++ b/src/windows-emulator-test/CMakeLists.txt @@ -20,14 +20,8 @@ if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) add_dependencies(windows-emulator-test test-sample) endif() -set(TEST_COMMAND "./windows-emulator-test") - -if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") - set(TEST_COMMAND node "./windows-emulator-test.js") -endif() - add_test(NAME windows-emulator-test - COMMAND "${PYTHON3_EXE}" "${PROJECT_SOURCE_DIR}/deps/gtest-parallel/gtest_parallel.py" ${TEST_COMMAND} + COMMAND "${PYTHON3_EXE}" "${PROJECT_SOURCE_DIR}/deps/gtest-parallel/gtest_parallel.py" ./windows-emulator-test WORKING_DIRECTORY "$") momo_targets_set_folder("tests" windows-emulator-test) From 9f10a555b085796d8aafb2e263c5d969789511b3 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 17:33:07 +0200 Subject: [PATCH 044/542] Add nodejs smoke test --- .github/workflows/build.yml | 43 ++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cd1b0a94..b4595d51 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -288,7 +288,6 @@ jobs: - Linux x86_64 Clang - macOS arm64 - macOS x86_64 - - Emscripten Node.js emulator: - Unicorn - Icicle @@ -361,7 +360,7 @@ jobs: run: cp build/${{matrix.preset}}/artifacts/test-sample.exe build/${{matrix.preset}}/artifacts/root/filesys/c/ - name: CMake Test - if: ${{ matrix.emulator != 'Icicle' || (matrix.platform != 'Windows x86' && !startsWith(matrix.platform, 'Emscripten')) }} + if: ${{ matrix.emulator != 'Icicle' || matrix.platform != 'Windows x86' }} run: cd build/${{matrix.preset}} && ctest --verbose -j env: EMULATOR_ROOT: ${{github.workspace}}/build/${{matrix.preset}}/artifacts/root @@ -423,6 +422,44 @@ jobs: ANALYSIS_SAMPLE: ${{github.workspace}}/build/release/artifacts/test-sample.exe + smoke-test-node: + name: Smoke Test Node.js + runs-on: ubuntu-24.04 + needs: [create-emulation-root, build] + steps: + - name: Checkout Source + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Download Artifacts + uses: pyTooling/download-artifact@v4 + with: + name: Emscripten Node.js Release Artifacts + path: build/release/artifacts + + - name: Download Windows Artifacts + uses: pyTooling/download-artifact@v4 + with: + name: Windows x86_64 Release Artifacts + path: build/release/artifacts + + - name: Download Emulation Root + uses: pyTooling/download-artifact@v4 + with: + name: Windows 2022 Emulation Root + path: build/release/artifacts/root + + - name: Copy Test Sample + run: cp build/release/artifacts/test-sample.exe build/release/artifacts/root/filesys/c/ + + - name: CMake Test + run: cd build/release/artifacts && node ./windows-emulator-test.js + env: + EMULATOR_ROOT: ${{github.workspace}}/build/release/artifacts/root + EMULATOR_VERBOSE: ${{ github.event.inputs.verbose }} + + smoke-test-android: name: Smoke Test Android runs-on: ${{ matrix.runner }} @@ -569,7 +606,7 @@ jobs: summary: name: Pipeline Summary runs-on: ubuntu-24.04 - needs: [build-page, clang-tidy, build-apiset-dumper, smoke-test-android, create-emulation-root, build, test, win-test, verify-formatting] + needs: [build-page, clang-tidy, build-apiset-dumper, smoke-test-node, smoke-test-android, create-emulation-root, build, test, win-test, verify-formatting] if: always() steps: - uses: geekyeggo/delete-artifact@v5 From 38b2c05fa720cb919690b8ff843ff19be5355df0 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 17:37:26 +0200 Subject: [PATCH 045/542] Maybe fix unicorn --- deps/unicorn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/unicorn b/deps/unicorn index 843eb15b..8dc3f4c5 160000 --- a/deps/unicorn +++ b/deps/unicorn @@ -1 +1 @@ -Subproject commit 843eb15bcd01571a5062fa0f3937fc8e49a40cdc +Subproject commit 8dc3f4c5cd6d8053428c3384baf0710f0ee60b40 From 26afe5731c9afd90f26079b1726b825f479c89ae Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 17:42:11 +0200 Subject: [PATCH 046/542] Fix pipeline --- .github/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b4595d51..3219cd96 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -317,8 +317,6 @@ jobs: runner: macos-latest - platform: macOS x86_64 runner: macos-13 - - platform: Emscripten Node.js - runner: ubuntu-24.04 steps: - name: Checkout Source uses: actions/checkout@v4 From a157a76cae7e68ee44ab40abfaf735b6cd8552c4 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 17:54:59 +0200 Subject: [PATCH 047/542] Ensure bash is used to set environment variables --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3219cd96..00455b6b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -252,6 +252,7 @@ jobs: add-to-path: false - name: Setup Environment Variables + shell: bash if: ${{ startsWith(matrix.platform, 'Android') }} run: | echo "ANDROID_NDK_ROOT=${{ steps.setup-ndk.outputs.ndk-path }}" >> $GITHUB_ENV @@ -324,6 +325,7 @@ jobs: submodules: recursive - name: Setup Environment Variables + shell: bash run: | echo "RUST_BACKTRACE=1" >> $GITHUB_ENV echo "ASAN_OPTIONS=detect_odr_violation=0" >> $GITHUB_ENV @@ -386,6 +388,7 @@ jobs: submodules: recursive - name: Setup Environment Variables + shell: bash run: | echo "RUST_BACKTRACE=1" >> $GITHUB_ENV echo "ASAN_OPTIONS=detect_odr_violation=0" >> $GITHUB_ENV From fc48ce11027b3aea345415b365d28dc8503c262c Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 18:19:39 +0200 Subject: [PATCH 048/542] Update unicorn --- deps/unicorn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/unicorn b/deps/unicorn index 8dc3f4c5..a19ae94c 160000 --- a/deps/unicorn +++ b/deps/unicorn @@ -1 +1 @@ -Subproject commit 8dc3f4c5cd6d8053428c3384baf0710f0ee60b40 +Subproject commit a19ae94cbf42ddb6aa2bf0b60c7c0fa907af039b From 0edd4c640c30a61d75be5f162efac0c865ed3d36 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 18 Apr 2025 20:11:27 +0200 Subject: [PATCH 049/542] Remove unnecessary permissions --- .github/workflows/build.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 00455b6b..27bd0c5f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -530,10 +530,6 @@ jobs: name: Build Page runs-on: ubuntu-latest needs: [create-emulation-root, build] - permissions: - contents: read - pages: write - id-token: write steps: - name: Checkout Source uses: actions/checkout@v4 From 5c70f780d10cb878bfb20291d967d27cd045b818 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 19 Apr 2025 08:01:47 +0200 Subject: [PATCH 050/542] Archive more dlls --- src/tools/create-root.bat | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tools/create-root.bat b/src/tools/create-root.bat index 6bd1bd34..79c92815 100644 --- a/src/tools/create-root.bat +++ b/src/tools/create-root.bat @@ -111,6 +111,12 @@ CALL :collect shcore.dll CALL :collect diagnosticdatasettings.dll CALL :collect mswsock.dll CALL :collect umpdc.dll +CALL :collect pdh.dll +CALL :collect dxva2.dll +CALL :collect propsys.dll +CALL :collect wintypes.dll +CALL :collect slwga.dll +CALL :collect sppc.dll CALL :collect locale.nls From 22c3d0bc7ce3ce29339a412fbda75c5e86fac008 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 19 Apr 2025 08:02:03 +0200 Subject: [PATCH 051/542] Watch objects in detail only with verbose logging --- src/analyzer/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 1b9ce5a4..96fb1a67 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -252,7 +252,7 @@ namespace win_emu->log.log("Using emulator: %s\n", win_emu->emu().get_name().c_str()); (void)&watch_system_objects; - watch_system_objects(*win_emu, options.modules, options.concise_logging); + watch_system_objects(*win_emu, options.modules, !options.verbose_logging); win_emu->buffer_stdout = options.buffer_stdout; if (options.silent) From 662db697a772f1084ed7c535446b04a1abe143e6 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 19 Apr 2025 08:13:31 +0200 Subject: [PATCH 052/542] Watch system objects everywhere --- src/analyzer/main.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 96fb1a67..353682e4 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -56,11 +56,6 @@ namespace void watch_system_objects(windows_emulator& win_emu, const std::set>& modules, const bool cache_logging) { - (void)win_emu; - (void)modules; - (void)cache_logging; - -#ifdef OS_WINDOWS watch_object(win_emu, modules, *win_emu.current_thread().teb, cache_logging); watch_object(win_emu, modules, win_emu.process.peb, cache_logging); watch_object(win_emu, modules, emulator_object{win_emu.emu(), kusd_mmio::address()}, @@ -84,7 +79,6 @@ namespace params_hook = watch_object(win_emu, modules, obj, cache_logging); } }); -#endif } bool read_yes_no_answer() From 2d7aecc3f40646d765209850bc698b5fd24cc323 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 19 Apr 2025 08:25:47 +0200 Subject: [PATCH 053/542] Fix warnings --- src/analyzer/object_watching.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyzer/object_watching.hpp b/src/analyzer/object_watching.hpp index 4d700d4c..79f94614 100644 --- a/src/analyzer/object_watching.hpp +++ b/src/analyzer/object_watching.hpp @@ -32,8 +32,8 @@ emulator_hook* watch_object(windows_emulator& emu, const std::set(offset)).c_str(), rip, + "Object access: %s - 0x%" PRIx64 " (%s) at 0x" PRIx64 " (%s)\n", i.get_type_name().c_str(), + offset, i.get_member_name(static_cast(offset)).c_str(), rip, mod ? mod->name.c_str() : ""); }); } From b5c37e6a928fa366ed1e580b596b2761d71f1b27 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 19 Apr 2025 08:25:58 +0200 Subject: [PATCH 054/542] Disable object watching for GCC --- src/analyzer/main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index 353682e4..ec943fa0 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -56,6 +56,11 @@ namespace void watch_system_objects(windows_emulator& win_emu, const std::set>& modules, const bool cache_logging) { + (void)win_emu; + (void)modules; + (void)cache_logging; + +#if !defined(__GNUC__) || defined(__clang__) watch_object(win_emu, modules, *win_emu.current_thread().teb, cache_logging); watch_object(win_emu, modules, win_emu.process.peb, cache_logging); watch_object(win_emu, modules, emulator_object{win_emu.emu(), kusd_mmio::address()}, @@ -79,6 +84,7 @@ namespace params_hook = watch_object(win_emu, modules, obj, cache_logging); } }); +#endif } bool read_yes_no_answer() From 3e53325c03481a2f6cc146bb4c88c2385bfa9ebd Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 19 Apr 2025 08:35:50 +0200 Subject: [PATCH 055/542] Fix compilation --- src/analyzer/object_watching.hpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/analyzer/object_watching.hpp b/src/analyzer/object_watching.hpp index 79f94614..dd77c4b3 100644 --- a/src/analyzer/object_watching.hpp +++ b/src/analyzer/object_watching.hpp @@ -2,6 +2,7 @@ #include "reflect_type_info.hpp" #include +#include template emulator_hook* watch_object(windows_emulator& emu, const std::set>& modules, @@ -31,9 +32,12 @@ emulator_hook* watch_object(windows_emulator& emu, const std::setname.c_str() : ""; + const auto& type_name = i.get_type_name(); + const auto member_name = i.get_member_name(static_cast(offset)); + emu.log.print(is_main_access ? color::green : color::dark_gray, - "Object access: %s - 0x%" PRIx64 " (%s) at 0x" PRIx64 " (%s)\n", i.get_type_name().c_str(), - offset, i.get_member_name(static_cast(offset)).c_str(), rip, - mod ? mod->name.c_str() : ""); + "Object access: %s - 0x%" PRIx64 " (%s) at 0x%" PRIx64 " (%s)\n", type_name.c_str(), offset, + member_name.c_str(), rip, mod_name); }); } From 5b09ec1aad83d6ac6a6813ef9af983a6d9db93bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 19 Apr 2025 06:58:06 +0000 Subject: [PATCH 056/542] Bump deps/reflect from `e85c958` to `68d8fd0` Bumps [deps/reflect](https://github.com/qlibs/reflect) from `e85c958` to `68d8fd0`. - [Release notes](https://github.com/qlibs/reflect/releases) - [Commits](https://github.com/qlibs/reflect/compare/e85c958d22b3ca3cb79cc91f7e547f2e5eb0872f...68d8fd0913711c1ac161af273f0bc1a4153601f8) --- updated-dependencies: - dependency-name: deps/reflect dependency-version: 68d8fd0913711c1ac161af273f0bc1a4153601f8 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- deps/reflect | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/reflect b/deps/reflect index e85c958d..68d8fd09 160000 --- a/deps/reflect +++ b/deps/reflect @@ -1 +1 @@ -Subproject commit e85c958d22b3ca3cb79cc91f7e547f2e5eb0872f +Subproject commit 68d8fd0913711c1ac161af273f0bc1a4153601f8 From 056eccd8c0475e6d50e32373dadccd91d9ffef7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 19 Apr 2025 06:58:07 +0000 Subject: [PATCH 057/542] Bump deps/googletest from `e90fe24` to `155b337` Bumps [deps/googletest](https://github.com/google/googletest) from `e90fe24` to `155b337`. - [Release notes](https://github.com/google/googletest/releases) - [Commits](https://github.com/google/googletest/compare/e90fe2485641bab0d6af4500192dc503384950d1...155b337c938a2953e5675f9dc18c99f05f4c85d0) --- updated-dependencies: - dependency-name: deps/googletest dependency-version: 155b337c938a2953e5675f9dc18c99f05f4c85d0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- deps/googletest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/googletest b/deps/googletest index e90fe248..155b337c 160000 --- a/deps/googletest +++ b/deps/googletest @@ -1 +1 @@ -Subproject commit e90fe2485641bab0d6af4500192dc503384950d1 +Subproject commit 155b337c938a2953e5675f9dc18c99f05f4c85d0 From a8d25b0a5c6163d11b5c53c11ea744c17013a8c6 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 19 Apr 2025 09:23:01 +0200 Subject: [PATCH 058/542] Save another DLL --- src/tools/create-root.bat | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/create-root.bat b/src/tools/create-root.bat index 79c92815..975b7bea 100644 --- a/src/tools/create-root.bat +++ b/src/tools/create-root.bat @@ -117,6 +117,7 @@ CALL :collect propsys.dll CALL :collect wintypes.dll CALL :collect slwga.dll CALL :collect sppc.dll +CALL :collect kernel.appcore.dll CALL :collect locale.nls From 30a750dea584bf97c468a1d3aac61a1194dc0f69 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 19 Apr 2025 09:41:27 +0200 Subject: [PATCH 059/542] Allow consuming up to 4gb of memory with emscripten --- cmake/compiler-env.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/compiler-env.cmake b/cmake/compiler-env.cmake index d9d09c5c..2840e536 100644 --- a/cmake/compiler-env.cmake +++ b/cmake/compiler-env.cmake @@ -100,6 +100,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") -sASSERTIONS -sWASM_BIGINT -sUSE_OFFSET_CONVERTER + -sMAXIMUM_MEMORY=4gb #-sEXCEPTION_CATCHING_ALLOWED=[..] -sEXIT_RUNTIME #-sASYNCIFY @@ -109,7 +110,6 @@ if(CMAKE_SYSTEM_NAME MATCHES "Emscripten") add_link_options( -lnodefs.js -sNODERAWFS=1 -sENVIRONMENT=node - -sMAXIMUM_MEMORY=4gb --pre-js ${CMAKE_CURRENT_LIST_DIR}/misc/node-pre-script.js ) else() From 3dfad0f78928a794d73cbe65ff1c765ec9824287 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sat, 19 Apr 2025 13:52:13 -0300 Subject: [PATCH 060/542] Add more atom-related syscalls --- src/windows-emulator/process_context.cpp | 76 ++++++++++++++++++++++ src/windows-emulator/process_context.hpp | 20 +++++- src/windows-emulator/syscalls.cpp | 81 ++++++++++++------------ 3 files changed, 136 insertions(+), 41 deletions(-) diff --git a/src/windows-emulator/process_context.cpp b/src/windows-emulator/process_context.cpp index b8d22677..36205f3e 100644 --- a/src/windows-emulator/process_context.cpp +++ b/src/windows-emulator/process_context.cpp @@ -236,3 +236,79 @@ handle process_context::create_thread(memory_manager& memory, const uint64_t sta this->callbacks_->on_create_thread(h, *thr); return h; } + +uint16_t process_context::add_or_find_atom(std::u16string name) +{ + uint16_t index = 0; + if (!atoms.empty()) + { + auto i = atoms.end(); + --i; + index = i->first + 1; + } + + std::optional last_entry{}; + for (auto& entry : atoms) + { + if (entry.second.name == name) + { + entry.second.ref_count++; + return entry.first; + } + + if (entry.first > 0) + { + if (!last_entry) + { + index = 0; + } + else + { + const auto diff = entry.first - *last_entry; + if (diff > 1) + { + index = *last_entry + 1; + } + } + } + + last_entry = entry.first; + } + + atoms[index] = {std::move(name), 1}; + + return index; +} + +bool process_context::delete_atom(const std::u16string& name) +{ + for (auto it = atoms.begin(); it != atoms.end(); ++it) + { + if (it->second.name == name) + { + if (--it->second.ref_count == 0) + { + atoms.erase(it); + } + return true; + } + } + + return false; +} + +bool process_context::delete_atom(uint16_t atom_id) +{ + const auto it = atoms.find(atom_id); + if (it == atoms.end()) + { + return false; + } + + if (--it->second.ref_count == 0) + { + atoms.erase(it); + } + + return true; +} diff --git a/src/windows-emulator/process_context.hpp b/src/windows-emulator/process_context.hpp index 1be428b3..ac42f5f1 100644 --- a/src/windows-emulator/process_context.hpp +++ b/src/windows-emulator/process_context.hpp @@ -36,6 +36,20 @@ struct process_context utils::optional_function on_thread_terminated{}; }; + struct atom_entry + { + std::u16string name; + uint32_t ref_count = 0; + + atom_entry(std::u16string n, uint32_t count) + : name(std::move(n)), + ref_count(count) + { + } + + atom_entry() = default; + }; + process_context(x64_emulator& emu, memory_manager& memory, utils::clock& clock, callbacks& cb) : callbacks_(&cb), base_allocator(emu), @@ -51,6 +65,10 @@ struct process_context handle create_thread(memory_manager& memory, uint64_t start_address, uint64_t argument, uint64_t stack_size, bool suspended); + uint16_t add_or_find_atom(std::u16string name); + bool delete_atom(const std::u16string& name); + bool delete_atom(uint16_t atom_id); + void serialize(utils::buffer_serializer& buffer) const; void deserialize(utils::buffer_deserializer& buffer); @@ -86,7 +104,7 @@ struct process_context handle_store ports{}; handle_store mutants{}; handle_store registry_keys{}; - std::map atoms{}; + std::map atoms{}; std::vector default_register_set{}; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index bf18f69c..536f02e5 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -525,47 +525,21 @@ namespace syscalls c.emu.read_memory(atom_name, name.data(), length); - uint16_t index = 0; - if (!c.proc.atoms.empty()) - { - auto i = c.proc.atoms.end(); - --i; - index = i->first + 1; - } - - std::optional last_entry{}; - for (auto& entry : c.proc.atoms) - { - if (entry.second == name) - { - if (atom) - { - atom.write(entry.first); - return STATUS_SUCCESS; - } - } - - if (entry.first > 0) - { - if (!last_entry) - { - index = 0; - } - else - { - const auto diff = entry.first - *last_entry; - if (diff > 1) - { - index = *last_entry + 1; - } - } - } - - last_entry = entry.first; - } - - c.proc.atoms[index] = std::move(name); + uint16_t index = c.proc.add_or_find_atom(name); atom.write(index); + + return STATUS_SUCCESS; + } + + NTSTATUS handle_NtAddAtom(const syscall_context& c, const uint64_t atom_name, const ULONG length, + const emulator_object atom) + { + return handle_NtAddAtomEx(c, atom_name, length, atom, 0); + } + + NTSTATUS handle_NtDeleteAtom(const syscall_context& c, const emulator_object atom) + { + c.proc.delete_atom(atom.read()); return STATUS_SUCCESS; } @@ -643,6 +617,29 @@ namespace syscalls { return 0; } + + template + struct CLSMENUNAME + { + LPSTR pszClientAnsiMenuName; + LPWSTR pwszClientUnicodeMenuName; + EMULATOR_CAST(typename Traits::PVOID, UNICODE_STRING*) pusMenuName; + }; + NTSTATUS handle_NtUserRegisterClassExWOW(const syscall_context& c, const emulator_object /*wnd_class_ex*/, + const emulator_object>> class_name, + const emulator_object>> /*class_version*/, + const emulator_object>> /*class_menu_name*/, + const DWORD /*function_id*/, const DWORD /*flags*/, const emulator_pointer /*wow*/) + { + uint16_t index = c.proc.add_or_find_atom(read_unicode_string(c.emu, class_name)); + return index; + } + + NTSTATUS handle_NtUserUnregisterClass(const syscall_context& c, const emulator_object>> class_name, + const emulator_pointer /*instance*/, const emulator_object>> /*class_menu_name*/) + { + return c.proc.delete_atom(read_unicode_string(c.emu, class_name)); + } } void syscall_dispatcher::add_handlers(std::map& handler_mapping) @@ -705,6 +702,8 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtQueryInformationToken); add_handler(NtDxgkIsFeatureEnabled); add_handler(NtAddAtomEx); + add_handler(NtAddAtom); + add_handler(NtDeleteAtom); add_handler(NtInitializeNlsFiles); add_handler(NtUnmapViewOfSection); add_handler(NtUnmapViewOfSectionEx); @@ -788,6 +787,8 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtCreateNamedPipeFile); add_handler(NtFsControlFile); add_handler(NtQueryFullAttributesFile); + add_handler(NtUserRegisterClassExWOW); + add_handler(NtUserUnregisterClass); #undef add_handler } From 6b1df674abed7313cdb93e196e15c9422a1e4b4c Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sat, 19 Apr 2025 15:07:12 -0300 Subject: [PATCH 061/542] Fix non-windows builds --- src/windows-emulator/syscalls.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 536f02e5..59164baa 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -621,11 +621,11 @@ namespace syscalls template struct CLSMENUNAME { - LPSTR pszClientAnsiMenuName; - LPWSTR pwszClientUnicodeMenuName; + EMULATOR_CAST(typename Traits::PVOID, char*) pszClientAnsiMenuName; + EMULATOR_CAST(typename Traits::PVOID, char16_t*) pwszClientUnicodeMenuName; EMULATOR_CAST(typename Traits::PVOID, UNICODE_STRING*) pusMenuName; }; - NTSTATUS handle_NtUserRegisterClassExWOW(const syscall_context& c, const emulator_object /*wnd_class_ex*/, + NTSTATUS handle_NtUserRegisterClassExWOW(const syscall_context& c, const emulator_pointer /*wnd_class_ex*/, const emulator_object>> class_name, const emulator_object>> /*class_version*/, const emulator_object>> /*class_menu_name*/, From 09b25d8c89d2cf1a1f2d5e78da30c51fcb560702 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sat, 19 Apr 2025 15:18:26 -0300 Subject: [PATCH 062/542] Fix NtDeleteAtom signature --- src/windows-emulator/syscalls.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 59164baa..720d6dab 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -537,9 +537,9 @@ namespace syscalls return handle_NtAddAtomEx(c, atom_name, length, atom, 0); } - NTSTATUS handle_NtDeleteAtom(const syscall_context& c, const emulator_object atom) + NTSTATUS handle_NtDeleteAtom(const syscall_context& c, const RTL_ATOM atom) { - c.proc.delete_atom(atom.read()); + c.proc.delete_atom(atom); return STATUS_SUCCESS; } From 88a744a7566d8dae92ba5bba7fc9999194e820dc Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sat, 19 Apr 2025 15:21:22 -0300 Subject: [PATCH 063/542] Hopefully fix formatting --- src/windows-emulator/syscalls.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 720d6dab..80e6fd06 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -625,18 +625,22 @@ namespace syscalls EMULATOR_CAST(typename Traits::PVOID, char16_t*) pwszClientUnicodeMenuName; EMULATOR_CAST(typename Traits::PVOID, UNICODE_STRING*) pusMenuName; }; + NTSTATUS handle_NtUserRegisterClassExWOW(const syscall_context& c, const emulator_pointer /*wnd_class_ex*/, const emulator_object>> class_name, const emulator_object>> /*class_version*/, const emulator_object>> /*class_menu_name*/, - const DWORD /*function_id*/, const DWORD /*flags*/, const emulator_pointer /*wow*/) + const DWORD /*function_id*/, const DWORD /*flags*/, + const emulator_pointer /*wow*/) { uint16_t index = c.proc.add_or_find_atom(read_unicode_string(c.emu, class_name)); return index; } - NTSTATUS handle_NtUserUnregisterClass(const syscall_context& c, const emulator_object>> class_name, - const emulator_pointer /*instance*/, const emulator_object>> /*class_menu_name*/) + NTSTATUS handle_NtUserUnregisterClass(const syscall_context& c, + const emulator_object>> class_name, + const emulator_pointer /*instance*/, + const emulator_object>> /*class_menu_name*/) { return c.proc.delete_atom(read_unicode_string(c.emu, class_name)); } From 5d19803020e876ca5d075f26f279893d9554f52b Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sat, 19 Apr 2025 16:29:47 -0300 Subject: [PATCH 064/542] Fix NtOpenSection --- src/windows-emulator/syscalls/section.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/windows-emulator/syscalls/section.cpp b/src/windows-emulator/syscalls/section.cpp index 3bcaaa90..bb0aa437 100644 --- a/src/windows-emulator/syscalls/section.cpp +++ b/src/windows-emulator/syscalls/section.cpp @@ -82,7 +82,8 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } - if (attributes.RootDirectory != KNOWN_DLLS_DIRECTORY) + if (attributes.RootDirectory != KNOWN_DLLS_DIRECTORY && + attributes.RootDirectory != BASE_NAMED_OBJECTS_DIRECTORY) { c.win_emu.log.error("Unsupported section\n"); c.emu.stop(); From c702bedaee6db0ba34b9e5c3866b8f223981d8c1 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sat, 19 Apr 2025 16:33:34 -0300 Subject: [PATCH 065/542] Add 3 new syscall handlers --- src/windows-emulator/syscalls.cpp | 15 ++++++++++++++- src/windows-emulator/syscalls/file.cpp | 20 +++++++++++++++++++- src/windows-emulator/syscalls/thread.cpp | 7 +++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index bf18f69c..77830139 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -109,6 +109,8 @@ namespace syscalls emulator_object>> io_status_block, ULONG fs_control_code, uint64_t input_buffer, ULONG input_buffer_length, uint64_t output_buffer, ULONG output_buffer_length); + NTSTATUS handle_NtFlushBuffersFile(const syscall_context& c, handle file_handle, + emulator_object>> /*io_status_block*/); // syscalls/locale.cpp: NTSTATUS handle_NtInitializeNlsFiles(const syscall_context& c, emulator_object base_address, @@ -269,6 +271,9 @@ namespace syscalls NTSTATUS handle_NtQueryInformationThread(const syscall_context& c, handle thread_handle, uint32_t info_class, uint64_t thread_information, uint32_t thread_information_length, emulator_object return_length); + NTSTATUS handle_NtOpenThread(const syscall_context&, handle thread_handle, ACCESS_MASK /*desired_access*/, + emulator_object>> /*object_attributes*/, + emulator_pointer /*client_id*/); NTSTATUS handle_NtOpenThreadToken(const syscall_context&, handle thread_handle, ACCESS_MASK /*desired_access*/, BOOLEAN /*open_as_self*/, emulator_object token_handle); NTSTATUS handle_NtOpenThreadTokenEx(const syscall_context& c, handle thread_handle, ACCESS_MASK desired_access, @@ -643,6 +648,11 @@ namespace syscalls { return 0; } + + NTSTATUS handle_NtUserGetProcessWindowStation() + { + return NULL; + } } void syscall_dispatcher::add_handlers(std::map& handler_mapping) @@ -663,6 +673,7 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtSetInformationVirtualMemory); add_handler(NtFreeVirtualMemory); add_handler(NtQueryVirtualMemory); + add_handler(NtOpenThread); add_handler(NtOpenThreadToken); add_handler(NtOpenThreadTokenEx); add_handler(NtQueryPerformanceCounter); @@ -788,6 +799,8 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtCreateNamedPipeFile); add_handler(NtFsControlFile); add_handler(NtQueryFullAttributesFile); + add_handler(NtFlushBuffersFile); + add_handler(NtUserGetProcessWindowStation); #undef add_handler -} +} \ No newline at end of file diff --git a/src/windows-emulator/syscalls/file.cpp b/src/windows-emulator/syscalls/file.cpp index 006e3ba2..730b6e9e 100644 --- a/src/windows-emulator/syscalls/file.cpp +++ b/src/windows-emulator/syscalls/file.cpp @@ -846,4 +846,22 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } -} + + NTSTATUS handle_NtFlushBuffersFile(const syscall_context& c, const handle file_handle, + const emulator_object>> /*io_status_block*/) + { + if (file_handle == STDOUT_HANDLE) + { + return STATUS_SUCCESS; + } + + const auto* f = c.proc.files.get(file_handle); + if (!f) + { + return STATUS_INVALID_HANDLE; + } + + (void)fflush(f->handle); + return STATUS_SUCCESS; + } +} \ No newline at end of file diff --git a/src/windows-emulator/syscalls/thread.cpp b/src/windows-emulator/syscalls/thread.cpp index 827dc0e1..4952759e 100644 --- a/src/windows-emulator/syscalls/thread.cpp +++ b/src/windows-emulator/syscalls/thread.cpp @@ -239,6 +239,13 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } + NTSTATUS handle_NtOpenThread(const syscall_context&, handle /*thread_handle*/, ACCESS_MASK /*desired_access*/, + emulator_object>> /*object_attributes*/, + emulator_pointer /*client_id*/) + { + return STATUS_NOT_SUPPORTED; + } + NTSTATUS handle_NtOpenThreadToken(const syscall_context&, const handle thread_handle, const ACCESS_MASK /*desired_access*/, const BOOLEAN /*open_as_self*/, const emulator_object token_handle) From d8f8bd07f9ef632b799b11578cdf53ffab39c159 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sat, 19 Apr 2025 17:12:27 -0300 Subject: [PATCH 066/542] Fix failed check --- src/windows-emulator/syscalls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 77830139..5e2e9aff 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -651,7 +651,7 @@ namespace syscalls NTSTATUS handle_NtUserGetProcessWindowStation() { - return NULL; + return 0; } } From 8e48793f47e71aa08cbd96b1483e030cf3a07319 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sat, 19 Apr 2025 17:12:41 -0300 Subject: [PATCH 067/542] Fix formatting --- src/windows-emulator/syscalls/file.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/windows-emulator/syscalls/file.cpp b/src/windows-emulator/syscalls/file.cpp index 730b6e9e..17e07e0c 100644 --- a/src/windows-emulator/syscalls/file.cpp +++ b/src/windows-emulator/syscalls/file.cpp @@ -847,8 +847,9 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } - NTSTATUS handle_NtFlushBuffersFile(const syscall_context& c, const handle file_handle, - const emulator_object>> /*io_status_block*/) + NTSTATUS handle_NtFlushBuffersFile( + const syscall_context& c, const handle file_handle, + const emulator_object>> /*io_status_block*/) { if (file_handle == STDOUT_HANDLE) { From e74b1dad5a619835e1cf77310f8d440e1e46f304 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sat, 19 Apr 2025 17:23:03 -0300 Subject: [PATCH 068/542] Fix formatting (attempt 2) --- src/windows-emulator/syscalls.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 80e6fd06..bd3126fc 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -626,12 +626,12 @@ namespace syscalls EMULATOR_CAST(typename Traits::PVOID, UNICODE_STRING*) pusMenuName; }; - NTSTATUS handle_NtUserRegisterClassExWOW(const syscall_context& c, const emulator_pointer /*wnd_class_ex*/, - const emulator_object>> class_name, - const emulator_object>> /*class_version*/, - const emulator_object>> /*class_menu_name*/, - const DWORD /*function_id*/, const DWORD /*flags*/, - const emulator_pointer /*wow*/) + NTSTATUS handle_NtUserRegisterClassExWOW( + const syscall_context& c, const emulator_pointer /*wnd_class_ex*/, + const emulator_object>> class_name, + const emulator_object>> /*class_version*/, + const emulator_object>> /*class_menu_name*/, const DWORD /*function_id*/, + const DWORD /*flags*/, const emulator_pointer /*wow*/) { uint16_t index = c.proc.add_or_find_atom(read_unicode_string(c.emu, class_name)); return index; From 763b8fc760d65eaca989ca95b1531ca6995f8e79 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sun, 20 Apr 2025 16:09:04 -0300 Subject: [PATCH 069/542] Improvements to registry syscalls --- src/common/platform/registry.hpp | 18 ++ src/common/utils/container.hpp | 31 +++ src/windows-emulator/registry/hive_parser.cpp | 27 ++- src/windows-emulator/registry/hive_parser.hpp | 48 ++++- .../registry/registry_manager.cpp | 35 +++- .../registry/registry_manager.hpp | 3 + src/windows-emulator/syscalls.cpp | 9 +- src/windows-emulator/syscalls/registry.cpp | 189 +++++++++++++++++- 8 files changed, 349 insertions(+), 11 deletions(-) diff --git a/src/common/platform/registry.hpp b/src/common/platform/registry.hpp index 7f76d0ae..a44e8b2e 100644 --- a/src/common/platform/registry.hpp +++ b/src/common/platform/registry.hpp @@ -34,6 +34,24 @@ struct KEY_NAME_INFORMATION char16_t Name[1]; }; +typedef struct _KEY_BASIC_INFORMATION +{ + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG NameLength; + char16_t Name[1]; +} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION; + +typedef struct _KEY_NODE_INFORMATION +{ + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG ClassOffset; + ULONG ClassLength; + ULONG NameLength; + char16_t Name[1]; +} KEY_NODE_INFORMATION, *PKEY_NODE_INFORMATION; + typedef struct _KEY_FULL_INFORMATION { LARGE_INTEGER LastWriteTime; diff --git a/src/common/utils/container.hpp b/src/common/utils/container.hpp index 2a7cb2f5..0877728c 100644 --- a/src/common/utils/container.hpp +++ b/src/common/utils/container.hpp @@ -18,8 +18,39 @@ namespace utils } }; + struct insensitive_string_hash + { + using is_transparent = void; + + size_t operator()(const std::string_view str) const + { + size_t hash = 0; + constexpr std::hash hasher{}; + for (unsigned char c : str) + { + hash ^= hasher(std::tolower(c)) + 0x9e3779b9 + (hash << 6) + (hash >> 2); + } + return hash; + } + }; + + struct insensitive_string_equal + { + using is_transparent = void; + + bool operator()(const std::string_view lhs, const std::string_view rhs) const + { + return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), + [](unsigned char c1, unsigned char c2) { return std::tolower(c1) == std::tolower(c2); }); + } + }; + template using unordered_string_map = std::unordered_map>; + template + using unordered_insensitive_string_map = + std::unordered_map; + using unordered_string_set = std::unordered_set>; } diff --git a/src/windows-emulator/registry/hive_parser.cpp b/src/windows-emulator/registry/hive_parser.cpp index c6892433..a328b2c9 100644 --- a/src/windows-emulator/registry/hive_parser.cpp +++ b/src/windows-emulator/registry/hive_parser.cpp @@ -159,6 +159,18 @@ const hive_value* hive_key::get_value(std::ifstream& file, const std::string_vie return &value; } +const hive_value* hive_key::get_value(std::ifstream& file, size_t index) +{ + this->parse(file); + + if (index < 0 || index >= values_by_index_.size()) + { + return nullptr; + } + + return get_value(file, values_by_index_[index]); +} + void hive_key::parse(std::ifstream& file) { if (this->parsed_) @@ -189,8 +201,11 @@ void hive_key::parse(std::ifstream& file) raw_value.data_offset = offset + static_cast(offsetof(value_block_t, offset)); } - utils::string::to_lower_inplace(value_name); - this->values_[std::move(value_name)] = std::move(raw_value); + const auto [it, inserted] = this->values_.emplace(std::move(value_name), std::move(raw_value)); + if (inserted) + { + this->values_by_index_.emplace_back(it->first); + } } // Subkeys @@ -212,9 +227,13 @@ void hive_key::parse(std::ifstream& file) const auto subkey = read_file_object(file, subkey_block_offset); std::string subkey_name(subkey.name, std::min(subkey.len, static_cast(sizeof(subkey.name)))); - utils::string::to_lower_inplace(subkey_name); - this->sub_keys_.emplace(std::move(subkey_name), hive_key{subkey.subkeys, subkey.value_count, subkey.offsets}); + const auto [it, inserted] = this->sub_keys_.emplace( + std::move(subkey_name), hive_key{subkey.subkeys, subkey.value_count, subkey.offsets}); + if (inserted) + { + this->sub_keys_by_index_.emplace_back(it->first); + } } } diff --git a/src/windows-emulator/registry/hive_parser.hpp b/src/windows-emulator/registry/hive_parser.hpp index 3677f821..011d0d7b 100644 --- a/src/windows-emulator/registry/hive_parser.hpp +++ b/src/windows-emulator/registry/hive_parser.hpp @@ -23,12 +23,24 @@ class hive_key { } - utils::unordered_string_map& get_sub_keys(std::ifstream& file) + utils::unordered_insensitive_string_map& get_sub_keys(std::ifstream& file) { this->parse(file); return this->sub_keys_; } + const std::string_view* get_sub_key_name(std::ifstream& file, size_t index) + { + this->parse(file); + + if (index < 0 || index >= sub_keys_by_index_.size()) + { + return nullptr; + } + + return &sub_keys_by_index_[index]; + } + hive_key* get_sub_key(std::ifstream& file, const std::string_view name) { auto& sub_keys = this->get_sub_keys(file); @@ -42,7 +54,13 @@ class hive_key return &entry->second; } + hive_key* get_sub_key(std::ifstream& file, size_t index) + { + return get_sub_key(file, *this->get_sub_key_name(file, index)); + } + const hive_value* get_value(std::ifstream& file, std::string_view name); + const hive_value* get_value(std::ifstream& file, size_t index); private: struct raw_hive_value : hive_value @@ -53,8 +71,10 @@ class hive_key }; bool parsed_{false}; - utils::unordered_string_map sub_keys_{}; - utils::unordered_string_map values_{}; + utils::unordered_insensitive_string_map sub_keys_{}; + std::vector sub_keys_by_index_{}; + utils::unordered_insensitive_string_map values_{}; + std::vector values_by_index_{}; const int subkey_block_offset_{}; const int value_count_{}; @@ -85,6 +105,17 @@ class hive_parser return current_key; } + [[nodiscard]] const std::string_view* get_sub_key_name(const std::filesystem::path& key, size_t index) + { + auto* target_key = this->get_sub_key(key); + if (!target_key) + { + return nullptr; + } + + return target_key->get_sub_key_name(this->file_, index); + } + [[nodiscard]] const hive_value* get_value(const std::filesystem::path& key, const std::string_view name) { auto* sub_key = this->get_sub_key(key); @@ -96,6 +127,17 @@ class hive_parser return sub_key->get_value(this->file_, name); } + [[nodiscard]] const hive_value* get_value(const std::filesystem::path& key, size_t index) + { + auto* sub_key = this->get_sub_key(key); + if (!sub_key) + { + return nullptr; + } + + return sub_key->get_value(this->file_, index); + } + private: std::ifstream file_{}; hive_key root_key_; diff --git a/src/windows-emulator/registry/registry_manager.cpp b/src/windows-emulator/registry/registry_manager.cpp index 9a822569..f09199df 100644 --- a/src/windows-emulator/registry/registry_manager.cpp +++ b/src/windows-emulator/registry/registry_manager.cpp @@ -116,8 +116,6 @@ std::optional registry_manager::get_key(const utils::path_key& key std::optional registry_manager::get_value(const registry_key& key, std::string name) { - utils::string::to_lower_inplace(name); - const auto iterator = this->hives_.find(key.hive); if (iterator == this->hives_.end()) { @@ -138,6 +136,28 @@ std::optional registry_manager::get_value(const registry_key& ke return v; } +std::optional registry_manager::get_value(const registry_key& key, size_t index) +{ + const auto iterator = this->hives_.find(key.hive); + if (iterator == this->hives_.end()) + { + return std::nullopt; + } + + const auto* entry = iterator->second->get_value(key.path.get(), index); + if (!entry) + { + return std::nullopt; + } + + registry_value v{}; + v.type = entry->type; + v.name = entry->name; + v.data = entry->data; + + return v; +} + registry_manager::hive_map::iterator registry_manager::find_hive(const utils::path_key& key) { for (auto i = this->hives_.begin(); i != this->hives_.end(); ++i) @@ -150,3 +170,14 @@ registry_manager::hive_map::iterator registry_manager::find_hive(const utils::pa return this->hives_.end(); } + +std::optional registry_manager::get_sub_key_name(const registry_key& key, size_t index) +{ + const auto iterator = this->hives_.find(key.hive); + if (iterator == this->hives_.end()) + { + return std::nullopt; + } + + return *iterator->second->get_sub_key_name(key.path.get(), index); +} \ No newline at end of file diff --git a/src/windows-emulator/registry/registry_manager.hpp b/src/windows-emulator/registry/registry_manager.hpp index c041d2f2..cd62c7d7 100644 --- a/src/windows-emulator/registry/registry_manager.hpp +++ b/src/windows-emulator/registry/registry_manager.hpp @@ -48,6 +48,9 @@ class registry_manager std::optional get_key(const utils::path_key& key); std::optional get_value(const registry_key& key, std::string name); + std::optional get_value(const registry_key& key, size_t index); + + std::optional get_sub_key_name(const registry_key& key, size_t index); private: std::filesystem::path hive_path_{}; diff --git a/src/windows-emulator/syscalls.cpp b/src/windows-emulator/syscalls.cpp index 605f000e..d62447cd 100644 --- a/src/windows-emulator/syscalls.cpp +++ b/src/windows-emulator/syscalls.cpp @@ -220,7 +220,13 @@ namespace syscalls NTSTATUS handle_NtCreateKey(); NTSTATUS handle_NtNotifyChangeKey(); NTSTATUS handle_NtSetInformationKey(); - NTSTATUS handle_NtEnumerateKey(); + NTSTATUS handle_NtEnumerateKey(const syscall_context& c, handle key_handle, ULONG index, + KEY_INFORMATION_CLASS key_information_class, uint64_t key_information, ULONG length, + emulator_object result_length); + NTSTATUS handle_NtEnumerateValueKey(const syscall_context& c, handle key_handle, ULONG index, + KEY_VALUE_INFORMATION_CLASS key_value_information_class, + uint64_t key_value_information, ULONG length, + emulator_object result_length); // syscalls/section.cpp: NTSTATUS handle_NtCreateSection(const syscall_context& c, emulator_object section_handle, @@ -778,6 +784,7 @@ void syscall_dispatcher::add_handlers(std::map& ha add_handler(NtUserGetDpiForCurrentProcess); add_handler(NtReleaseSemaphore); add_handler(NtEnumerateKey); + add_handler(NtEnumerateValueKey); add_handler(NtAlpcConnectPort); add_handler(NtGetNextThread); add_handler(NtSetInformationObject); diff --git a/src/windows-emulator/syscalls/registry.cpp b/src/windows-emulator/syscalls/registry.cpp index d383335a..be62249f 100644 --- a/src/windows-emulator/syscalls/registry.cpp +++ b/src/windows-emulator/syscalls/registry.cpp @@ -242,8 +242,195 @@ namespace syscalls return STATUS_NOT_SUPPORTED; } - NTSTATUS handle_NtEnumerateKey() + NTSTATUS handle_NtEnumerateKey(const syscall_context& c, const handle key_handle, const ULONG index, + const KEY_INFORMATION_CLASS key_information_class, const uint64_t key_information, + const ULONG length, const emulator_object result_length) { + const auto* key = c.proc.registry_keys.get(key_handle); + if (!key) + { + return STATUS_INVALID_HANDLE; + } + + const auto subkey_name = c.win_emu.registry.get_sub_key_name(*key, index); + if (!subkey_name) + { + return STATUS_NO_MORE_ENTRIES; + } + + const std::u16string subkey_name_u16(subkey_name->begin(), subkey_name->end()); + + if (key_information_class == KeyBasicInformation) + { + constexpr auto base_size = offsetof(KEY_BASIC_INFORMATION, Name); + const auto name_size = subkey_name_u16.size() * 2; + const auto required_size = base_size + name_size; + result_length.write(static_cast(required_size)); + + KEY_BASIC_INFORMATION info{}; + info.LastWriteTime.QuadPart = 0; + info.TitleIndex = 0; + info.NameLength = static_cast(name_size); + + if (base_size <= length) + { + c.emu.write_memory(key_information, &info, base_size); + } + + if (required_size > length) + { + return STATUS_BUFFER_OVERFLOW; + } + + c.emu.write_memory(key_information + base_size, subkey_name_u16.data(), name_size); + + return STATUS_SUCCESS; + } + + if (key_information_class == KeyNodeInformation) + { + constexpr auto base_size = offsetof(KEY_NODE_INFORMATION, Name); + const auto name_size = subkey_name_u16.size() * 2; + constexpr auto class_size = 0; // TODO: Class Name + const auto required_size = base_size + name_size + class_size; + result_length.write(static_cast(required_size)); + + KEY_NODE_INFORMATION info{}; + info.LastWriteTime.QuadPart = 0; + info.TitleIndex = 0; + info.ClassOffset = static_cast(base_size + name_size); + info.ClassLength = static_cast(class_size); + info.NameLength = static_cast(name_size); + + if (base_size <= length) + { + c.emu.write_memory(key_information, &info, base_size); + } + + if (required_size > length) + { + return STATUS_BUFFER_OVERFLOW; + } + + c.emu.write_memory(key_information + base_size, subkey_name_u16.data(), name_size); + // TODO: Write Class Name + + return STATUS_SUCCESS; + } + + c.win_emu.log.print(color::gray, "Unsupported registry enumeration class: %X\n", key_information_class); + return STATUS_NOT_SUPPORTED; + } + + NTSTATUS handle_NtEnumerateValueKey(const syscall_context& c, const handle key_handle, const ULONG index, + const KEY_VALUE_INFORMATION_CLASS key_value_information_class, + const uint64_t key_value_information, const ULONG length, + const emulator_object result_length) + { + const auto* key = c.proc.registry_keys.get(key_handle); + if (!key) + { + return STATUS_INVALID_HANDLE; + } + + const auto value = c.win_emu.registry.get_value(*key, index); + if (!value) + { + return STATUS_NO_MORE_ENTRIES; + } + + const std::u16string value_name_u16(value->name.begin(), value->name.end()); + + if (key_value_information_class == KeyValueBasicInformation) + { + constexpr auto base_size = offsetof(KEY_VALUE_BASIC_INFORMATION, Name); + const auto name_size = value_name_u16.size() * 2; + const auto required_size = base_size + name_size; + result_length.write(static_cast(required_size)); + + KEY_VALUE_BASIC_INFORMATION info{}; + info.TitleIndex = 0; + info.Type = value->type; + info.NameLength = static_cast(name_size); + + if (base_size <= length) + { + c.emu.write_memory(key_value_information, &info, base_size); + } + + if (required_size > length) + { + return STATUS_BUFFER_OVERFLOW; + } + + c.emu.write_memory(key_value_information + base_size, value_name_u16.data(), name_size); + + return STATUS_SUCCESS; + } + + if (key_value_information_class == KeyValuePartialInformation) + { + constexpr auto base_size = offsetof(KEY_VALUE_PARTIAL_INFORMATION, Data); + const auto data_size = value->data.size(); + const auto required_size = base_size + data_size; + result_length.write(static_cast(required_size)); + + KEY_VALUE_PARTIAL_INFORMATION info{}; + info.TitleIndex = 0; + info.Type = value->type; + info.DataLength = static_cast(data_size); + + if (base_size <= length) + { + c.emu.write_memory(key_value_information, &info, base_size); + } + + if (required_size > length) + { + return STATUS_BUFFER_OVERFLOW; + } + + c.emu.write_memory(key_value_information + base_size, value->data.data(), data_size); + + return STATUS_SUCCESS; + } + + if (key_value_information_class == KeyValueFullInformation) + { + constexpr auto base_size = offsetof(KEY_VALUE_FULL_INFORMATION, Name); + const auto name_size = value_name_u16.size() * 2; + const auto data_size = value->data.size(); + const ULONG data_offset = static_cast(base_size + name_size); + const auto required_size = data_offset + data_size; + + result_length.write(static_cast(required_size)); + + KEY_VALUE_FULL_INFORMATION info{}; + info.TitleIndex = 0; + info.Type = value->type; + info.DataOffset = data_offset; + info.DataLength = static_cast(data_size); + info.NameLength = static_cast(name_size); + + if (base_size <= length) + { + c.emu.write_memory(key_value_information, &info, base_size); + } + + if (required_size > length) + { + return STATUS_BUFFER_OVERFLOW; + } + + c.emu.write_memory(key_value_information + base_size, value_name_u16.data(), name_size); + + c.emu.write_memory(key_value_information + data_offset, value->data.data(), data_size); + + return STATUS_SUCCESS; + } + + c.win_emu.log.print(color::gray, "Unsupported registry value enumeration class: %X\n", + key_value_information_class); return STATUS_NOT_SUPPORTED; } } From 140d1dd5b35512379a8701f2e7a8a6a1af1f281e Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sun, 20 Apr 2025 16:26:08 -0300 Subject: [PATCH 070/542] Fix failing checks --- src/windows-emulator/registry/hive_parser.cpp | 2 +- src/windows-emulator/registry/hive_parser.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/windows-emulator/registry/hive_parser.cpp b/src/windows-emulator/registry/hive_parser.cpp index a328b2c9..3388b3c7 100644 --- a/src/windows-emulator/registry/hive_parser.cpp +++ b/src/windows-emulator/registry/hive_parser.cpp @@ -163,7 +163,7 @@ const hive_value* hive_key::get_value(std::ifstream& file, size_t index) { this->parse(file); - if (index < 0 || index >= values_by_index_.size()) + if (index >= values_by_index_.size()) { return nullptr; } diff --git a/src/windows-emulator/registry/hive_parser.hpp b/src/windows-emulator/registry/hive_parser.hpp index 011d0d7b..0db9979b 100644 --- a/src/windows-emulator/registry/hive_parser.hpp +++ b/src/windows-emulator/registry/hive_parser.hpp @@ -33,7 +33,7 @@ class hive_key { this->parse(file); - if (index < 0 || index >= sub_keys_by_index_.size()) + if (index >= sub_keys_by_index_.size()) { return nullptr; } From 5eba6d5e8d6e6aaccda21205d24b60af340f2750 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sun, 20 Apr 2025 16:44:12 -0300 Subject: [PATCH 071/542] Fix clang-tidy warning --- src/windows-emulator/registry/registry_manager.cpp | 2 +- src/windows-emulator/registry/registry_manager.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/windows-emulator/registry/registry_manager.cpp b/src/windows-emulator/registry/registry_manager.cpp index f09199df..ca3a21c3 100644 --- a/src/windows-emulator/registry/registry_manager.cpp +++ b/src/windows-emulator/registry/registry_manager.cpp @@ -114,7 +114,7 @@ std::optional registry_manager::get_key(const utils::path_key& key return {std::move(reg_key)}; } -std::optional registry_manager::get_value(const registry_key& key, std::string name) +std::optional registry_manager::get_value(const registry_key& key, const std::string& name) { const auto iterator = this->hives_.find(key.hive); if (iterator == this->hives_.end()) diff --git a/src/windows-emulator/registry/registry_manager.hpp b/src/windows-emulator/registry/registry_manager.hpp index cd62c7d7..db45e733 100644 --- a/src/windows-emulator/registry/registry_manager.hpp +++ b/src/windows-emulator/registry/registry_manager.hpp @@ -47,7 +47,7 @@ class registry_manager registry_manager& operator=(const registry_manager&) = delete; std::optional get_key(const utils::path_key& key); - std::optional get_value(const registry_key& key, std::string name); + std::optional get_value(const registry_key& key, const std::string& name); std::optional get_value(const registry_key& key, size_t index); std::optional get_sub_key_name(const registry_key& key, size_t index); From 7dbf498ea9425a062cc54700fbfa8044c0463554 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sun, 20 Apr 2025 17:08:54 -0300 Subject: [PATCH 072/542] Fix clang-tidy warning (attempt 2) --- src/common/utils/container.hpp | 4 ++-- src/windows-emulator/syscalls/registry.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/utils/container.hpp b/src/common/utils/container.hpp index 0877728c..9c1ae8aa 100644 --- a/src/common/utils/container.hpp +++ b/src/common/utils/container.hpp @@ -40,8 +40,8 @@ namespace utils bool operator()(const std::string_view lhs, const std::string_view rhs) const { - return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), - [](unsigned char c1, unsigned char c2) { return std::tolower(c1) == std::tolower(c2); }); + return std::ranges::equal( + lhs, rhs, [](unsigned char c1, unsigned char c2) { return std::tolower(c1) == std::tolower(c2); }); } }; diff --git a/src/windows-emulator/syscalls/registry.cpp b/src/windows-emulator/syscalls/registry.cpp index be62249f..f27190b6 100644 --- a/src/windows-emulator/syscalls/registry.cpp +++ b/src/windows-emulator/syscalls/registry.cpp @@ -400,7 +400,7 @@ namespace syscalls constexpr auto base_size = offsetof(KEY_VALUE_FULL_INFORMATION, Name); const auto name_size = value_name_u16.size() * 2; const auto data_size = value->data.size(); - const ULONG data_offset = static_cast(base_size + name_size); + const auto data_offset = static_cast(base_size + name_size); const auto required_size = data_offset + data_size; result_length.write(static_cast(required_size)); From c59002e2632d1193ba522fc8debab4fd0b308e81 Mon Sep 17 00:00:00 2001 From: Igor Pissolati Date: Sun, 20 Apr 2025 18:34:20 -0300 Subject: [PATCH 073/542] Fix null dereference issue --- src/windows-emulator/registry/registry_manager.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/windows-emulator/registry/registry_manager.cpp b/src/windows-emulator/registry/registry_manager.cpp index ca3a21c3..94df07be 100644 --- a/src/windows-emulator/registry/registry_manager.cpp +++ b/src/windows-emulator/registry/registry_manager.cpp @@ -179,5 +179,11 @@ std::optional registry_manager::get_sub_key_name(const registr return std::nullopt; } - return *iterator->second->get_sub_key_name(key.path.get(), index); -} \ No newline at end of file + const auto* name = iterator->second->get_sub_key_name(key.path.get(), index); + if (!name) + { + return std::nullopt; + } + + return *name; +} From 0aae376a511c0b7f979106fa3f29196fd3d193c5 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Mon, 21 Apr 2025 17:54:40 +0200 Subject: [PATCH 074/542] Add new site --- page/.gitignore | 24 + page/bigboiii.webp | Bin 51370 -> 0 bytes page/components.json | 21 + page/eslint.config.js | 28 + page/index.html | 524 +- page/noise.webp | Bin 37576 -> 0 bytes page/package-lock.json | 4872 +++++++++++++++++ page/package.json | 49 + .../emulator-worker.js} | 27 +- page/src/App.css | 70 + page/src/App.tsx | 126 + page/src/components/app-sidebar.tsx | 53 + page/src/components/output.tsx | 244 + page/src/components/settings-menu.tsx | 72 + page/src/components/status-indicator.tsx | 21 + page/src/components/theme-provider.tsx | 73 + page/src/components/ui/badge.tsx | 46 + page/src/components/ui/breadcrumb.tsx | 109 + page/src/components/ui/button.tsx | 59 + page/src/components/ui/card.tsx | 92 + page/src/components/ui/checkbox.tsx | 30 + page/src/components/ui/input.tsx | 21 + page/src/components/ui/label.tsx | 22 + page/src/components/ui/popover.tsx | 46 + page/src/components/ui/scroll-area.tsx | 56 + page/src/components/ui/separator.tsx | 28 + page/src/components/ui/sheet.tsx | 137 + page/src/components/ui/sidebar.tsx | 724 +++ page/src/components/ui/skeleton.tsx | 13 + page/src/components/ui/tooltip.tsx | 61 + page/src/emulator.ts | 71 + page/src/filesystem.ts | 92 + page/src/hooks/use-mobile.ts | 19 + page/src/index.css | 120 + page/src/lib/utils.ts | 6 + page/src/main.tsx | 10 + page/src/settings.ts | 37 + page/src/vite-env.d.ts | 1 + page/src/zip-file.ts | 33 + page/tsconfig.app.json | 32 + page/tsconfig.json | 19 + page/tsconfig.node.json | 24 + page/vite.config.ts | 14 + page/why-eclipse.svg | 20 - page/why-eclipse2.svg | 20 - 45 files changed, 7610 insertions(+), 556 deletions(-) create mode 100644 page/.gitignore delete mode 100644 page/bigboiii.webp create mode 100644 page/components.json create mode 100644 page/eslint.config.js delete mode 100644 page/noise.webp create mode 100644 page/package-lock.json create mode 100644 page/package.json rename page/{emulator.js => public/emulator-worker.js} (62%) create mode 100644 page/src/App.css create mode 100644 page/src/App.tsx create mode 100644 page/src/components/app-sidebar.tsx create mode 100644 page/src/components/output.tsx create mode 100644 page/src/components/settings-menu.tsx create mode 100644 page/src/components/status-indicator.tsx create mode 100644 page/src/components/theme-provider.tsx create mode 100644 page/src/components/ui/badge.tsx create mode 100644 page/src/components/ui/breadcrumb.tsx create mode 100644 page/src/components/ui/button.tsx create mode 100644 page/src/components/ui/card.tsx create mode 100644 page/src/components/ui/checkbox.tsx create mode 100644 page/src/components/ui/input.tsx create mode 100644 page/src/components/ui/label.tsx create mode 100644 page/src/components/ui/popover.tsx create mode 100644 page/src/components/ui/scroll-area.tsx create mode 100644 page/src/components/ui/separator.tsx create mode 100644 page/src/components/ui/sheet.tsx create mode 100644 page/src/components/ui/sidebar.tsx create mode 100644 page/src/components/ui/skeleton.tsx create mode 100644 page/src/components/ui/tooltip.tsx create mode 100644 page/src/emulator.ts create mode 100644 page/src/filesystem.ts create mode 100644 page/src/hooks/use-mobile.ts create mode 100644 page/src/index.css create mode 100644 page/src/lib/utils.ts create mode 100644 page/src/main.tsx create mode 100644 page/src/settings.ts create mode 100644 page/src/vite-env.d.ts create mode 100644 page/src/zip-file.ts create mode 100644 page/tsconfig.app.json create mode 100644 page/tsconfig.json create mode 100644 page/tsconfig.node.json create mode 100644 page/vite.config.ts delete mode 100644 page/why-eclipse.svg delete mode 100644 page/why-eclipse2.svg diff --git a/page/.gitignore b/page/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/page/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/page/bigboiii.webp b/page/bigboiii.webp deleted file mode 100644 index 15089347cf42833e160b7a42568e7b188d8ed2b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51370 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 Y1B#d!=mnm1VcPX!{fi6nzIVA901n?YPyhe` diff --git a/page/components.json b/page/components.json new file mode 100644 index 00000000..73afbdbc --- /dev/null +++ b/page/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/page/eslint.config.js b/page/eslint.config.js new file mode 100644 index 00000000..092408a9 --- /dev/null +++ b/page/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/page/index.html b/page/index.html index 8564196d..5285eb18 100644 --- a/page/index.html +++ b/page/index.html @@ -1,511 +1,13 @@ - - - - - - - Windows User Space Emulator - - - - - - -
- - - -
-
-
-
-
-
-
-
- Windows User Space Emulator -
-
-
-
-
- -
- - - \ No newline at end of file + + + + + + + Windows User Space Emulator + + +
+ + + diff --git a/page/noise.webp b/page/noise.webp deleted file mode 100644 index 50fa598461e833741b635f25457513460578983d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37576 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 diff --git a/page/package-lock.json b/page/package-lock.json new file mode 100644 index 00000000..d9b743b6 --- /dev/null +++ b/page/package-lock.json @@ -0,0 +1,4872 @@ +{ + "name": "emulator-ui", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "emulator-ui", + "version": "0.0.0", + "dependencies": { + "@radix-ui/react-checkbox": "^1.2.2", + "@radix-ui/react-dialog": "^1.1.10", + "@radix-ui/react-icons": "^1.3.2", + "@radix-ui/react-label": "^2.1.4", + "@radix-ui/react-popover": "^1.1.10", + "@radix-ui/react-scroll-area": "^1.2.5", + "@radix-ui/react-separator": "^1.1.4", + "@radix-ui/react-slot": "^1.2.0", + "@radix-ui/react-tooltip": "^1.2.3", + "@tailwindcss/vite": "^4.1.4", + "@types/react-window": "^1.8.8", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "jszip": "^3.10.1", + "lucide-react": "^0.501.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-window": "^1.8.11", + "tailwind-merge": "^3.2.0", + "tailwindcss": "^4.1.4", + "tw-animate-css": "^1.2.5" + }, + "devDependencies": { + "@eslint/js": "^9.22.0", + "@types/node": "^22.14.1", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.22.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "typescript": "~5.7.2", + "typescript-eslint": "^8.26.1", + "vite": "^6.3.1" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", + "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz", + "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", + "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.25.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.0.tgz", + "integrity": "sha512-iWhsUS8Wgxz9AXNfvfOPFSW4VfMXdVhp1hjkZVhXCrpgh/aLcc45rX6MPu+tIVUWDw0HfNwth7O28M1xDxNf9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.13.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", + "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.9" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.4.tgz", + "integrity": "sha512-qz+fxrqgNxG0dYew5l7qR3c7wdgRu1XVUHGnGYX7rg5HM4p9SWaRmJwfgR3J0SgyUKayLmzQIun+N6rWRgiRKw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.2.2.tgz", + "integrity": "sha512-pMxzQLK+m/tkDRXJg7VUjRx6ozsBdzNLOV4vexfVBU57qT2Gvf4cw2gKKhOohJxjadQ+WcUXCKosTIxcZzi03A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.3", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.10.tgz", + "integrity": "sha512-m6pZb0gEM5uHPSb+i2nKKGQi/HMSVjARMsLMWQfKDP+eJ6B+uqryHnXhpnohTWElw+vEcMk/o4wJODtdRKHwqg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.4", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.6", + "@radix-ui/react-presence": "1.1.3", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.7.tgz", + "integrity": "sha512-j5+WBUdhccJsmH5/H0K6RncjDtoALSEr6jbkaZu+bjw6hOPOhHycr6vEUujl+HBK8kjUfWcoCJXxP6e4lUlMZw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", + "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.4.tgz", + "integrity": "sha512-r2annK27lIW5w9Ho5NyQgqs0MmgZSTIKXWpVCJaLC1q2kZrZkcqnmHkCHMEmv8XLvsLlurKMPT+kbKkRkm/xVA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-icons": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.2.tgz", + "integrity": "sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==", + "license": "MIT", + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.4.tgz", + "integrity": "sha512-wy3dqizZnZVV4ja0FNnUhIWNwWdoldXrneEyUcVtLYDAt8ovGS4ridtMAOGgXBBIfggL4BOveVWsjXDORdGEQg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.10.tgz", + "integrity": "sha512-IZN7b3sXqajiPsOzKuNJBSP9obF4MX5/5UhTgWNofw4r1H+eATWb0SyMlaxPD/kzA4vadFgy1s7Z1AEJ6WMyHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-focus-scope": "1.1.4", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.4", + "@radix-ui/react-portal": "1.1.6", + "@radix-ui/react-presence": "1.1.3", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.4.tgz", + "integrity": "sha512-3p2Rgm/a1cK0r/UVkx5F/K9v/EplfjAeIFCGOPYPO4lZ0jtg4iSQXt/YGTSLWaf4x7NG6Z4+uKFcylcTZjeqDA==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.6.tgz", + "integrity": "sha512-XmsIl2z1n/TsYFLIdYam2rmFwf9OC/Sh2avkbmVMDuBZIe7hSpM0cYnWPAo7nHOVx8zTuwDZGByfcqLdnzp3Vw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.3.tgz", + "integrity": "sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", + "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.5.tgz", + "integrity": "sha512-VyLjxI8/gXYn+Wij1FLpXjZp6Z/uNklUFQQ75tOpJNESeNaZ2kCRfjiEDmHgWmLeUPeJGwrqbgRmcdFjtYEkMA==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.3", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.4.tgz", + "integrity": "sha512-2fTm6PSiUm8YPq9W0E4reYuv01EE3aFSzt8edBiXqPHshF8N9+Kymt/k0/R+F3dkY5lQyB/zPtrP82phskLi7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", + "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.3.tgz", + "integrity": "sha512-0KX7jUYFA02np01Y11NWkk6Ip6TqMNmD4ijLelYAzeIndl2aVeltjJFJ2gwjNa1P8U/dgjQ+8cr9Y3Ni+ZNoRA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.4", + "@radix-ui/react-portal": "1.1.6", + "@radix-ui/react-presence": "1.1.3", + "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.0.tgz", + "integrity": "sha512-rQj0aAWOpCdCMRbI6pLQm8r7S2BM3YhTa0SzOYD55k+hJA8oo9J+H+9wLM9oMlZWOX/wJWPTzfDfmZkf7LvCfg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", + "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", + "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", + "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", + "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", + "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", + "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", + "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", + "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", + "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", + "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", + "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", + "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", + "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", + "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", + "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", + "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", + "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", + "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.4.tgz", + "integrity": "sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw==", + "license": "MIT", + "dependencies": { + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.29.2", + "tailwindcss": "4.1.4" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.4.tgz", + "integrity": "sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.4", + "@tailwindcss/oxide-darwin-arm64": "4.1.4", + "@tailwindcss/oxide-darwin-x64": "4.1.4", + "@tailwindcss/oxide-freebsd-x64": "4.1.4", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.4", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.4", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.4", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.4", + "@tailwindcss/oxide-linux-x64-musl": "4.1.4", + "@tailwindcss/oxide-wasm32-wasi": "4.1.4", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.4", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.4" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.4.tgz", + "integrity": "sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.4.tgz", + "integrity": "sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.4.tgz", + "integrity": "sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.4.tgz", + "integrity": "sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.4.tgz", + "integrity": "sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.4.tgz", + "integrity": "sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.4.tgz", + "integrity": "sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.4.tgz", + "integrity": "sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.4.tgz", + "integrity": "sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.4.tgz", + "integrity": "sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.0", + "@emnapi/runtime": "^1.4.0", + "@emnapi/wasi-threads": "^1.0.1", + "@napi-rs/wasm-runtime": "^0.2.8", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.4.tgz", + "integrity": "sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.4.tgz", + "integrity": "sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.4.tgz", + "integrity": "sha512-4UQeMrONbvrsXKXXp/uxmdEN5JIJ9RkH7YVzs6AMxC/KC1+Np7WZBaNIco7TEjlkthqxZbt8pU/ipD+hKjm80A==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.4", + "@tailwindcss/oxide": "4.1.4", + "tailwindcss": "4.1.4" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", + "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.2.tgz", + "integrity": "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/react-window": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz", + "integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==", + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", + "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/type-utils": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", + "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", + "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", + "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", + "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", + "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", + "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", + "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.0.tgz", + "integrity": "sha512-x/EztcTKVj+TDeANY1WjNeYsvZjZdfWRMP/KXi5Yn8BoTzpa13ZltaQqKfvWYbX8CE10GOHHdC5v86jY9x8i/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.10", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001714", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001714.tgz", + "integrity": "sha512-mtgapdwDLSSBnCI3JokHM7oEQBLxiJKVRtg10AxM1AyeiKcM96f0Mkbqeq+1AbiCtvMcHRulAAEMu693JrSWqg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.139", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.139.tgz", + "integrity": "sha512-GGnRYOTdN5LYpwbIr0rwP/ZHOQSvAF6TG0LSzp28uCBb9JiXHJGmaaKw29qjNJc5bGnnp6kXJqRnGMQoELwi5w==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/esbuild": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.25.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.0.tgz", + "integrity": "sha512-MsBdObhM4cEwkzCiraDv7A6txFXEqtNXOb877TsSp2FCkBNl8JfVQrmiuDqC1IkejT6JLPzYBXx/xAiYhyzgGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.13.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.25.0", + "@eslint/plugin-kit": "^0.2.8", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz", + "integrity": "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", + "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lightningcss": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", + "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.2", + "lightningcss-darwin-x64": "1.29.2", + "lightningcss-freebsd-x64": "1.29.2", + "lightningcss-linux-arm-gnueabihf": "1.29.2", + "lightningcss-linux-arm64-gnu": "1.29.2", + "lightningcss-linux-arm64-musl": "1.29.2", + "lightningcss-linux-x64-gnu": "1.29.2", + "lightningcss-linux-x64-musl": "1.29.2", + "lightningcss-win32-arm64-msvc": "1.29.2", + "lightningcss-win32-x64-msvc": "1.29.2" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", + "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", + "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", + "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", + "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", + "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", + "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", + "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", + "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", + "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", + "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.501.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.501.0.tgz", + "integrity": "sha512-E2KoyhW59fCb/yUbR3rbDer83fqn7a8NG91ZhIot2yWaPHjPyGzzsNKh40N//GezYShAuycf3TcQksRQznIsRw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", + "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-window": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.11.tgz", + "integrity": "sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.0.0", + "memoize-one": ">=3.1.1 <6" + }, + "engines": { + "node": ">8.0.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwind-merge": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.2.0.tgz", + "integrity": "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.4.tgz", + "integrity": "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", + "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "license": "MIT", + "dependencies": { + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tw-animate-css": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.2.5.tgz", + "integrity": "sha512-ABzjfgVo+fDbhRREGL4KQZUqqdPgvc5zVrLyeW9/6mVqvaDepXc7EvedA+pYmMnIOsUAQMwcWzNvom26J2qYvQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", + "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.30.1", + "@typescript-eslint/parser": "8.30.1", + "@typescript-eslint/utils": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.2.tgz", + "integrity": "sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.3", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.12" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/page/package.json b/page/package.json new file mode 100644 index 00000000..1b5693b6 --- /dev/null +++ b/page/package.json @@ -0,0 +1,49 @@ +{ + "name": "emulator-ui", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@radix-ui/react-checkbox": "^1.2.2", + "@radix-ui/react-dialog": "^1.1.10", + "@radix-ui/react-icons": "^1.3.2", + "@radix-ui/react-label": "^2.1.4", + "@radix-ui/react-popover": "^1.1.10", + "@radix-ui/react-scroll-area": "^1.2.5", + "@radix-ui/react-separator": "^1.1.4", + "@radix-ui/react-slot": "^1.2.0", + "@radix-ui/react-tooltip": "^1.2.3", + "@tailwindcss/vite": "^4.1.4", + "@types/react-window": "^1.8.8", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "jszip": "^3.10.1", + "lucide-react": "^0.501.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-window": "^1.8.11", + "tailwind-merge": "^3.2.0", + "tailwindcss": "^4.1.4", + "tw-animate-css": "^1.2.5" + }, + "devDependencies": { + "@eslint/js": "^9.22.0", + "@types/node": "^22.14.1", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.22.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "typescript": "~5.7.2", + "typescript-eslint": "^8.26.1", + "vite": "^6.3.1" + } +} diff --git a/page/emulator.js b/page/public/emulator-worker.js similarity index 62% rename from page/emulator.js rename to page/public/emulator-worker.js index d78ea051..ed047e63 100644 --- a/page/emulator.js +++ b/page/public/emulator-worker.js @@ -1,18 +1,34 @@ +var logLines = []; +var lastFlush = new Date().getTime(); + onmessage = async (event) => { const data = event.data; if (data.message == "run") { const payload = data.data; - runEmulation(payload.filesystem, payload.file); + runEmulation(payload.filesystem, payload.file, payload.options); } }; -function logLine(text) { - postMessage({ message: "log", data: text }); +function flushLines() { + const lines = logLines; + logLines = []; + lastFlush = new Date().getTime(); + postMessage({ message: "log", data: lines }); } -function runEmulation(filesystem, file) { +function logLine(text) { + logLines.push(text); + + const now = new Date().getTime(); + + if(lastFlush + 15 < now) { + flushLines(); + } +} + +function runEmulation(filesystem, file, options) { globalThis.Module = { - arguments: ["-b", /*"-c",*/ "-e", "./root", file], + arguments: [...options, "-e", "./root", file], onRuntimeInitialized: function () { filesystem.forEach(e => { if (e.name.endsWith("/")) { @@ -31,6 +47,7 @@ function runEmulation(filesystem, file) { print: logLine, printErr: logLine, postRun: () => { + flushLines(); postMessage({ message: "end", data: null }); self.close(); }, diff --git a/page/src/App.css b/page/src/App.css new file mode 100644 index 00000000..8ea195fa --- /dev/null +++ b/page/src/App.css @@ -0,0 +1,70 @@ +@media (pointer:fine) { + ::-webkit-scrollbar { + width: 20px; + } + + ::-webkit-scrollbar-track { + background-color: transparent; + } + + ::-webkit-scrollbar-thumb { + background-color: rgba(97, 97, 97, 0.7); + border-radius: 20px; + min-height: 50px; + border: 6px solid transparent; + background-clip: content-box; + transition: all 0.1s linear; + } + + ::-webkit-scrollbar-thumb:hover { + background-color: rgba(97, 97, 97, 0.9); + } +} + +html { + overflow: hidden; +} + +.terminal-output { + line-height: 1.5; + font-weight: 600; + font-size: 1.05em; + font-family: monospace; + height: 100%; +} + +.terminal-black { + color: #0C0C0C; +} + +.terminal-red { + color: #FF3131; +} + +.terminal-green { + color: #86C000; +} + +.terminal-yellow { + color: #FFB940; +} + +.terminal-blue { + color: #3A96DD; +} + +.terminal-cyan { + color: #00ADF7; +} + +.terminal-pink { + color: #9750DD; +} + +.terminal-white { + color: #ECECEC; +} + +.terminal-dark-gray { + color: rgb(65, 65, 65); +} diff --git a/page/src/App.tsx b/page/src/App.tsx new file mode 100644 index 00000000..84482d22 --- /dev/null +++ b/page/src/App.tsx @@ -0,0 +1,126 @@ +import { useState, useRef } from 'react' +import { Output } from '@/components/output' + +import { AppSidebar } from "@/components/app-sidebar" +import { ThemeProvider } from "@/components/theme-provider" +import { Separator } from "@/components/ui/separator" +import { + SidebarInset, + SidebarProvider, + SidebarTrigger, +} from "@/components/ui/sidebar" +import { Button } from './components/ui/button' + +import { Emulator, UserFile } from './emulator'; +import { getFilesystem } from './filesystem'; + +import './App.css' +import { Popover, PopoverContent, PopoverTrigger } from './components/ui/popover' + +import { createDefaultSettings } from './settings'; +import { SettingsMenu } from './components/settings-menu'; + +import { PlayIcon, GearIcon, StopIcon } from "@radix-ui/react-icons"; +import { StatusIndicator } from './components/status-indicator' + +function selectAndReadFile(): Promise { + return new Promise((resolve, reject) => { + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = '.exe'; + + fileInput.addEventListener('change', function (event) { + const file = (event as any).target.files[0]; + if (file) { + const reader = new FileReader(); + + reader.onload = function (e: ProgressEvent) { + const arrayBuffer = e.target?.result; + resolve({ + name: file.name, + data: arrayBuffer as ArrayBuffer + }); + }; + + reader.onerror = function (e: ProgressEvent) { + reject(new Error('Error reading file: ' + e.target?.error)); + }; + + reader.readAsArrayBuffer(file); + } else { + reject(new Error('No file selected')); + } + }); + + fileInput.click(); + }); +} + +function App() { + const output = useRef(null); + const [settings, setSettings] = useState(createDefaultSettings()); + const [emulator, setEmulator] = useState(null); + + function logLine(line: string) { + output.current?.logLine(line); + } + + function logLines(lines: string[]) { + output.current?.logLines(lines); + } + + async function createEmulator(userFile: UserFile | null = null) { + emulator?.stop(); + output.current?.clear(); + + logLine("Starting emulation..."); + + const fs = await getFilesystem((current, total, file) => { + logLine(`Processing filesystem (${current}/${total}): ${file}`); + }); + + const new_emulator = new Emulator(fs, logLines); + new_emulator.onTerminate().then(() => setEmulator(null)); + setEmulator(new_emulator); + + new_emulator.start(settings, userFile); + } + + async function loadAndRunUserFile() { + const fileBuffer = await selectAndReadFile(); + await createEmulator(fileBuffer); + } + + return ( + + + + +
+ + + + + + + + + + + + + + +
+ +
+
+
+ +
+
+
+
) +} + +export default App diff --git a/page/src/components/app-sidebar.tsx b/page/src/components/app-sidebar.tsx new file mode 100644 index 00000000..08967930 --- /dev/null +++ b/page/src/components/app-sidebar.tsx @@ -0,0 +1,53 @@ +import * as React from "react" + +import { + Sidebar, + SidebarContent, + SidebarGroup, + SidebarGroupLabel, + SidebarHeader, + /*SidebarMenu, + SidebarGroupContent, + SidebarMenuButton, + SidebarMenuItem,*/ + SidebarRail, +} from "@/components/ui/sidebar" + +// This is sample data. +const data = { + navMain: [ + { + title: "TODO", + }, + ], +} + +export function AppSidebar({ ...props }: React.ComponentProps) { + return ( + + + Filesystem + + + {/* We create a SidebarGroup for each parent. */} + {data.navMain.map((item) => ( + + {item.title} + {/* + + {item.items.map((item) => ( + + + {item.title} + + + ))} + + */} + + ))} + + + + ) +} diff --git a/page/src/components/output.tsx b/page/src/components/output.tsx new file mode 100644 index 00000000..5188cd95 --- /dev/null +++ b/page/src/components/output.tsx @@ -0,0 +1,244 @@ +import React from 'react'; +import { FixedSizeList as List } from 'react-window'; + +interface OutputProps { } + +interface ColorState { + color: string; +} + +interface OutputState extends ColorState { + lines: LogLine[]; +} + +interface FullOutputState extends OutputState { + grouper: OutputGrouper; + height: number, + width: number, +} + +interface LogLine { + text: string; + classNames: string; +}; + +function removeSubstringFromStart(str: string, substring: string): string { + if (str.startsWith(substring)) { + return str.slice(substring.length); + } + return str; +} + +function removeSubstringFromEnd(str: string, substring: string): string { + if (str.endsWith(substring)) { + return str.slice(0, -substring.length); + } + return str; +} + +function removeSpanFromStart(str: string, color: string) { + const pattern = /^/; + const match = str.match(pattern); + + if (match) { + const terminalValue = match[1]; + const cleanedString = str.replace(pattern, ''); + return [cleanedString, terminalValue]; + } + + return [str, color]; +} + +function extractColor(line: string, colorState: ColorState) { + while (true) { + const newLine = removeSubstringFromStart(line, ""); + if (newLine == line) { + break + } + + line = newLine; + colorState.color = ''; + } + + const [nextLine, color] = removeSpanFromStart(line, colorState.color); + + const finalLine = removeSubstringFromEnd(nextLine, "
"); + if (finalLine != nextLine) { + colorState.color = ''; + } else { + colorState.color = color; + } + + return [finalLine, color]; +} + +function renderLine(line: string, colorState: ColorState) { + const [newLine, color] = extractColor(line, colorState); + + return { + text: newLine, + classNames: 'whitespace-nowrap block ' + color + }; +} + +function renderLines(lines: string[], color: string): OutputState { + var state: ColorState = { + color + }; + + const resultLines = lines.map(line => renderLine(line, state)); + + return { + lines: resultLines, + color: state.color, + }; +} + +function mergeLines(previousState: OutputState, newLines: string[]): OutputState { + const result = renderLines(newLines, previousState.color); + return { lines: previousState.lines.concat(result.lines), color: result.color }; +} + +class OutputGrouper { + private lines: string[]; + private flushQueued: boolean; + handler: (lines: string[]) => void; + + constructor() { + this.lines = []; + this.flushQueued = false; + this.handler = () => { }; + } + + clear() { + this.lines = []; + this.flushQueued = false; + } + + flush() { + const lines = this.lines; + this.lines = []; + this.handler(lines); + } + + queueFlush() { + if (this.flushQueued) { + return false; + } + + this.flushQueued = true; + + requestAnimationFrame(() => { + if (!this.flushQueued) { + return; + } + + this.flushQueued = false; + this.flush(); + }); + } + + storeLines(lines: string[]) { + this.lines = this.lines.concat(lines); + this.queueFlush(); + } +} + +export class Output extends React.Component { + private outputRef: React.RefObject; + private listRef: React.RefObject; + private resizeObserver: ResizeObserver; + + constructor(props: OutputProps) { + super(props); + + this.clear = this.clear.bind(this); + this.logLine = this.logLine.bind(this); + this.logLines = this.logLines.bind(this); + this.updateDimensions = this.updateDimensions.bind(this); + + this.outputRef = React.createRef(); + this.listRef = React.createRef(); + this.resizeObserver = new ResizeObserver(this.updateDimensions); + + this.state = { + lines: [], + color: '', + grouper: new OutputGrouper(), + height: 10, + width: 10, + }; + + this.state.grouper.handler = (lines: string[]) => { + this.setState((s) => mergeLines(s, lines)); + }; + } + + componentDidMount() { + this.updateDimensions(); + + if (this.outputRef.current) { + this.resizeObserver.observe(this.outputRef.current); + } + } + + componentWillUnmount() { + this.resizeObserver.disconnect(); + } + + componentDidUpdate(_: OutputProps, prevState: FullOutputState) { + if (prevState.lines.length == this.state.lines.length || !this.listRef.current) { + return; + } + + this.listRef.current.scrollToItem(this.state.lines.length - 1); + } + + clear() { + this.state.grouper.clear(); + this.setState({ + lines: [], + color: '', + }); + } + + updateDimensions() { + if (!this.outputRef.current) { + return; + } + + this.setState({ + width: this.outputRef.current.offsetWidth, + height: this.outputRef.current.offsetHeight, + }); + } + + logLines(lines: string[]) { + this.state.grouper.storeLines(lines); + } + + logLine(line: string) { + this.logLines([line]); + } + + render() { + return ( +
+ + {({ index, style }) => { + const line = this.state.lines[index]; + return ( + + {line.text} + + ) + }} + +
+ ); + } +} diff --git a/page/src/components/settings-menu.tsx b/page/src/components/settings-menu.tsx new file mode 100644 index 00000000..c42476ad --- /dev/null +++ b/page/src/components/settings-menu.tsx @@ -0,0 +1,72 @@ +import React from "react"; +import { Checkbox } from "./ui/checkbox"; +import { Label } from "./ui/label"; + +import { Settings } from "@/settings"; + +interface SettingsMenuProps { + settings: Settings; + onChange: (settings: Settings) => void; + }; + +export class SettingsMenu extends React.Component { + + constructor(props: SettingsMenuProps) { + super(props); + this.getSettings = this.getSettings.bind(this); + this.state = props.settings; + } + + getSettings() { + return this.state; + } + + updateSettings(settings: Settings) { + this.setState(() => settings); + } + + componentDidUpdate() { + this.props.onChange(this.state); + } + + render() { + return ( +
+
+

Settings

+

+ Set the settings for the emulation. +

+
+ +
+ { + this.setState({ verbose: checked }); + }} /> + +
+ +
+ { + this.setState({ concise: checked }); + }} /> + +
+ +
+ { + this.setState({ silent: checked }); + }} /> + +
+ +
+ { + this.setState({ bufferStdout: checked }); + }} /> + +
+
+ ); + } +}; diff --git a/page/src/components/status-indicator.tsx b/page/src/components/status-indicator.tsx new file mode 100644 index 00000000..2dad9efe --- /dev/null +++ b/page/src/components/status-indicator.tsx @@ -0,0 +1,21 @@ +import { Badge } from '@/components/ui/badge' +import { CircleIcon } from '@radix-ui/react-icons'; + +export interface StatusIndicatorProps { + running: boolean; +}; + +export function StatusIndicator(props: StatusIndicatorProps) { + + const getText = () => { + return props.running ? " Running" : " Stopped"; + }; + + const getColor = () => { + return props.running ? "bg-lime-600" : "bg-orange-600"; + } + + return ( + {getText()} + ); +} \ No newline at end of file diff --git a/page/src/components/theme-provider.tsx b/page/src/components/theme-provider.tsx new file mode 100644 index 00000000..9d0f2dbc --- /dev/null +++ b/page/src/components/theme-provider.tsx @@ -0,0 +1,73 @@ +import { createContext, useContext, useEffect, useState } from "react" + +type Theme = "dark" | "light" | "system" + +type ThemeProviderProps = { + children: React.ReactNode + defaultTheme?: Theme + storageKey?: string +} + +type ThemeProviderState = { + theme: Theme + setTheme: (theme: Theme) => void +} + +const initialState: ThemeProviderState = { + theme: "system", + setTheme: () => null, +} + +const ThemeProviderContext = createContext(initialState) + +export function ThemeProvider({ + children, + defaultTheme = "system", + storageKey = "vite-ui-theme", + ...props +}: ThemeProviderProps) { + const [theme, setTheme] = useState( + () => (localStorage.getItem(storageKey) as Theme) || defaultTheme + ) + + useEffect(() => { + const root = window.document.documentElement + + root.classList.remove("light", "dark") + + if (theme === "system") { + const systemTheme = window.matchMedia("(prefers-color-scheme: dark)") + .matches + ? "dark" + : "light" + + root.classList.add(systemTheme) + return + } + + root.classList.add(theme) + }, [theme]) + + const value = { + theme, + setTheme: (theme: Theme) => { + localStorage.setItem(storageKey, theme) + setTheme(theme) + }, + } + + return ( + + {children} + + ) +} + +export const useTheme = () => { + const context = useContext(ThemeProviderContext) + + if (context === undefined) + throw new Error("useTheme must be used within a ThemeProvider") + + return context +} diff --git a/page/src/components/ui/badge.tsx b/page/src/components/ui/badge.tsx new file mode 100644 index 00000000..02054139 --- /dev/null +++ b/page/src/components/ui/badge.tsx @@ -0,0 +1,46 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/page/src/components/ui/breadcrumb.tsx b/page/src/components/ui/breadcrumb.tsx new file mode 100644 index 00000000..eb88f321 --- /dev/null +++ b/page/src/components/ui/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return