diff --git a/BeefLibs/Beefy2D/src/gfx/Image.bf b/BeefLibs/Beefy2D/src/gfx/Image.bf index 5f737827..f46b5c72 100644 --- a/BeefLibs/Beefy2D/src/gfx/Image.bf +++ b/BeefLibs/Beefy2D/src/gfx/Image.bf @@ -3,6 +3,7 @@ using System.Collections; using System.Text; using Beefy.utils; using System.Diagnostics; +using res; #if STUDIO_CLIENT using Beefy.ipc; @@ -94,11 +95,13 @@ namespace Beefy.gfx return CreateFromNativeTextureSegment(aNativeTextureSegment); } - public static Image LoadFromFile(String fileName, LoadFlags flags = .None) + public static Image LoadFromFile(StringView fileName, LoadFlags flags = .None) { scope AutoBeefPerf("Image.LoadFromFile"); - void* aNativeTextureSegment = Gfx_LoadTexture(fileName, (int32)flags); + var useFileName = scope String()..Append(fileName); + FilePackManager.TryMakeMemoryString(useFileName); + void* aNativeTextureSegment = Gfx_LoadTexture(useFileName, (int32)flags); if (aNativeTextureSegment == null) { if (flags.HasFlag(.FatalError)) diff --git a/BeefLibs/Beefy2D/src/gfx/Shader.bf b/BeefLibs/Beefy2D/src/gfx/Shader.bf index 25e0de6a..334888b5 100644 --- a/BeefLibs/Beefy2D/src/gfx/Shader.bf +++ b/BeefLibs/Beefy2D/src/gfx/Shader.bf @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Text; +using res; namespace Beefy.gfx { @@ -28,9 +29,22 @@ namespace Beefy.gfx [CallingConvention(.Stdcall), CLink] static extern void* Gfx_GetShaderParam(void* shader, String paramName); - public static Shader CreateFromFile(String fileName, VertexDefinition vertexDefinition) + public static Shader CreateFromFile(StringView fileName, VertexDefinition vertexDefinition) { - void* aNativeShader = Gfx_LoadShader(fileName, vertexDefinition.mNativeVertexDefinition); + var useFileName = scope String(fileName); + if (FilePackManager.TryMakeMemoryString(useFileName, ".fx_VS_vs_4_0")) + { + var useFileName2 = scope String(fileName); + if (FilePackManager.TryMakeMemoryString(useFileName2, ".fx_PS_ps_4_0")) + { + useFileName.Append("\n"); + useFileName.Append(useFileName2); + } + } + + FilePackManager.TryMakeMemoryString(useFileName, ".fx"); + + void* aNativeShader = Gfx_LoadShader(useFileName, vertexDefinition.mNativeVertexDefinition); if (aNativeShader == null) return null; diff --git a/BeefLibs/Beefy2D/src/res/FilePack.bf b/BeefLibs/Beefy2D/src/res/FilePack.bf new file mode 100644 index 00000000..30fa734b --- /dev/null +++ b/BeefLibs/Beefy2D/src/res/FilePack.bf @@ -0,0 +1,591 @@ +#pragma warning disable 168 + +using System; +using System.IO; +using System.Collections; +using utils; + +namespace res; + +class FilePack +{ + public class FindFileData + { + public enum Flags + { + None = 0, + Files = 1, + Directories = 2, + } + + public virtual bool MoveNext() => false; + public virtual void GetFileName(String outStr) {} + public virtual void GetFilePath(String outStr) {} + + public virtual Platform.BfpTimeStamp GetWriteTime() => 0; + public virtual Platform.BfpTimeStamp GetCreatedTime() => 0; + public virtual Platform.BfpTimeStamp GetAccessTime() => 0; + public virtual Platform.BfpFileAttributes GetFileAttributes() => 0; + public virtual int64 GetFileSize() => 0; + } + + public class FileEntry + { + public virtual void GetFilePath(String outFileName) {} + public virtual void GetFileName(String outFileName) {} + public virtual Result> GetFileData() => .Err; + public virtual FindFileData CreateFindFileData(StringView wildcard, FindFileData.Flags flags) => null; + public virtual Result ExtractTo(StringView dest) => .Err; + } + + public virtual StringView FilePath => default; + + public virtual bool PathPackMatches(StringView path) + { + return false; + } + + public virtual Stream OpenFile(StringView file) + { + return null; + } + + public virtual FileEntry GetFileEntry(StringView path) + { + return null; + } +} + +#if BF_DEPENDS_MINIZ +class ZipFilePack : FilePack +{ + public class ZipFindFileData : FindFileData + { + public ZipFileEntry mZipFileEntry; + public int32 mIdx = -1; + public String mWildcard ~ delete _; + public Flags mFlags; + + public override bool MoveNext() + { + while (true) + { + mIdx++; + if ((mZipFileEntry.mFiles == null) || (mIdx >= mZipFileEntry.mFiles.Count)) + return false; + + var fileName = GetFileName(.. scope .()); + if (Path.WildcareCompare(fileName, mWildcard)) + break; + } + return true; + } + + public override void GetFilePath(String outFileName) + { + var fileEntry = mZipFileEntry.mFiles[mIdx]; + fileEntry.mZipEntry.GetFileName(outFileName); + } + + public override void GetFileName(String outFileName) + { + var fileEntry = mZipFileEntry.mFiles[mIdx]; + var filePath = fileEntry.mZipEntry.GetFileName(.. scope .()); + Path.GetFileName(filePath, outFileName); + } + + public override Platform.BfpTimeStamp GetWriteTime() + { + return (.)mZipFileEntry.mFiles[mIdx].mZipEntry.GetTimestamp(); + } + + public override Platform.BfpTimeStamp GetCreatedTime() + { + return GetWriteTime(); + } + + public override Platform.BfpTimeStamp GetAccessTime() + { + return GetWriteTime(); + } + + public override Platform.BfpFileAttributes GetFileAttributes() + { + return .Normal; + } + + public override int64 GetFileSize() + { + return mZipFileEntry.mFiles[mIdx].mZipEntry.GetUncompressedSize(); + } + } + + public class ZipFileEntry : FileEntry + { + public MiniZ.ZipFile.Entry mZipEntry ~ delete _; + public List mFiles ~ delete _; + + public override Result> GetFileData() + { + return mZipEntry.ExtractToMemory(); + } + + public override FindFileData CreateFindFileData(StringView wildcard, FindFileData.Flags flags) + { + var findFileData = new ZipFindFileData(); + findFileData.mZipFileEntry = this; + findFileData.mWildcard = new .(wildcard); + findFileData.mFlags = flags; + return findFileData; + } + + public override Result ExtractTo(StringView dest) + { + return mZipEntry.ExtractToFile(dest); + } + + public override void GetFilePath(String outFileName) + { + mZipEntry.GetFileName(outFileName); + } + + public override void GetFileName(String outFileName) + { + var filePath = mZipEntry.GetFileName(.. scope .()); + Path.GetFileName(filePath, outFileName); + } + } + + String mPath ~ delete _; + MiniZ.ZipFile mZipFile ~ delete _; + Dictionary mFileMap ~ DeleteDictionaryAndKeysAndValues!(_); + ZipFileEntry mRootFileEntry; + + public List RootFiles + { + get + { + FillFileMap(); + return mRootFileEntry.mFiles; + } + } + + public this() + { + + } + + public override StringView FilePath => mPath; + + public Result Init(StringView path) + { + mPath = new .(path); + mZipFile = new MiniZ.ZipFile(); + Try!(mZipFile.OpenMapped(path)); + return .Ok; + } + + public Result Init(StringView path, Range range) + { + mPath = new .(path); + mZipFile = new MiniZ.ZipFile(); + Try!(mZipFile.OpenMapped(path, range)); + return .Ok; + } + + public Result Init(StringView path, Span data) + { + mPath = new .(path); + mZipFile = new MiniZ.ZipFile(); + Try!(mZipFile.Open(data)); + return .Ok; + } + + void FillFileMap() + { + if (mFileMap != null) + return; + mRootFileEntry = new .(); + mRootFileEntry.mFiles = new .(); + mFileMap = new .(); + mFileMap[new .("/")] = mRootFileEntry; + + for (int i < mZipFile.GetNumFiles()) + { + var entry = new MiniZ.ZipFile.Entry(); + mZipFile.SelectEntry(i, entry); + var fileName = entry.GetFileName(.. new .()); + + ZipFileEntry zipFileEntry = new .(); + zipFileEntry.mZipEntry = entry; + mFileMap[fileName] = zipFileEntry; + + var checkFileName = StringView(fileName); + if (checkFileName.EndsWith('/')) + checkFileName.RemoveFromEnd(1); + var fileDir = Path.GetDirectoryPath(checkFileName, .. scope .()); + fileDir.Append("/"); + if (mFileMap.TryGet(fileDir, ?, var dirEntry)) + { + if (dirEntry.mFiles == null) + dirEntry.mFiles = new .(); + dirEntry.mFiles.Add(zipFileEntry); + } + } + } + + public override FileEntry GetFileEntry(StringView path) + { + var path; + FillFileMap(); + if (path.StartsWith('/')) + path.RemoveFromStart(1); + if (path.Contains('\\')) + { + var pathStr = scope:: String(path); + pathStr.Replace('\\', '/'); + path = pathStr; + } + if (mFileMap.TryGetAlt(path, ?, var entry)) + return entry; + return null; + } + + public override bool PathPackMatches(StringView path) + { + return Path.Equals(path, mPath); + } + + public override Stream OpenFile(StringView file) + { + return base.OpenFile(file); + } +} +#endif + +static class FilePackManager +{ + public static List sFilePacks = new .() ~ DeleteContainerAndItems!(_); + + public static void AddFilePack(FilePack filePack) + { + sFilePacks.Add(filePack); + } + + public static void RemoveFilePack(FilePack filePack) + { + sFilePacks.Remove(filePack); + } + + [StaticHook(typeof(Platform.Hook), typeof(FilePackManager.HookOverrides)), AlwaysInclude] + public static struct Hook + { + } + + static StableIndexedList sStreams = new .() ~ DeleteContainerAndItems!(_); + static StableIndexedList sFindFileData = new .() ~ DeleteContainerAndItems!(_); + + static Stream TryGetStream(Platform.BfpFile* file, out int idx) + { + idx = (int)(void*)file; + return sStreams.SafeGet(idx); + } + + static FilePack.FindFileData TryGetFindFileData(Platform.BfpFindFileData* findFileData, out int idx) + { + idx = (int)(void*)findFileData; + return sFindFileData.SafeGet(idx); + } + + public static struct HookOverrides + { + [StaticHook] + public static Platform.BfpFile* BfpFile_Create(char8* name, Platform.BfpFileCreateKind createKind, Platform.BfpFileCreateFlags createFlags, Platform.BfpFileAttributes createdFileAttrs, Platform.BfpFileResult* outResult) + { + if ((createKind == .OpenExisting) && (!createFlags.HasFlag(.Write))) + { + if (var memory = TryGetMemory(.(name))) + { + var memoryStream = new FixedMemoryStream(memory); + return (.)(void*)sStreams.Add(memoryStream); + } + } + + return Hook.sBfpFile_Create(name, createKind, createFlags, createdFileAttrs, outResult); + } + + [StaticHook] + public static int BfpFile_GetSystemHandle(Platform.BfpFile* file) + { + return Hook.sBfpFile_GetSystemHandle(file); + } + + [StaticHook] + public static void BfpFile_Release(Platform.BfpFile* file) + { + if (var stream = TryGetStream(file, var idx)) + { + sStreams.RemoveAt(idx); + delete stream; + return; + } + + Hook.sBfpFile_Release(file); + } + + [StaticHook] + public static int BfpFile_Write(Platform.BfpFile* file, void* buffer, int size, int timeoutMS, Platform.BfpFileResult* outResult) + { + return Hook.sBfpFile_Write(file, buffer, size, timeoutMS, outResult); + } + + [StaticHook] + public static int BfpFile_Read(Platform.BfpFile* file, void* buffer, int size, int timeoutMS, Platform.BfpFileResult* outResult) + { + if (var stream = TryGetStream(file, ?)) + { + switch (stream.TryRead(.((.)buffer, size))) + { + case .Ok(let val): + if (val != size) + { + if (outResult != null) + *outResult = .PartialData; + } + else if (outResult != null) + *outResult = .Ok; + return val; + case .Err(let err): + if (outResult != null) + *outResult = .UnknownError; + return 0; + } + } + + return Hook.sBfpFile_Read(file, buffer, size, timeoutMS, outResult); + } + + [StaticHook] + public static void BfpFile_Flush(Platform.BfpFile* file) + { + if (var stream = TryGetStream(file, ?)) + { + return; + } + + Hook.sBfpFile_Flush(file); + } + + [StaticHook] + public static int64 BfpFile_GetFileSize(Platform.BfpFile* file) + { + if (var stream = TryGetStream(file, ?)) + { + return stream.Length; + } + + return Hook.sBfpFile_GetFileSize(file); + } + + [StaticHook] + public static int64 BfpFile_Seek(Platform.BfpFile* file, int64 offset, Platform.BfpFileSeekKind seekKind) + { + if (var stream = TryGetStream(file, ?)) + { + stream.Seek(offset, (.)seekKind); + return stream.Position; + } + + return Hook.sBfpFile_Seek(file, offset, seekKind); + + } + + [StaticHook] + public static void BfpFile_Truncate(Platform.BfpFile* file, Platform.BfpFileResult* outResult) + { + Hook.sBfpFile_Truncate(file, outResult); + } + + [StaticHook] + public static Platform.BfpFindFileData* BfpFindFileData_FindFirstFile(char8* path, Platform.BfpFindFileFlags flags, Platform.BfpFileResult* outResult) + { + StringView pathStr = .(path); + + String findDir = scope .(); + Path.GetDirectoryPath(pathStr, findDir).IgnoreError(); + findDir.Append("/"); + String wildcard = scope .(); + Path.GetFileName(pathStr, wildcard); + + if (var packFile = FindFilePackEntry(findDir)) + { + var packFindFile = packFile.CreateFindFileData(wildcard, (.)flags); + if (!packFindFile.MoveNext()) + { + delete packFile; + if (outResult != null) + *outResult = .NoResults; + return null; + } + return (.)(void*)sFindFileData.Add(packFindFile); + } + + return Hook.sBfpFindFileData_FindFirstFile(path, flags, outResult); + } + + [StaticHook] + public static bool BfpFindFileData_FindNextFile(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + return packFindFile.MoveNext(); + } + + return Hook.sBfpFindFileData_FindNextFile(findData); + } + + [StaticHook] + public static void BfpFindFileData_GetFileName(Platform.BfpFindFileData* findData, char8* outName, int32* inOutNameSize, Platform.BfpFileResult* outResult) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + var str = packFindFile.GetFileName(.. scope .()); + Platform.SetStrHelper(str, outName, inOutNameSize, (.)outResult); + return; + } + + Hook.sBfpFindFileData_GetFileName(findData, outName, inOutNameSize, outResult); + } + + [StaticHook] + public static Platform.BfpTimeStamp BfpFindFileData_GetTime_LastWrite(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + return packFindFile.GetWriteTime(); + } + + return Hook.sBfpFindFileData_GetTime_LastWrite(findData); + } + + [StaticHook] + public static Platform.BfpTimeStamp BfpFindFileData_GetTime_Created(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + return packFindFile.GetCreatedTime(); + } + + return Hook.sBfpFindFileData_GetTime_Created(findData); + } + + [StaticHook] + public static Platform.BfpTimeStamp BfpFindFileData_GetTime_Access(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + return packFindFile.GetAccessTime(); + } + + return Hook.sBfpFindFileData_GetTime_Access(findData); + } + + [StaticHook] + public static Platform.BfpFileAttributes BfpFindFileData_GetFileAttributes(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + return packFindFile.GetFileAttributes(); + } + + return Hook.sBfpFindFileData_GetFileAttributes(findData); + } + + [StaticHook] + public static int64 BfpFindFileData_GetFileSize(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + return packFindFile.GetFileSize(); + } + + return Hook.sBfpFindFileData_GetFileSize(findData); + } + + [StaticHook] + public static void BfpFindFileData_Release(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, var idx)) + { + sFindFileData.RemoveAt(idx); + delete packFindFile; + return; + } + + Hook.sBfpFindFileData_Release(findData); + } + } + + public static FilePack.FileEntry FindFilePackEntry(StringView path) + { + if (path.StartsWith('[')) + { + int packEndIdx = path.IndexOf(']'); + + if (packEndIdx != -1) + { + var packPath = path.Substring(1, packEndIdx - 1); + var filePath = path.Substring(packEndIdx + 1); + for (var filePack in sFilePacks) + { + if (filePack.PathPackMatches(packPath)) + { + return filePack.GetFileEntry(filePath); + } + } + } + } + return null; + } + + public static Result> TryGetMemory(StringView path) + { + var fileEntry = FindFilePackEntry(path); + if (fileEntry == null) + return .Err; + + return fileEntry.GetFileData(); + } + + public static bool TryMakeMemoryString(String path) + { + if (TryGetMemory(path) case .Ok(let val)) + { + var ext = Path.GetExtension(path, .. scope .()); + path.Set(scope $"@{(int)(void*)val.Ptr:X}:{val.Length}"); + path.Append(ext); + return true; + } + return false; + } + + public static bool TryMakeMemoryString(String path, params Span checkExts) + { + var ext = Path.GetExtension(path, .. scope .()); + if (!ext.IsEmpty) + return TryMakeMemoryString(path); + + for (var checkExt in checkExts) + { + var testPath = scope String(); + testPath.Append(path); + testPath.Append(checkExt); + if (TryMakeMemoryString(testPath)) + { + path.Set(testPath); + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/BeefLibs/Beefy2D/src/res/PEFile.bf b/BeefLibs/Beefy2D/src/res/PEFile.bf new file mode 100644 index 00000000..dc58fe01 --- /dev/null +++ b/BeefLibs/Beefy2D/src/res/PEFile.bf @@ -0,0 +1,274 @@ +#pragma warning disable 168 + +using System; +using System.IO; + +namespace Beefy.Res; + + +static class PEFile +{ + public const uint16 PE_MACHINE_X86 = 0x14c; + public const uint16 PE_MACHINE_X64 = 0x8664; + + [CRepr] + public struct PEHeader + { + public uint16 e_magic; // Magic number + public uint16 e_cblp; // uint8s on last page of file + public uint16 e_cp; // Pages in file + public uint16 e_crlc; // Relocations + public uint16 e_cparhdr; // Size of header in paragraphs + public uint16 e_minalloc; // Minimum extra paragraphs needed + public uint16 e_maxalloc; // Maximum extra paragraphs needed + public uint16 e_ss; // Initial (relative) SS value + public uint16 e_sp; // Initial SP value + public uint16 e_csum; // Checksum + public uint16 e_ip; // Initial IP value + public uint16 e_cs; // Initial (relative) CS value + public uint16 e_lfarlc; // File address of relocation table + public uint16 e_ovno; // Overlay number + public uint16[4] e_res; // Reserved uint16s + public uint16 e_oemid; // OEM identifier (for e_oeminfo) + public uint16 e_oeminfo; // OEM information; e_oemid specific + public uint16[10] e_res2; // Reserved uint16s + public int32 e_lfanew; // File address of new exe header + }; + + + [CRepr] + public struct PEFileHeader + { + public uint16 mMachine; + public uint16 mNumberOfSections; + public uint32 mTimeDateStamp; + public uint32 mPointerToSymbolTable; + public uint32 mNumberOfSymbols; + public uint16 mSizeOfOptionalHeader; + public uint16 mCharacteristics; + }; + + [CRepr] + public struct PEImportObjectHeader + { + public uint16 mSig1; + public uint16 mSig2; + public uint16 mVersion; + public uint16 mMachine; + public uint32 mTimeDateStamp; + public uint32 mDataSize; + public uint16 mHint; + public uint16 mType; + }; + + [CRepr] + public struct PEDataDirectory + { + public uint32 mVirtualAddress; + public uint32 mSize; + }; + + [CRepr] + public struct PEOptionalHeader32 + { + // + // Standard fields. + // + + public uint16 mMagic; + public uint8 mMajorLinkerVersion; + public uint8 mMinorLinkerVersion; + public uint32 mSizeOfCode; + public uint32 mSizeOfInitializedData; + public uint32 mSizeOfUninitializedData; + public uint32 mAddressOfEntryPoint; + public uint32 mBaseOfCode; + public uint32 mBaseOfData; + + // + // NT additional fields. + // + + public uint32 mImageBase; + public uint32 mSectionAlignment; + public uint32 mFileAlignment; + public uint16 mMajorOperatingSystemVersion; + public uint16 mMinorOperatingSystemVersion; + public uint16 mMajorImageVersion; + public uint16 mMinorImageVersion; + public uint16 mMajorSubsystemVersion; + public uint16 mMinorSubsystemVersion; + public uint32 mReserved1; + public uint32 mSizeOfImage; + public uint32 mSizeOfHeaders; + public uint32 mCheckSum; + public uint16 mSubsystem; + public uint16 mDllCharacteristics; + public uint32 mSizeOfStackReserve; + public uint32 mSizeOfStackCommit; + public uint32 mSizeOfHeapReserve; + public uint32 mSizeOfHeapCommit; + public uint32 mLoaderFlags; + public uint32 mNumberOfRvaAndSizes; + public PEDataDirectory[16] mDataDirectory; + }; + + [CRepr] + public struct PEOptionalHeader64 + { + // + // Standard fields. + // + + public uint16 mMagic; + public uint8 mMajorLinkerVersion; + public uint8 mMinorLinkerVersion; + public uint32 mSizeOfCode; + public uint32 mSizeOfInitializedData; + public uint32 mSizeOfUninitializedData; + public uint32 mAddressOfEntryPoint; + public uint32 mBaseOfCode; + + // + // NT additional fields. + // + + public uint64 mImageBase; + public uint32 mSectionAlignment; + public uint32 mFileAlignment; + public uint16 mMajorOperatingSystemVersion; + public uint16 mMinorOperatingSystemVersion; + public uint16 mMajorImageVersion; + public uint16 mMinorImageVersion; + public uint16 mMajorSubsystemVersion; + public uint16 mMinorSubsystemVersion; + public uint32 mReserved1; + public uint32 mSizeOfImage; + public uint32 mSizeOfHeaders; + public uint32 mCheckSum; + public uint16 mSubsystem; + public uint16 mDllCharacteristics; + public uint64 mSizeOfStackReserve; + public uint64 mSizeOfStackCommit; + public uint64 mSizeOfHeapReserve; + public uint64 mSizeOfHeapCommit; + public uint32 mLoaderFlags; + public uint32 mNumberOfRvaAndSizes; + public PEDataDirectory[16] mDataDirectory; + }; + + [CRepr] + struct PE_NTHeaders32 + { + uint32 mSignature; + PEFileHeader mFileHeader; + PEOptionalHeader32 mOptionalHeader; + }; + + [CRepr] + public struct PE_NTHeaders64 + { + public uint32 mSignature; + public PEFileHeader mFileHeader; + public PEOptionalHeader64 mOptionalHeader; + }; + + const int IMAGE_SIZEOF_SHORT_NAME = 8; + + [CRepr] + public struct PESectionHeader + { + public char8[IMAGE_SIZEOF_SHORT_NAME] mName; + public uint32 mVirtualSize; + public uint32 mVirtualAddress; + public uint32 mSizeOfRawData; + public uint32 mPointerToRawData; + public uint32 mPointerToRelocations; + public uint32 mPointerToLineNumbers; + public uint16 mNumberOfRelocations; + public uint16 mNumberOfLineNumbers; + public uint32 mCharacteristics; + }; + + [CRepr] + struct COFFRelocation + { + uint32 mVirtualAddress; + uint32 mSymbolTableIndex; + uint16 mType; + }; + + [CRepr] + struct PE_SymInfo + { + [Union] + public struct Name + { + public char8[8] mName; + public int32[2] mNameOfs; + } + + public Name mName; + + /*union + { + char mName[8]; + int32 mNameOfs[2]; + };*/ + + public int32 mValue; + public uint16 mSectionNum; + public uint16 mType; + public int8 mStorageClass; + public int8 mNumOfAuxSymbols; + }; + + [CRepr] + struct PE_SymInfoAux + { + public uint32 mLength; + public uint16 mNumberOfRelocations; + public uint16 mNumberOfLinenumbers; + public uint32 mCheckSum; + public uint16 mNumber; + public uint8 mSelection; + public char8 mUnused; + public char8 mUnused2; + public char8 mUnused3; + }; + + public static Result LocateExtraFileInExecutable() + { + var exePath = Environment.GetExecutableFilePath(.. scope .()); + var exeFileInfo = scope FileInfo(exePath); + + int fileLength = exeFileInfo.GetFileSize(); + + var moduleHandle = Windows.GetModuleHandleA(null); + uint8* modulePtr = (.)(void*)(int)moduleHandle; + PEFile.PEHeader* header = (.)modulePtr; + PEFile.PE_NTHeaders64* hdr64 = (.)(modulePtr + header.e_lfanew); + if (hdr64.mFileHeader.mMachine != PEFile.PE_MACHINE_X64) + return .Err; + + int exeEnd = 0; + for (int sectIdx < hdr64.mFileHeader.mNumberOfSections) + { + PEFile.PESectionHeader* sectHdrHead = (.)(modulePtr + header.e_lfanew + sizeof(PEFile.PE_NTHeaders64) + sectIdx * sizeof(PEFile.PESectionHeader)); + exeEnd = Math.Max(exeEnd, sectHdrHead.mPointerToRawData + sectHdrHead.mSizeOfRawData); + } + + if (hdr64.mOptionalHeader.mDataDirectory[4].mVirtualAddress != 0) + { + // This handles the case where our installer is signed + fileLength = hdr64.mOptionalHeader.mDataDirectory[4].mVirtualAddress; + } + + Range span = .(exeEnd, fileLength); + if (span.Length > 0) + return span; + + return .Err; + } +} + diff --git a/BeefLibs/Beefy2D/src/utils/StableIndexedList.bf b/BeefLibs/Beefy2D/src/utils/StableIndexedList.bf new file mode 100644 index 00000000..6bcb182d --- /dev/null +++ b/BeefLibs/Beefy2D/src/utils/StableIndexedList.bf @@ -0,0 +1,60 @@ +using System.Collections; +namespace utils; + +class StableIndexedList +{ + public List mList = new .() ~ delete _; + public List mFreeIndices = new .() ~ delete _; + + public this() + { + mList.Add(default); // Reserve idx 0 + } + + public ref T this[int idx] + { + get + { + return ref mList[idx]; + } + + set + { + mList[idx] = value; + } + } + + public int Add(T val) + { + if (!mFreeIndices.IsEmpty) + { + int32 idx = mFreeIndices.PopBack(); + mList[idx] = val; + return idx; + } + mList.Add(val); + return mList.Count - 1; + } + + public void RemoveAt(int idx) + { + if (idx == mList.Count - 1) + { + mList.PopBack(); + } + else + { + mList[idx] = default; + mFreeIndices.Add((.)idx); + } + } + + public List.Enumerator GetEnumerator() => mList.GetEnumerator(); + + public T SafeGet(int idx) + { + if ((idx < 0) || (idx > mList.Count)) + return default; + return mList[idx]; + } +} \ No newline at end of file