1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-07-01 05:45:59 +02:00

Beefy2D updates, file pack support

This commit is contained in:
Brian Fiete 2025-01-17 10:15:47 -08:00
parent 2131d97756
commit 2e40bab705
5 changed files with 946 additions and 4 deletions

View file

@ -3,6 +3,7 @@ using System.Collections;
using System.Text; using System.Text;
using Beefy.utils; using Beefy.utils;
using System.Diagnostics; using System.Diagnostics;
using res;
#if STUDIO_CLIENT #if STUDIO_CLIENT
using Beefy.ipc; using Beefy.ipc;
@ -94,11 +95,13 @@ namespace Beefy.gfx
return CreateFromNativeTextureSegment(aNativeTextureSegment); 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"); 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 (aNativeTextureSegment == null)
{ {
if (flags.HasFlag(.FatalError)) if (flags.HasFlag(.FatalError))

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Text; using System.Text;
using res;
namespace Beefy.gfx namespace Beefy.gfx
{ {
@ -28,9 +29,22 @@ namespace Beefy.gfx
[CallingConvention(.Stdcall), CLink] [CallingConvention(.Stdcall), CLink]
static extern void* Gfx_GetShaderParam(void* shader, String paramName); 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) if (aNativeShader == null)
return null; return null;

View file

@ -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<Span<uint8>> GetFileData() => .Err;
public virtual FindFileData CreateFindFileData(StringView wildcard, FindFileData.Flags flags) => null;
public virtual Result<void> 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<ZipFileEntry> mFiles ~ delete _;
public override Result<Span<uint8>> 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<void> 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<String, ZipFileEntry> mFileMap ~ DeleteDictionaryAndKeysAndValues!(_);
ZipFileEntry mRootFileEntry;
public List<ZipFileEntry> RootFiles
{
get
{
FillFileMap();
return mRootFileEntry.mFiles;
}
}
public this()
{
}
public override StringView FilePath => mPath;
public Result<void> Init(StringView path)
{
mPath = new .(path);
mZipFile = new MiniZ.ZipFile();
Try!(mZipFile.OpenMapped(path));
return .Ok;
}
public Result<void> Init(StringView path, Range range)
{
mPath = new .(path);
mZipFile = new MiniZ.ZipFile();
Try!(mZipFile.OpenMapped(path, range));
return .Ok;
}
public Result<void> Init(StringView path, Span<uint8> 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<FilePack> 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<Stream> sStreams = new .() ~ DeleteContainerAndItems!(_);
static StableIndexedList<FilePack.FindFileData> 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<Span<uint8>> 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<StringView> 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;
}
}

View file

@ -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<Range> 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;
}
}

View file

@ -0,0 +1,60 @@
using System.Collections;
namespace utils;
class StableIndexedList<T>
{
public List<T> mList = new .() ~ delete _;
public List<int32> 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<T>.Enumerator GetEnumerator() => mList.GetEnumerator();
public T SafeGet(int idx)
{
if ((idx < 0) || (idx > mList.Count))
return default;
return mList[idx];
}
}