mirror of
https://github.com/beefytech/Beef.git
synced 2025-07-01 22:05:59 +02:00
Beefy2D updates, file pack support
This commit is contained in:
parent
2131d97756
commit
2e40bab705
5 changed files with 946 additions and 4 deletions
|
@ -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))
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
591
BeefLibs/Beefy2D/src/res/FilePack.bf
Normal file
591
BeefLibs/Beefy2D/src/res/FilePack.bf
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
274
BeefLibs/Beefy2D/src/res/PEFile.bf
Normal file
274
BeefLibs/Beefy2D/src/res/PEFile.bf
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
60
BeefLibs/Beefy2D/src/utils/StableIndexedList.bf
Normal file
60
BeefLibs/Beefy2D/src/utils/StableIndexedList.bf
Normal 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];
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue