477 lines
16 KiB
C++
477 lines
16 KiB
C++
#pragma comment(lib, "user32")
|
|
#pragma comment(lib, "shell32")
|
|
#pragma comment(lib, "comctl32")
|
|
// #pragma comment(lib, "shlwapi")
|
|
#include <shellapi.h>
|
|
#include <shlobj_core.h> // SHGetFolderPathW
|
|
#include <commctrl.h> // ImageList_GetIcon
|
|
|
|
f64 GetUnixTimestamp ();
|
|
s64 GetUnixTimestampNanoseconds ();
|
|
|
|
enum class Wait_For_Result: s32 {
|
|
SUCCESS = 0,
|
|
TIMEOUT = 1,
|
|
ERROR = 2 // can't use ERROR because of Windows.h *sigh*
|
|
};
|
|
|
|
internal void semaphore_init (Semaphore* sem, s32 initial_value = 0);
|
|
internal void semaphore_destroy (Semaphore* sem);
|
|
internal void signal (Semaphore* sem);
|
|
internal Wait_For_Result wait_for (Semaphore* sem, s32 milliseconds = -1);
|
|
|
|
internal void condition_variable_init (Condition_Variable* cv);
|
|
internal void condition_variable_destroy (Condition_Variable* cv);
|
|
internal void wait (Condition_Variable* cv, Mutex* mutex, s32 wait_time_ms = -1);
|
|
internal void wake (Condition_Variable* cv);
|
|
internal void wake_all (Condition_Variable* cv);
|
|
|
|
struct Thread; // #hack forward declares to get this to compile.
|
|
typedef s64 (*Thread_Proc)(Thread* thread);
|
|
|
|
internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name="");
|
|
internal void thread_deinit (Thread* thread, bool zero_thread=false);
|
|
internal void thread_start (Thread* thread, void* thread_data = nullptr);
|
|
internal bool thread_is_done (Thread* thread, s32 milliseconds=0);
|
|
|
|
typedef u32 OS_Error_Code;
|
|
internal string get_error_string (OS_Error_Code error_code);
|
|
|
|
internal bool file_is_valid (File file);
|
|
internal File file_open (string file_path, bool for_writing=false, bool keep_existing_content=false, bool log_errors=false);
|
|
internal void file_close (File* file);
|
|
internal bool file_read (File file, void* data, s64 bytes_to_read_count, s64* bytes_read_count=nullptr);
|
|
internal bool file_length (File file, s64* length);
|
|
internal bool file_length (string file_path, s64* length);
|
|
internal s64 file_current_position (File file);
|
|
internal bool file_set_position (File file, s64 position);
|
|
internal ArrayView<u8> read_entire_file (File file, bool add_null_terminator);
|
|
internal ArrayView<u8> read_entire_file (string file_path, bool add_null_terminator=false, bool log_errors=false);
|
|
|
|
// use to_byte_view to convert ArrayView<non-u8> to ArrayView<u8>
|
|
internal bool file_write (File* file, void* data, s64 length);
|
|
internal bool write_entire_file (string file_path, void* file_data, s64 count);
|
|
internal bool write_entire_file (string file_path, ArrayView<u8> file_data);
|
|
|
|
internal bool file_exists (string file_path);
|
|
|
|
// file_write
|
|
// write_entire_file...
|
|
|
|
// #TODO #fs File System Operations
|
|
// file_move, file_delete
|
|
|
|
// #window_creation
|
|
typedef HWND Window_Type;
|
|
typedef HICON Window_Icon;
|
|
|
|
struct Window_Dimensions {
|
|
s32 window_x;
|
|
s32 window_y;
|
|
s32 window_width;
|
|
s32 window_height;
|
|
};
|
|
|
|
struct Window_Info {
|
|
Window_Type window;
|
|
Window_Dimensions initial_dimensions; // for resetting.
|
|
Window_Icon icon;
|
|
Window_Icon icon_minimized;
|
|
|
|
bool is_main_window;
|
|
bool tray_icon_added;
|
|
bool minimized_to_tray;
|
|
// Platform-Specific (Win32)
|
|
HMENU tray_icon_menu;
|
|
HDC hdc;
|
|
// Likely will only be used for main window:
|
|
NOTIFYICONDATAW nid;
|
|
};
|
|
|
|
// #move: Monitor - platform-independent hardware monitor information:
|
|
struct Monitor {
|
|
s64 left;
|
|
s64 top;
|
|
s64 right;
|
|
s64 bottom;
|
|
bool primary;
|
|
bool present;
|
|
#if OS_WINDOWS
|
|
MONITORINFO monitor_info;
|
|
#endif
|
|
};
|
|
|
|
enum class Win32_Drive_Type: s32 {
|
|
Unknown = 0,
|
|
No_Root_Dir = 1,
|
|
Removable = 2,
|
|
Fixed = 3,
|
|
Remote = 4,
|
|
cdrom = 5,
|
|
ramdisk = 6
|
|
};
|
|
|
|
enum class File_System: s32 {
|
|
Unknown = 0,
|
|
MusaFS = 1,
|
|
exFAT = 2,
|
|
Ext2 = 65,
|
|
Ext3 = 66,
|
|
Ext4 = 67,
|
|
Btrfs = 79,
|
|
XFS = 86,
|
|
ZFS = 91,
|
|
NTFS = 128, // Windows
|
|
ReFS = 130, // Windows
|
|
AFS = 256, // Apple File System
|
|
F2FS = 1024,
|
|
// Legacy systems:
|
|
FAT32 = -1,
|
|
// FAT16 :: -2;
|
|
// FAT12 :: -3;
|
|
};
|
|
|
|
string to_string (Win32_Drive_Type type) {
|
|
switch (type) {
|
|
case Win32_Drive_Type::Unknown: { return "Unknown"; } break;
|
|
case Win32_Drive_Type::No_Root_Dir: { return "No_Root_Dir"; } break;
|
|
case Win32_Drive_Type::Removable: { return "Removable"; } break;
|
|
case Win32_Drive_Type::Fixed: { return "Fixed"; } break;
|
|
case Win32_Drive_Type::Remote: { return "Remote"; } break;
|
|
case Win32_Drive_Type::cdrom: { return "cdrom"; } break;
|
|
case Win32_Drive_Type::ramdisk: { return "ramdisk"; } break;
|
|
}
|
|
|
|
Assert(false);
|
|
return "Unknown";
|
|
}
|
|
|
|
string to_string (File_System fs) {
|
|
switch (fs) {
|
|
case File_System::Unknown: { return "Unknown"; } break;
|
|
case File_System::MusaFS: { return "MusaFS"; } break;
|
|
case File_System::exFAT: { return "exFAT"; } break;
|
|
case File_System::Ext2: { return "Ext2"; } break;
|
|
case File_System::Ext3: { return "Ext3"; } break;
|
|
case File_System::Ext4: { return "Ext4"; } break;
|
|
case File_System::Btrfs: { return "Btrfs"; } break;
|
|
case File_System::XFS: { return "XFS"; } break;
|
|
case File_System::ZFS: { return "ZFS"; } break;
|
|
case File_System::NTFS: { return "NTFS"; } break;
|
|
case File_System::ReFS: { return "ReFS"; } break;
|
|
case File_System::AFS: { return "AFS"; } break;
|
|
case File_System::F2FS: { return "F2FS"; } break;
|
|
case File_System::FAT32: { return "FAT32"; } break;
|
|
}
|
|
|
|
Assert(false);
|
|
return "Unknown";
|
|
}
|
|
|
|
File_System Win32_filesystem_from_string (string s) {
|
|
string s_copy = to_lower_copy(s);
|
|
|
|
if (s_copy == "ntfs") { return File_System::NTFS; }
|
|
if (s_copy == "refs") { return File_System::ReFS; }
|
|
if (s_copy == "fat") { return File_System::FAT32; }
|
|
if (s_copy == "exfat") { return File_System::exFAT; }
|
|
|
|
Assert(false);
|
|
return File_System::Unknown;
|
|
}
|
|
|
|
struct Win32_Drive {
|
|
string label;
|
|
string volume_name;
|
|
Win32_Drive_Type type;
|
|
File_System file_system;
|
|
s64 full_size;
|
|
s64 free_space;
|
|
u32 serial_number;
|
|
u32 max_component_length;
|
|
u32 file_system_flags;
|
|
bool is_present;
|
|
};
|
|
|
|
// internal void serialize_win32_drive (Serializer* s, Win32_Drive* drive);
|
|
// internal void deserialize_win32_drive (Deserializer* s, Win32_Drive* drive);
|
|
internal Win32_Drive* copy_win32_drive (Win32_Drive* drive);
|
|
|
|
struct Win32_Icon {
|
|
string path;
|
|
s32 width;
|
|
s32 height;
|
|
ArrayView<u8> bitmap;
|
|
};
|
|
|
|
typedef Win32_Icon Icon;
|
|
typedef Win32_Drive OS_Drive;
|
|
|
|
internal void os_log_error ();
|
|
|
|
bool os_window_is_minimized (Window_Type window);
|
|
|
|
bool os_main_window_is_minimized ();
|
|
|
|
bool os_create_window (string new_window_name, Window_Type parent=nullptr, bool center_window=true, bool open_on_largest_monitor=true, bool display_window=true, void* wnd_proc_override=nullptr);
|
|
|
|
Window_Info get_main_window ();
|
|
Window_Info* get_main_window_pointer ();
|
|
|
|
string os_get_machine_name ();
|
|
|
|
// #NTFS#MFT
|
|
// Reference: https://handmade.network/forums/articles/t/7002-tutorial_parsing_the_mft
|
|
|
|
#pragma pack(push,1)
|
|
struct NTFS_BootSector {
|
|
u8 jump[3];
|
|
u8 name[8];
|
|
u16 bytesPerSector;
|
|
u8 sectorsPerCluster;
|
|
u16 reservedSectors;
|
|
u8 unused0[3];
|
|
u16 unused1;
|
|
u8 media;
|
|
u16 unused2;
|
|
u16 sectorsPerTrack;
|
|
u16 headsPerCylinder;
|
|
u32 hiddenSectors;
|
|
u32 unused3;
|
|
u32 unused4;
|
|
u64 totalSectors;
|
|
u64 mftStart;
|
|
u64 mftMirrorStart;
|
|
u32 clustersPerFileRecord;
|
|
u32 clustersPerIndexBlock;
|
|
u64 serialNumber;
|
|
u32 checksum;
|
|
u8 bootloader[426];
|
|
u16 bootSignature;
|
|
};
|
|
|
|
struct NTFS_FileRecordHeader { // https://flatcap.github.io/linux-ntfs/ntfs/concepts/file_record.html
|
|
u32 magic;
|
|
u16 updateSequenceOffset; // Offset to the Update Sequence
|
|
u16 updateSequenceSize; // Size in words of Update Sequence (S)
|
|
u64 logSequence; // $LogFile Sequence Number (LSN): This is changed every time the record is modified.
|
|
u16 sequenceNumber; // Number of times this mft record has been reused. N.B. The increment (skipping zero) is done when the file is deleted. N.B. If this is set to zero it is left as zero.
|
|
u16 hardLinkCount; // Number of hard links, i.e. the number of directory entries referencing this record.
|
|
u16 firstAttributeOffset; // Offset to the first attribute
|
|
u16 inUse : 1; // 0x01
|
|
u16 isDirectory : 1; // 0x02
|
|
// 0x04 record is an extension (set for records in the $Extend directory)
|
|
// 0x08 special index present (set for non-directory records containing an index: $Secure, $ObjID, $Quota, $Reparse)
|
|
u32 usedSize; // Real size is a count of how many bytes of a record are actually used.
|
|
u32 allocatedSize; // Allocated size the file record takes on disk (multiple of cluster size).
|
|
u64 fileReference; // Base MFT record - File reference to the base FILE record
|
|
// When it is not zero, it is an MFT reference pointing to the base MFT record to which this record belongs. The Base Record contains information about the Extension Record. This information is stored in an ATTRIBUTE_LIST attribute.
|
|
u16 nextAttributeID; // The Attribute Id that will be assigned to the next Attribute added to this MFT Record. (Incremented each time it is used, Every time the MFT Record is reused this Id is set to zero, The first instance number is always 0.)
|
|
u16 unused; // [added in XP] Align to 4 byte boundary
|
|
u32 recordNumber; // [added in XP] Number of this MFT Record
|
|
};
|
|
|
|
struct NTFS_AttributeHeader {
|
|
u32 attributeType; // see https://flatcap.github.io/linux-ntfs/ntfs/attributes/index.html
|
|
u32 length;
|
|
u8 nonResident;
|
|
u8 nameLength;
|
|
u16 nameOffset;
|
|
u16 flags;
|
|
u16 attributeID;
|
|
};
|
|
|
|
struct NTFS_ResidentAttributeHeader : NTFS_AttributeHeader {
|
|
u32 attributeLength;
|
|
u16 attributeOffset;
|
|
u8 indexed;
|
|
u8 unused;
|
|
};
|
|
|
|
struct NTFS_FileNameAttributeHeader : NTFS_ResidentAttributeHeader {
|
|
//u64 fileReferenceNumber; // A reference consists of a 48-bit index into the mft and a 16-bit sequence number used to detect stale references.
|
|
u64 parentRecordNumber : 48; // low 48 bits
|
|
u64 sequenceNumber : 16;
|
|
u64 creationTime;
|
|
u64 modificationTime;
|
|
u64 metadataModificationTime;
|
|
u64 readTime;
|
|
u64 allocatedSize; // This information is not reliable - use the $DATA attribute's unnamed stream for the correct file sizes.
|
|
u64 realSize; // This information is not reliable - use the $DATA attribute's unnamed stream for the correct file sizes.
|
|
u32 flags;
|
|
u32 repase;
|
|
u8 fileNameLength;
|
|
u8 namespaceType;
|
|
u16 fileName[1];
|
|
};
|
|
|
|
struct NTFS_FileStandardInformationHeader : NTFS_AttributeHeader {
|
|
// source: https://flatcap.github.io/linux-ntfs/ntfs/attributes/standard_information.html
|
|
u64 creationTime; // file creation time
|
|
u64 modificationTime; // changed when file contents are modified.
|
|
u64 metadataModificationTime; // changed when MFT record is modified.
|
|
u64 readTime; // last read
|
|
u32 filePermissions; // DOS file permissions.
|
|
u32 maximumNumberOfVersions;
|
|
u32 versionNumber;
|
|
u32 classID; // [added in Windows 2000]
|
|
u32 ownerID; // [added in Windows 2000]
|
|
u32 securityID; // [added in Windows 2000]
|
|
u64 quotaCharged; // [added in Windows 2000]
|
|
u64 updateSequenceNumber; // [added in Windows 2000]
|
|
};
|
|
|
|
struct NTFS_NonResidentAttributeHeader : NTFS_AttributeHeader {
|
|
// source: https://flatcap.github.io/linux-ntfs/ntfs/concepts/attribute_header.html
|
|
// see: Non-Resident, No Name, and Non-Resident, Named
|
|
u64 firstCluster; // Starting VCN
|
|
u64 lastCluster; // Last VCN
|
|
u16 dataRunsOffset; // Offset to the data runs. If not named, this is always 0x40.
|
|
u16 compressionUnit; // Compression unit size (if file is compressed), 0 implies uncompressed
|
|
u32 unused; // Padding.
|
|
u64 attributeAllocated; // allocated size of the attribute (NOT THE FILE) This is the attribute size rounded up to the cluster size.
|
|
u64 attributeSize; // Logical "real" size of the attribute (NOT THE FILE)
|
|
u64 streamDataSize; // Initialized data size of the stream (Compressed data size / actual valid bytes)
|
|
};
|
|
|
|
struct NTFS_RunHeader {
|
|
u8 lengthFieldBytes : 4;
|
|
u8 offsetFieldBytes : 4;
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
struct NTFS_MFT_Internal {
|
|
ArrayView<u8> mft_file;
|
|
ArrayView<u8> mft_buffer;
|
|
HANDLE handle;
|
|
s64 bytes_accessed;
|
|
s64 object_count;
|
|
string drive_label;
|
|
};
|
|
|
|
constexpr s64 NTFS_MFT_File_Record_Size = 1024; // File Entry Block
|
|
constexpr s64 NTFS_MFT_Files_Per_Buffer = 65536;
|
|
|
|
NTFS_MFT_Internal* new_ntfs_mft_internal () { // call with temp
|
|
NTFS_MFT_Internal* mft = New<NTFS_MFT_Internal>(true);
|
|
mft->mft_file = ArrayView<u8>(NTFS_MFT_File_Record_Size);
|
|
mft->mft_buffer = ArrayView<u8>(NTFS_MFT_File_Record_Size * NTFS_MFT_Files_Per_Buffer); // 64 MB
|
|
|
|
return mft;
|
|
}
|
|
|
|
enum class NTFS_File_Flags: u16 {
|
|
is_compressed = 0x0001, // (attribute->flags)
|
|
is_encrypted = 0x0002, // (attribute->flags)
|
|
is_sparse = 0x0004, // (attribute->flags)
|
|
// [DOS File Permissions] fileInfoAttribute->filePermissions
|
|
read_only_file = 0x0008,
|
|
hidden = 0x0010,
|
|
system = 0x0020,
|
|
archive = 0x0040,
|
|
device = 0x0080,
|
|
normal = 0x0100,
|
|
temporary = 0x0200,
|
|
sparse = 0x0400, // redundant
|
|
reparse_point = 0x0800,
|
|
compressed = 0x1000, // redundant
|
|
offline = 0x2000,
|
|
not_content_indexed = 0x4000,
|
|
encrypted = 0x8000, // redundant
|
|
};
|
|
|
|
force_inline NTFS_File_Flags operator | (NTFS_File_Flags a, NTFS_File_Flags b) {
|
|
return (NTFS_File_Flags)(((u16)a) | ((u16)b));
|
|
}
|
|
|
|
force_inline NTFS_File_Flags operator & (NTFS_File_Flags a, NTFS_File_Flags b) {
|
|
return (NTFS_File_Flags)(((u16)a) & ((u16)b));
|
|
}
|
|
|
|
force_inline NTFS_File_Flags& operator |= (NTFS_File_Flags& a, NTFS_File_Flags b) {
|
|
a = a | b;
|
|
return a;
|
|
}
|
|
|
|
force_inline NTFS_File_Flags operator ~ (NTFS_File_Flags a) {
|
|
return (NTFS_File_Flags)(~((u16)a));
|
|
}
|
|
|
|
// #TODO: I probably don't need this struct at all
|
|
struct NTFS_File {
|
|
u64 parent_id;
|
|
u64 record_id;
|
|
u16* name_data;
|
|
u64 file_modtime; // FILETIME
|
|
u64 file_size;
|
|
u8 name_count;
|
|
bool is_directory;
|
|
NTFS_File_Flags flags;
|
|
};
|
|
|
|
force_inline void add_file_permissions (NTFS_File* file, u32 permissions) {
|
|
if (permissions & 0x0001 == 0x0001) {
|
|
file->flags |= NTFS_File_Flags::read_only_file;
|
|
}
|
|
if (permissions & 0x0002 == 0x0002) {
|
|
file->flags |= NTFS_File_Flags::hidden;
|
|
}
|
|
if (permissions & 0x0004 == 0x0004) {
|
|
file->flags |= NTFS_File_Flags::system;
|
|
}
|
|
if (permissions & 0x0020 == 0x0020) {
|
|
file->flags |= NTFS_File_Flags::archive;
|
|
}
|
|
if (permissions & 0x0040 == 0x0040) {
|
|
file->flags |= NTFS_File_Flags::device;
|
|
}
|
|
if (permissions & 0x0080 == 0x0080) {
|
|
file->flags |= NTFS_File_Flags::normal;
|
|
}
|
|
if (permissions & 0x0100 == 0x0100) {
|
|
file->flags |= NTFS_File_Flags::temporary;
|
|
}
|
|
if (permissions & 0x0200 == 0x0200) {
|
|
file->flags |= NTFS_File_Flags::sparse;
|
|
}
|
|
if (permissions & 0x0400 == 0x0400) {
|
|
file->flags |= NTFS_File_Flags::reparse_point;
|
|
}
|
|
if (permissions & 0x0800 == 0x0800) {
|
|
file->flags |= NTFS_File_Flags::compressed;
|
|
}
|
|
if (permissions & 0x1000 == 0x1000) {
|
|
file->flags |= NTFS_File_Flags::offline;
|
|
}
|
|
if (permissions & 0x2000 == 0x2000) {
|
|
file->flags |= NTFS_File_Flags::not_content_indexed;
|
|
}
|
|
if (permissions & 0x4000 == 0x4000) {
|
|
file->flags |= NTFS_File_Flags::encrypted;
|
|
}
|
|
}
|
|
|
|
force_inline void add_flags (NTFS_File* file, u16 attribute_flags) {
|
|
if (attribute_flags & 0x0001 == 0x0001) {
|
|
file->flags |= NTFS_File_Flags::is_compressed;
|
|
}
|
|
if (attribute_flags & 0x4000 == 0x4000) {
|
|
file->flags |= NTFS_File_Flags::is_encrypted;
|
|
}
|
|
if (attribute_flags & 0x8000 == 0x8000) {
|
|
file->flags |= NTFS_File_Flags::is_sparse;
|
|
}
|
|
}
|
|
|
|
// #NTFS#MFT #API
|
|
// bool ntfs_mft_initialize (); // sets up thread group
|
|
// bool ntfs_mft_enumerate_drive (Win32_Drive* drive);
|
|
// bool ntfs_mft_enumeration_is_done ();
|
|
// void ntfs_mft_finish ();
|
|
|
|
// #NTFS#MFT#USNJrnl
|
|
// struct NTFS_USN_Journal {
|
|
// bool no_permission;
|
|
// HANDLE hVol;
|
|
// ArrayView<USN_Journal_Change> changes;
|
|
// };
|