diff --git a/.gitignore b/.gitignore index f0d368f8..2e19b794 100644 --- a/.gitignore +++ b/.gitignore @@ -89,6 +89,13 @@ redguard-viewer-win64 Assets/Editor/UnityEditorDarkMode Assets/Editor/UnityEditorDarkMode.meta +# Roslyn plugin (MCP dependency) +Assets/Plugins/Roslyn/ +Assets/Plugins/Roslyn.meta + # get the iotst outputs out the damn list /io_tst/*/*.txt /io_tst/*/*.exe + +# IDE config +.vsconfig diff --git a/Assets/Editor/RgprePluginDownloader.cs b/Assets/Editor/RgprePluginDownloader.cs new file mode 100644 index 00000000..7c944c15 --- /dev/null +++ b/Assets/Editor/RgprePluginDownloader.cs @@ -0,0 +1,613 @@ +using System; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; +using UnityEngine.Networking; + +[InitializeOnLoad] +public static class RgprePluginDownloader +{ + private const string RepoOwner = "michidk"; + private const string RepoName = "redguard-preservation"; + private const string LatestReleaseApiUrl = "https://api.github.com/repos/" + RepoOwner + "/" + RepoName + "/releases/latest"; + private const string TagReleaseApiUrl = "https://api.github.com/repos/" + RepoOwner + "/" + RepoName + "/releases/tags/"; + private const string PluginsDirectoryAssetPath = "Assets/Plugins/rgpre"; + private const string VersionAssetPath = "Assets/Plugins/rgpre/rgpre.version.txt"; + + [Serializable] + private sealed class GitHubRelease + { + public string tag_name; + public GitHubAsset[] assets; + } + + [Serializable] + private sealed class GitHubAsset + { + public string name; + public string browser_download_url; + } + + private sealed class PlatformTarget + { + public string Label; + public string AssetPattern; + public string BinaryFileName; + public string TargetAssetPath; + public BuildTarget BuildTarget; + public string EditorCpu; + public string PlatformCpu; + } + + private static readonly PlatformTarget[] AllPlatforms = new[] + { + new PlatformTarget + { + Label = "Windows x64", + AssetPattern = "librgpre-x86_64-pc-windows-msvc.zip", + BinaryFileName = "rgpre.dll", + TargetAssetPath = "Assets/Plugins/rgpre/Windows/x86_64/rgpre.dll", + BuildTarget = BuildTarget.StandaloneWindows64, + EditorCpu = "x86_64", + PlatformCpu = "x86_64" + }, + new PlatformTarget + { + Label = "macOS x64", + AssetPattern = "librgpre-x86_64-apple-darwin.tar.gz", + BinaryFileName = "librgpre.dylib", + TargetAssetPath = "Assets/Plugins/rgpre/macOS/x86_64/librgpre.dylib", + BuildTarget = BuildTarget.StandaloneOSX, + EditorCpu = "x86_64", + PlatformCpu = "x86_64" + }, + new PlatformTarget + { + Label = "macOS ARM64", + AssetPattern = "librgpre-aarch64-apple-darwin.tar.gz", + BinaryFileName = "librgpre.dylib", + TargetAssetPath = "Assets/Plugins/rgpre/macOS/arm64/librgpre.dylib", + BuildTarget = BuildTarget.StandaloneOSX, + EditorCpu = "ARM64", + PlatformCpu = "ARM64" + }, + new PlatformTarget + { + Label = "Linux x64", + AssetPattern = "librgpre-x86_64-unknown-linux-gnu.tar.gz", + BinaryFileName = "librgpre.so", + TargetAssetPath = "Assets/Plugins/rgpre/Linux/x86_64/librgpre.so", + BuildTarget = BuildTarget.StandaloneLinux64, + EditorCpu = "x86_64", + PlatformCpu = "x86_64" + } + }; + + // ========== Startup check ========== + + static RgprePluginDownloader() + { + bool anyFound = false; + foreach (var platform in AllPlatforms) + { + if (File.Exists(AssetPathToAbsolutePath(platform.TargetAssetPath))) + { + anyFound = true; + break; + } + } + + if (!anyFound) + { + Debug.LogWarning("[rgpre] No native plugins found. Use Tools > rgpre > Update to Latest to download them."); + } + } + + // ========== Menu items ========== + + [MenuItem("Tools/rgpre/Update to Latest")] + public static async void UpdateToLatest() + { + await DownloadReleaseAsync(null); + } + + [MenuItem("Tools/rgpre/Download Specific Version...")] + public static void DownloadSpecificVersion() + { + RgpreVersionWindow.Show(); + } + + // ========== Core download logic ========== + + internal static async Task DownloadReleaseAsync(string versionTag) + { + bool isLatest = string.IsNullOrWhiteSpace(versionTag); + string description = isLatest ? "latest" : versionTag; + + try + { + EditorUtility.DisplayProgressBar("rgpre Plugin Update", $"Fetching {description} release...", 0.02f); + + string apiUrl = isLatest ? LatestReleaseApiUrl : TagReleaseApiUrl + versionTag; + GitHubRelease release = await FetchReleaseAsync(apiUrl); + if (release == null) + { + Debug.LogError($"[rgpre] Failed to fetch release ({description}). Check the version tag."); + return; + } + + string resolvedVersion = string.IsNullOrWhiteSpace(release.tag_name) ? "unknown" : release.tag_name.Trim(); + string currentVersion = ReadCurrentVersion(); + + if (!string.IsNullOrEmpty(currentVersion) && string.Equals(currentVersion, resolvedVersion, StringComparison.Ordinal)) + { + Debug.Log($"[rgpre] Already at {resolvedVersion}."); + } + else + { + Debug.Log($"[rgpre] Current: {currentVersion ?? "none"} → Target: {resolvedVersion}"); + } + + int succeeded = 0; + int failed = 0; + + for (int i = 0; i < AllPlatforms.Length; i++) + { + PlatformTarget target = AllPlatforms[i]; + float baseProgress = 0.05f + (0.9f * i / AllPlatforms.Length); + float sliceSize = 0.9f / AllPlatforms.Length; + + EditorUtility.DisplayProgressBar("rgpre Plugin Update", $"[{i + 1}/{AllPlatforms.Length}] {target.Label}: finding asset...", baseProgress); + + GitHubAsset matchingAsset = FindMatchingAsset(release.assets, target.AssetPattern); + if (matchingAsset == null || string.IsNullOrWhiteSpace(matchingAsset.browser_download_url)) + { + Debug.LogWarning($"[rgpre] No release asset for {target.Label} (expected '{target.AssetPattern}')."); + failed++; + continue; + } + + EditorUtility.DisplayProgressBar("rgpre Plugin Update", $"[{i + 1}/{AllPlatforms.Length}] {target.Label}: downloading...", baseProgress + sliceSize * 0.1f); + + byte[] archiveBytes = await DownloadBytesAsync( + matchingAsset.browser_download_url, + progress => EditorUtility.DisplayProgressBar( + "rgpre Plugin Update", + $"[{i + 1}/{AllPlatforms.Length}] {target.Label}: downloading...", + baseProgress + sliceSize * (0.1f + 0.7f * Mathf.Clamp01(progress)))); + + EditorUtility.DisplayProgressBar("rgpre Plugin Update", $"[{i + 1}/{AllPlatforms.Length}] {target.Label}: extracting...", baseProgress + sliceSize * 0.85f); + + byte[] binaryBytes = ExtractExpectedBinary(archiveBytes, matchingAsset.name, target.BinaryFileName); + if (binaryBytes == null || binaryBytes.Length == 0) + { + Debug.LogError($"[rgpre] Failed to extract '{target.BinaryFileName}' from '{matchingAsset.name}'."); + failed++; + continue; + } + + string targetAbsoluteDir = Path.GetDirectoryName(AssetPathToAbsolutePath(target.TargetAssetPath)); + Directory.CreateDirectory(targetAbsoluteDir); + File.WriteAllBytes(AssetPathToAbsolutePath(target.TargetAssetPath), binaryBytes); + AssetDatabase.ImportAsset(target.TargetAssetPath, ImportAssetOptions.ForceUpdate); + ConfigurePluginImporter(target); + + Debug.Log($"[rgpre] {target.Label}: {target.TargetAssetPath} ({binaryBytes.LongLength:N0} bytes)"); + succeeded++; + } + + // Write version file + string pluginsAbsolute = AssetPathToAbsolutePath(PluginsDirectoryAssetPath); + Directory.CreateDirectory(pluginsAbsolute); + File.WriteAllText(AssetPathToAbsolutePath(VersionAssetPath), resolvedVersion + "\n", Encoding.UTF8); + AssetDatabase.ImportAsset(VersionAssetPath, ImportAssetOptions.ForceUpdate); + AssetDatabase.Refresh(); + + Debug.Log($"[rgpre] Updated to {resolvedVersion}: {succeeded} platforms succeeded, {failed} failed."); + } + catch (Exception ex) + { + Debug.LogError($"[rgpre] Update failed: {ex.Message}"); + } + finally + { + EditorUtility.ClearProgressBar(); + } + } + + // ========== GitHub API ========== + + private static async Task FetchReleaseAsync(string apiUrl) + { + using (UnityWebRequest request = UnityWebRequest.Get(apiUrl)) + { + request.SetRequestHeader("Accept", "application/vnd.github+json"); + request.SetRequestHeader("User-Agent", "redguard-unity-rgpre-updater"); + + await SendRequestAsync(request, null); + if (request.result != UnityWebRequest.Result.Success) + { + return null; + } + + return JsonUtility.FromJson(request.downloadHandler.text); + } + } + + private static async Task DownloadBytesAsync(string url, Action onProgress) + { + using (UnityWebRequest request = UnityWebRequest.Get(url)) + { + request.SetRequestHeader("User-Agent", "redguard-unity-rgpre-updater"); + await SendRequestAsync(request, onProgress); + return request.downloadHandler.data; + } + } + + private static async Task SendRequestAsync(UnityWebRequest request, Action onProgress) + { + UnityWebRequestAsyncOperation operation = request.SendWebRequest(); + while (!operation.isDone) + { + onProgress?.Invoke(request.downloadProgress); + await Task.Delay(100); + } + + onProgress?.Invoke(1f); + + if (request.result != UnityWebRequest.Result.Success) + { + throw new InvalidOperationException($"HTTP {request.responseCode}: {request.error}"); + } + } + + // ========== Asset matching ========== + + private static GitHubAsset FindMatchingAsset(GitHubAsset[] assets, string expectedName) + { + if (assets == null) + { + return null; + } + + for (int i = 0; i < assets.Length; i++) + { + if (assets[i] != null && string.Equals(assets[i].name, expectedName, StringComparison.Ordinal)) + { + return assets[i]; + } + } + + return null; + } + + // ========== Archive extraction ========== + + private static byte[] ExtractExpectedBinary(byte[] archiveBytes, string archiveName, string expectedBinaryFileName) + { + if (archiveName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase)) + { + return ExtractFromZip(archiveBytes, expectedBinaryFileName); + } + + if (archiveName.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase)) + { + return ExtractFromTarGz(archiveBytes, expectedBinaryFileName); + } + + throw new InvalidOperationException($"Unsupported archive format: {archiveName}"); + } + + private static byte[] ExtractFromZip(byte[] archiveBytes, string expectedBinaryFileName) + { + string tempRoot = Path.Combine(Path.GetTempPath(), "rgpre-unity-zip-" + Guid.NewGuid().ToString("N")); + string archivePath = Path.Combine(tempRoot, "archive.zip"); + string extractDirectory = Path.Combine(tempRoot, "extract"); + + Directory.CreateDirectory(extractDirectory); + + try + { + File.WriteAllBytes(archivePath, archiveBytes); + ZipFile.ExtractToDirectory(archivePath, extractDirectory); + + string[] files = Directory.GetFiles(extractDirectory, "*", SearchOption.AllDirectories); + for (int i = 0; i < files.Length; i++) + { + if (string.Equals(Path.GetFileName(files[i]), expectedBinaryFileName, StringComparison.Ordinal)) + { + return File.ReadAllBytes(files[i]); + } + } + + return null; + } + finally + { + if (Directory.Exists(tempRoot)) + { + Directory.Delete(tempRoot, true); + } + } + } + + private static byte[] ExtractFromTarGz(byte[] archiveBytes, string expectedBinaryFileName) + { + using (MemoryStream compressedStream = new MemoryStream(archiveBytes)) + using (GZipStream gzipStream = new GZipStream(compressedStream, CompressionMode.Decompress)) + { + while (TryReadExact(gzipStream, 512, out byte[] header)) + { + if (IsAllZeroBlock(header)) + { + break; + } + + string entryName = ReadTarString(header, 0, 100); + long size = ParseTarOctal(header, 124, 12); + byte typeFlag = header[156]; + bool isRegularFile = typeFlag == 0 || typeFlag == (byte)'0'; + long paddedSize = AlignTo512(size); + + if (isRegularFile) + { + byte[] fileData = ReadExactOrThrow(gzipStream, size); + long remainingPadding = paddedSize - size; + if (remainingPadding > 0) + { + SkipBytesOrThrow(gzipStream, remainingPadding); + } + + if (string.Equals(Path.GetFileName(entryName), expectedBinaryFileName, StringComparison.Ordinal)) + { + return fileData; + } + } + else + { + SkipBytesOrThrow(gzipStream, paddedSize); + } + } + } + + return null; + } + + // ========== Plugin importer configuration ========== + + private static void ConfigurePluginImporter(PlatformTarget target) + { + PluginImporter importer = AssetImporter.GetAtPath(target.TargetAssetPath) as PluginImporter; + if (importer == null) + { + Debug.LogWarning($"[rgpre] PluginImporter not found for {target.TargetAssetPath}."); + return; + } + + importer.SetCompatibleWithAnyPlatform(false); + + // Editor: compatible, with correct CPU + importer.SetCompatibleWithEditor(true); + importer.SetEditorData("CPU", target.EditorCpu); + importer.SetEditorData("OS", GetEditorOs(target.BuildTarget)); + + // Standalone platforms: only the matching one + importer.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, false); + importer.SetCompatibleWithPlatform(BuildTarget.StandaloneOSX, false); + importer.SetCompatibleWithPlatform(BuildTarget.StandaloneLinux64, false); + importer.SetCompatibleWithPlatform(target.BuildTarget, true); + importer.SetPlatformData(target.BuildTarget, "CPU", target.PlatformCpu); + + importer.SaveAndReimport(); + } + + private static string GetEditorOs(BuildTarget target) + { + switch (target) + { + case BuildTarget.StandaloneWindows64: return "Windows"; + case BuildTarget.StandaloneOSX: return "OSX"; + case BuildTarget.StandaloneLinux64: return "Linux"; + default: return "AnyOS"; + } + } + + // ========== Utilities ========== + + private static string ReadCurrentVersion() + { + string path = AssetPathToAbsolutePath(VersionAssetPath); + if (!File.Exists(path)) + { + return null; + } + + return File.ReadAllText(path).Trim(); + } + + private static string AssetPathToAbsolutePath(string assetPath) + { + string projectRoot = Path.GetDirectoryName(Application.dataPath); + return Path.Combine(projectRoot, assetPath.Replace('/', Path.DirectorySeparatorChar)); + } + + // ========== Tar/stream helpers ========== + + private static bool TryReadExact(Stream stream, int count, out byte[] buffer) + { + buffer = new byte[count]; + int offset = 0; + + while (offset < count) + { + int read = stream.Read(buffer, offset, count - offset); + if (read <= 0) + { + if (offset == 0) + { + buffer = null; + return false; + } + + throw new EndOfStreamException("Unexpected end of stream while reading tar header."); + } + + offset += read; + } + + return true; + } + + private static byte[] ReadExactOrThrow(Stream stream, long count) + { + if (count < 0 || count > int.MaxValue) + { + throw new InvalidOperationException($"Invalid tar entry size: {count}"); + } + + byte[] buffer = new byte[(int)count]; + int offset = 0; + int intCount = (int)count; + + while (offset < intCount) + { + int read = stream.Read(buffer, offset, intCount - offset); + if (read <= 0) + { + throw new EndOfStreamException("Unexpected end of stream while reading tar entry data."); + } + + offset += read; + } + + return buffer; + } + + private static void SkipBytesOrThrow(Stream stream, long bytesToSkip) + { + if (bytesToSkip <= 0) + { + return; + } + + byte[] skipBuffer = new byte[8192]; + long remaining = bytesToSkip; + while (remaining > 0) + { + int request = remaining > skipBuffer.Length ? skipBuffer.Length : (int)remaining; + int read = stream.Read(skipBuffer, 0, request); + if (read <= 0) + { + throw new EndOfStreamException("Unexpected end of stream while skipping tar entry data."); + } + + remaining -= read; + } + } + + private static bool IsAllZeroBlock(byte[] block) + { + for (int i = 0; i < block.Length; i++) + { + if (block[i] != 0) + { + return false; + } + } + + return true; + } + + private static string ReadTarString(byte[] buffer, int offset, int length) + { + int end = offset; + int max = offset + length; + while (end < max && buffer[end] != 0) + { + end++; + } + + return Encoding.ASCII.GetString(buffer, offset, end - offset).Trim(); + } + + private static long ParseTarOctal(byte[] buffer, int offset, int length) + { + string value = ReadTarString(buffer, offset, length).Trim(); + if (string.IsNullOrEmpty(value)) + { + return 0; + } + + long result = 0; + for (int i = 0; i < value.Length; i++) + { + char c = value[i]; + if (c < '0' || c > '7') + { + continue; + } + + result = (result * 8) + (c - '0'); + } + + return result; + } + + private static long AlignTo512(long value) + { + if (value <= 0) + { + return 0; + } + + long remainder = value % 512; + return remainder == 0 ? value : value + (512 - remainder); + } +} + +/// +/// Small EditorWindow for entering a specific rgpre version tag to download. +/// +public class RgpreVersionWindow : EditorWindow +{ + private string versionTag = "v"; + + public static void Show() + { + var window = GetWindow(true, "Download rgpre Version", true); + window.minSize = new Vector2(340, 90); + window.maxSize = new Vector2(340, 90); + window.ShowUtility(); + } + + private void OnGUI() + { + EditorGUILayout.Space(8); + EditorGUILayout.LabelField("Enter version tag (e.g. v0.3.4):"); + versionTag = EditorGUILayout.TextField(versionTag); + + EditorGUILayout.Space(4); + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + + if (GUILayout.Button("Download", GUILayout.Width(100))) + { + string tag = versionTag?.Trim(); + if (!string.IsNullOrEmpty(tag)) + { + Close(); + _ = RgprePluginDownloader.DownloadReleaseAsync(tag); + } + } + + if (GUILayout.Button("Cancel", GUILayout.Width(80))) + { + Close(); + } + + EditorGUILayout.EndHorizontal(); + } +} diff --git a/Assets/Editor/RgprePluginDownloader.cs.meta b/Assets/Editor/RgprePluginDownloader.cs.meta new file mode 100644 index 00000000..6cf7a4dd --- /dev/null +++ b/Assets/Editor/RgprePluginDownloader.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fd6fcb97115819e4b96e12c4de79d25b \ No newline at end of file diff --git a/Assets/Playground/SceneLoading/PlayerMeshLoader.cs b/Assets/Playground/SceneLoading/PlayerMeshLoader.cs index 950a5b3b..a18450f3 100644 --- a/Assets/Playground/SceneLoading/PlayerMeshLoader.cs +++ b/Assets/Playground/SceneLoading/PlayerMeshLoader.cs @@ -14,41 +14,37 @@ public void Start() EnableAnimations(true); } - public void Spawn3DC(string f3Dname, string colname) { - // Create the object and parent it under the root - playerMesh = ModelLoader.Load3DC(f3Dname, colname); - + playerMesh = FFIModelLoader.Load3DC(f3Dname, colname); + playerMesh.transform.SetParent(meshParent.transform); playerMesh.transform.localPosition = Vector3.zero; playerMesh.transform.localRotation = Quaternion.Euler(new Vector3(0, 180, 0)); playerMesh.transform.localScale = Vector3.one * 0.3f; - - print("Loaded object: " + f3Dname); - //SwitchTextureFilterMode(FilterMode.Point); + print("Loaded object: " + f3Dname); } - + public void SwitchTextureFilterMode(FilterMode mode) { - foreach (var mat in RGTexStore.MaterialDict) + if (playerMesh == null) return; + foreach (var renderer in playerMesh.GetComponentsInChildren()) { - mat.Value.mainTexture.filterMode = mode; + foreach (var mat in renderer.materials) + { + if (mat.mainTexture != null) + mat.mainTexture.filterMode = mode; + } } } - + public void EnableAnimations(bool enableAnimations) { - if (playerMesh.TryGetComponent(out RGScriptedObject rgso)) + if (playerMesh == null) return; + if (playerMesh.TryGetComponent(out BlendShapeAnimator animator)) { - if (rgso.type == RGScriptedObject.ScriptedObjectType.scriptedobject_animated) - { - if(enableAnimations) - rgso.SetAnim(20,0); - else - rgso.ClearAnim(); - } + animator.enabled = enableAnimations; } } } diff --git a/Assets/Playground/SceneLoading/SceneLoader.cs b/Assets/Playground/SceneLoading/SceneLoader.cs index 6cb6713f..a3fc50c8 100644 --- a/Assets/Playground/SceneLoading/SceneLoader.cs +++ b/Assets/Playground/SceneLoading/SceneLoader.cs @@ -7,36 +7,26 @@ public class SceneLoader : MonoBehaviour [SerializeField] private string RGM; [SerializeField] private string WLD; [SerializeField] private string COL; - + private GameObject _sceneSubRoot; private List loadedObjects; - + void Start() { - // Maybe generate an RGM dropdown? - // List worldList = RGINIStore.GetWorldList(); - // for (int i = 0; i < worldList.Count; i++) - // { - // print(worldList[i].RGM); - // }; - - // Load scene SpawnArea(RGM, WLD, COL); - _sceneSubRoot.transform.localScale = Vector3.one * 0.3f; + _sceneSubRoot.transform.localScale = Vector3.one * 0.3f; } - public void SpawnArea(string RGM, string WLD, string COL) { - // objectRootGenerated is simply a new GameObject that makes deleting objects easier Destroy(_sceneSubRoot); _sceneSubRoot = new GameObject(); _sceneSubRoot.transform.SetParent(sceneParent.transform); _sceneSubRoot.name = RGM; - // Create all objects of that area and parent them under the root - loadedObjects = ModelLoader.LoadArea(RGM, COL, WLD); - + FFIModelLoader.ClearCache(); + loadedObjects = FFIModelLoader.LoadArea(RGM, COL, WLD); + foreach (var obj in loadedObjects) { obj.transform.SetParent(_sceneSubRoot.transform); @@ -44,36 +34,31 @@ public void SpawnArea(string RGM, string WLD, string COL) SwitchTextureFilterMode(FilterMode.Point); EnableAnimations(true); - + print("Loaded area: " + RGM); - // RGMeshStore.DumpDict(); - // RG3DStore.DumpDict(); - // RGRGMStore.DumpDict(); - // RGTexStore.DumpDict(); } - + public void SwitchTextureFilterMode(FilterMode mode) { - foreach (var mat in RGTexStore.MaterialDict) + if (_sceneSubRoot == null) return; + foreach (var renderer in _sceneSubRoot.GetComponentsInChildren()) { - mat.Value.mainTexture.filterMode = mode; + foreach (var mat in renderer.materials) + { + if (mat.mainTexture != null) + mat.mainTexture.filterMode = mode; + } } } - + public void EnableAnimations(bool enableAnimations) { + if (loadedObjects == null) return; foreach (var obj in loadedObjects) { - if (obj.TryGetComponent(out RGScriptedObject rgso)) + if (obj.TryGetComponent(out BlendShapeAnimator animator)) { - if (rgso.type == RGScriptedObject.ScriptedObjectType.scriptedobject_animated) - { - if(enableAnimations) - rgso.SetAnim(20,0); - else - rgso.ClearAnim(); - - } + animator.enabled = enableAnimations; } } } diff --git a/Assets/Scripts/RGFileImport/RGGFXImport.meta b/Assets/Plugins.meta similarity index 77% rename from Assets/Scripts/RGFileImport/RGGFXImport.meta rename to Assets/Plugins.meta index ff2e2d64..f4e33881 100644 --- a/Assets/Scripts/RGFileImport/RGGFXImport.meta +++ b/Assets/Plugins.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 43a41bdaa38159e4a88295c28aade81f +guid: 91ba5b6ac494a32489bb4946c138fa7e folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/Plugins/rgpre.meta b/Assets/Plugins/rgpre.meta new file mode 100644 index 00000000..58db15b5 --- /dev/null +++ b/Assets/Plugins/rgpre.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6b0bec7914d72814ebd660d39dea62ba +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/rgpre/Linux.meta b/Assets/Plugins/rgpre/Linux.meta new file mode 100644 index 00000000..984cf9f2 --- /dev/null +++ b/Assets/Plugins/rgpre/Linux.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 30a59b05f77ee2b4786717ac18dd0a38 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/rgpre/Linux/x86_64.meta b/Assets/Plugins/rgpre/Linux/x86_64.meta new file mode 100644 index 00000000..d2ecf9d4 --- /dev/null +++ b/Assets/Plugins/rgpre/Linux/x86_64.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ad0a6ed85db36b64582be38434c6ae2a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/rgpre/Linux/x86_64/librgpre.so b/Assets/Plugins/rgpre/Linux/x86_64/librgpre.so new file mode 100644 index 00000000..c9114faa Binary files /dev/null and b/Assets/Plugins/rgpre/Linux/x86_64/librgpre.so differ diff --git a/Assets/Plugins/rgpre/Linux/x86_64/librgpre.so.meta b/Assets/Plugins/rgpre/Linux/x86_64/librgpre.so.meta new file mode 100644 index 00000000..79f19939 --- /dev/null +++ b/Assets/Plugins/rgpre/Linux/x86_64/librgpre.so.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 70c8463f8b24017439e9b8409167ee72 +PluginImporter: + externalObjects: {} + serializedVersion: 3 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + Android: + enabled: 0 + settings: + Is16KbAligned: false + Any: + enabled: 0 + settings: {} + Editor: + enabled: 1 + settings: + CPU: x86_64 + DefaultValueInitialized: true + OS: Linux + Linux64: + enabled: 1 + settings: + CPU: x86_64 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/rgpre/Windows.meta b/Assets/Plugins/rgpre/Windows.meta new file mode 100644 index 00000000..5e33a5ff --- /dev/null +++ b/Assets/Plugins/rgpre/Windows.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ed1e5dfada966c64ea1dfd7d045994b5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/rgpre/Windows/x86_64.meta b/Assets/Plugins/rgpre/Windows/x86_64.meta new file mode 100644 index 00000000..164a0511 --- /dev/null +++ b/Assets/Plugins/rgpre/Windows/x86_64.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8de70de8626115e4aafbe8f264f50374 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/rgpre/Windows/x86_64/rgpre.dll b/Assets/Plugins/rgpre/Windows/x86_64/rgpre.dll new file mode 100644 index 00000000..60185b03 Binary files /dev/null and b/Assets/Plugins/rgpre/Windows/x86_64/rgpre.dll differ diff --git a/Assets/Plugins/rgpre/Windows/x86_64/rgpre.dll.meta b/Assets/Plugins/rgpre/Windows/x86_64/rgpre.dll.meta new file mode 100644 index 00000000..dcc30942 --- /dev/null +++ b/Assets/Plugins/rgpre/Windows/x86_64/rgpre.dll.meta @@ -0,0 +1,37 @@ +fileFormatVersion: 2 +guid: 8eb256ca0b678004e84cd8d4827a3d73 +PluginImporter: + externalObjects: {} + serializedVersion: 3 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + Any: + enabled: 0 + settings: {} + Editor: + enabled: 1 + settings: + CPU: x86_64 + DefaultValueInitialized: true + OS: Windows + Win: + enabled: 0 + settings: + CPU: None + Win64: + enabled: 1 + settings: + CPU: x86_64 + WindowsStoreApps: + enabled: 0 + settings: + CPU: x64 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/rgpre/macOS.meta b/Assets/Plugins/rgpre/macOS.meta new file mode 100644 index 00000000..a845a66d --- /dev/null +++ b/Assets/Plugins/rgpre/macOS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bb8d1f5f7f62bd545880cd7e55e94fb0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/rgpre/macOS/arm64.meta b/Assets/Plugins/rgpre/macOS/arm64.meta new file mode 100644 index 00000000..5265d6c4 --- /dev/null +++ b/Assets/Plugins/rgpre/macOS/arm64.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ce57ff7aad00e0f4e8c953853f47ac6d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/rgpre/macOS/arm64/librgpre.dylib b/Assets/Plugins/rgpre/macOS/arm64/librgpre.dylib new file mode 100644 index 00000000..8c0f659c Binary files /dev/null and b/Assets/Plugins/rgpre/macOS/arm64/librgpre.dylib differ diff --git a/Assets/Plugins/rgpre/macOS/arm64/librgpre.dylib.meta b/Assets/Plugins/rgpre/macOS/arm64/librgpre.dylib.meta new file mode 100644 index 00000000..0d3ac89a --- /dev/null +++ b/Assets/Plugins/rgpre/macOS/arm64/librgpre.dylib.meta @@ -0,0 +1,29 @@ +fileFormatVersion: 2 +guid: d7488b401d10e8848b36962301ebcb43 +PluginImporter: + externalObjects: {} + serializedVersion: 3 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + Any: + enabled: 0 + settings: {} + Editor: + enabled: 1 + settings: + CPU: ARM64 + DefaultValueInitialized: true + OS: OSX + OSXUniversal: + enabled: 1 + settings: + CPU: ARM64 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/rgpre/macOS/x86_64.meta b/Assets/Plugins/rgpre/macOS/x86_64.meta new file mode 100644 index 00000000..29cfc801 --- /dev/null +++ b/Assets/Plugins/rgpre/macOS/x86_64.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e1d3ca8cbac6e214cbc1ab273590e5a8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/rgpre/macOS/x86_64/librgpre.dylib b/Assets/Plugins/rgpre/macOS/x86_64/librgpre.dylib new file mode 100644 index 00000000..d60bf06d Binary files /dev/null and b/Assets/Plugins/rgpre/macOS/x86_64/librgpre.dylib differ diff --git a/Assets/Plugins/rgpre/macOS/x86_64/librgpre.dylib.meta b/Assets/Plugins/rgpre/macOS/x86_64/librgpre.dylib.meta new file mode 100644 index 00000000..587551c5 --- /dev/null +++ b/Assets/Plugins/rgpre/macOS/x86_64/librgpre.dylib.meta @@ -0,0 +1,29 @@ +fileFormatVersion: 2 +guid: c6d73b441d06518468d376390addeb4d +PluginImporter: + externalObjects: {} + serializedVersion: 3 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + Any: + enabled: 0 + settings: {} + Editor: + enabled: 1 + settings: + CPU: x86_64 + DefaultValueInitialized: true + OS: OSX + OSXUniversal: + enabled: 1 + settings: + CPU: x86_64 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/rgpre/rgpre.version.txt b/Assets/Plugins/rgpre/rgpre.version.txt new file mode 100644 index 00000000..017305b2 --- /dev/null +++ b/Assets/Plugins/rgpre/rgpre.version.txt @@ -0,0 +1 @@ +v0.5.1 diff --git a/Assets/Scripts/ModelViewer/Export/Export.asmdef.meta b/Assets/Plugins/rgpre/rgpre.version.txt.meta similarity index 59% rename from Assets/Scripts/ModelViewer/Export/Export.asmdef.meta rename to Assets/Plugins/rgpre/rgpre.version.txt.meta index 07301e09..82d964d2 100644 --- a/Assets/Scripts/ModelViewer/Export/Export.asmdef.meta +++ b/Assets/Plugins/rgpre/rgpre.version.txt.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: d6383459466d7574fa393f01f8d4b609 -AssemblyDefinitionImporter: +guid: c9bfb639ba6aafb498a63cbc551dfa7c +TextScriptImporter: externalObjects: {} userData: assetBundleName: diff --git a/Assets/Scripts/Core/ModelLoader.cs b/Assets/Scripts/Core/ModelLoader.cs deleted file mode 100644 index 675184cd..00000000 --- a/Assets/Scripts/Core/ModelLoader.cs +++ /dev/null @@ -1,224 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; -using Unity.Profiling; - -public static class ModelLoader -{ - public static Dictionary scriptedObjects; - - public static GameObject Load3D(string f3Dname, string colname) - { - RGMeshStore.UnityData_3D data_3D = RGMeshStore.LoadMesh3D(RGMeshStore.mesh_type.mesh_3d, f3Dname, colname); - GameObject obj = Add3DToScene($"3D_{f3Dname}", data_3D, Vector3.zero, Vector3.zero); - return obj; - } - - public static GameObject Load3DC(string f3Dname, string colname) - { - RGMeshStore.UnityData_3D data_3D = RGMeshStore.LoadMesh(RGMeshStore.mesh_type.mesh_3d, f3Dname, colname); - GameObject obj = Add3DToScene($"3DC_{f3Dname}", data_3D, Vector3.zero, Vector3.zero); - return obj; - } - - public static List LoadROB(string ROBname, string name_col) - { - List ROBObjects = new List(); - - RG3DStore.MeshIntermediateDict.Clear(); - RG3DStore.LoadMeshIntermediatesROB(ROBname); - - foreach (var mesh in RG3DStore.MeshIntermediateDict) - { - Debug.Log(mesh.Key); - - RGMeshStore.UnityData_3D data_3D = RGMeshStore.LoadMesh(RGMeshStore.mesh_type.mesh_3d, mesh.Key, name_col); - GameObject obj = Add3DToScene(mesh.Key, data_3D, Vector3.zero, Vector3.zero); - ROBObjects.Add(obj); - } - return ROBObjects; - } - - private static GameObject Add3DToScene(string name, RGMeshStore.UnityData_3D data_3D, Vector3 position, Vector3 eulers) - { - // Create new GameObject - GameObject obj = new GameObject(name); - - if (data_3D.framecount > 0) - { - // Animated mesh: use SkinnedMeshRenderer for blendshape animation - SkinnedMeshRenderer smr = obj.AddComponent(); - smr.sharedMesh = data_3D.mesh; - smr.SetMaterials(data_3D.materials); - - BlendShapeAnimator animator = obj.AddComponent(); - animator.Initialize(smr); - } - else - { - // Static mesh: use regular MeshRenderer - MeshRenderer meshRenderer = obj.AddComponent(); - MeshFilter meshFilter = obj.AddComponent(); - meshFilter.sharedMesh = data_3D.mesh; - meshRenderer.SetMaterials(data_3D.materials); - } - - MeshCollider meshCollider = obj.AddComponent(); - meshCollider.sharedMesh = data_3D.mesh; - - // Set Position & Rotation - obj.transform.position = position; - obj.transform.Rotate(eulers); - - return obj; - } - - public static List LoadArea(string areaname, string palettename, string wldname) - { - RG3DStore.MeshIntermediateDict.Clear(); - - List areaObjects; - RG3DStore.LoadMeshIntermediatesROB(areaname); - areaObjects = LoadRGM(areaname, palettename); - if(!String.IsNullOrEmpty(wldname)) - areaObjects.Add(SetModel_wld(wldname, palettename)); - return areaObjects; - } - - static readonly ProfilerMarker s_load_RGM = new ProfilerMarker("LoadRGM"); - static readonly ProfilerMarker s_load_MPOB = new ProfilerMarker("LoadMPOB"); - static readonly ProfilerMarker s_load_MPSO = new ProfilerMarker("LoadMPSO"); - static readonly ProfilerMarker s_load_MPSF = new ProfilerMarker("LoadMPSF"); - static readonly ProfilerMarker s_load_MPRP = new ProfilerMarker("LoadMPRP"); - - private static List LoadRGM(string RGMname, string name_col) - { - scriptedObjects = new Dictionary(); - List areaObjects = new List(); - List RGM_MPSOs = RGRGMStore.LoadMPSO(RGMname); - List RGM_MPSFs = RGRGMStore.LoadMPSF(RGMname); - List RGM_MPMKs = RGRGMStore.LoadMPMK(RGMname); - List RGM_MPRPs = RGRGMStore.LoadMPRP(RGMname); - - s_load_RGM.Begin(); - RGFileImport.RGRGMFile filergm = RGRGMStore.GetRGM(RGMname); - s_load_RGM.End(); - RGRGMAnimStore.ReadAnim(filergm); - RGRGMScriptStore.ReadScript(filergm); - - s_load_MPOB.Begin(); - for(int i=0;i(); - spawned.GetComponent().Instanciate(filergm.MPOB.items[i], filergm, name_col); - scriptedObjects.Add(filergm.MPOB.items[i].id, spawned.GetComponent()); - } - catch(Exception ex) - { - Debug.LogWarning($"ERR: B{i:D3}_{filergm.MPOB.items[i].scriptName}: {ex.Message} : {ex.StackTrace}"); - } - } - s_load_MPOB.End(); - s_load_MPSO.Begin(); - for(int i=0;i().sharedMesh = data_WLD.mesh; - obj_wld.AddComponent().SetMaterials(data_WLD.materials); - obj_wld.AddComponent().sharedMesh = data_WLD.mesh; - return obj_wld; - } -} diff --git a/Assets/Scripts/Core/ModelLoader.cs.meta b/Assets/Scripts/Core/ModelLoader.cs.meta deleted file mode 100644 index 32e81b27..00000000 --- a/Assets/Scripts/Core/ModelLoader.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 96f202abd4b4b6a4d85138c206b5abca \ No newline at end of file diff --git a/Assets/Scripts/Core/WorldLoader.cs b/Assets/Scripts/Core/WorldLoader.cs index 7ccebf80..2861ce03 100644 --- a/Assets/Scripts/Core/WorldLoader.cs +++ b/Assets/Scripts/Core/WorldLoader.cs @@ -8,7 +8,7 @@ public static class WorldLoader struct WorldLoadRequest { public bool loadRequested; - public RGINIStore.worldData worldData; + public FFIWorldStore.WorldData worldData; public int playerSpawnLocation; public int playerSpawnOrientation; } @@ -33,12 +33,15 @@ static WorldLoader() public static void RequestLoadWorld(int worldId, int playerSpawnLocation, int playerSpawnOrientation) { loadRequest.loadRequested = true; - loadRequest.worldData = RGINIStore.GetWorldList()[worldId]; + loadRequest.worldData = FFIWorldStore.GetWorldList()[worldId]; loadRequest.playerSpawnLocation = playerSpawnLocation; loadRequest.playerSpawnOrientation = playerSpawnOrientation; - Material loadScreenMat = RGTexStore.GetMaterial_GXA(loadRequest.worldData.loadScreen, 0); - Game.uiManager.ShowLoadingScreen(loadScreenMat.mainTexture); + Material loadScreenMat = FFIGxaLoader.GetMaterial_GXA(loadRequest.worldData.loadScreen, 0); + if (loadScreenMat != null) + { + Game.uiManager.ShowLoadingScreen(loadScreenMat.mainTexture); + } } static void UnloadLoadedWorld() @@ -68,7 +71,7 @@ public static bool LoadWorldIfRequested() } return false; } - public static void LoadWorld(RGINIStore.worldData worldData, int playerSpawnLocation, int playerSpawnOrientation) + public static void LoadWorld(FFIWorldStore.WorldData worldData, int playerSpawnLocation, int playerSpawnOrientation) { string RGM; string COL; @@ -87,17 +90,19 @@ public static void LoadWorld(RGINIStore.worldData worldData, int playerSpawnLoca // soundLoading if(!SFXLoaded) { - RGSoundStore.LoadSFX(SFX); + FFISoundStore.LoadSFX(SFX); + SFXLoaded = true; } if(!RTXLoaded) { - RGSoundStore.LoadRTX(RTX); + FFISoundStore.LoadRTX(RTX); + RTXLoaded = true; } // load in objects - loadedObjects = ModelLoader.LoadArea(RGM, COL, WLD); + loadedObjects = FFIModelLoader.LoadArea(RGM, COL, WLD); // load the player LoadPlayer(RGM, playerSpawnLocation, playerSpawnOrientation); @@ -105,7 +110,10 @@ public static void LoadWorld(RGINIStore.worldData worldData, int playerSpawnLoca for(int i = 0; i < WorldObjects[RGM].Count;i++) { - ModelLoader.scriptedObjects[WorldObjects[RGM][i]].EnableScripting(); + if (FFIModelLoader.ScriptedObjects.TryGetValue(WorldObjects[RGM][i], out RGScriptedObject scriptedObject)) + { + scriptedObject.EnableScripting(); + } } } @@ -115,7 +123,7 @@ static void LoadPlayer(string RGM, int playerSpawnLocation, int playerSpawnOrien if(!playerLoaded) { // TODO: we need to account for gremlin too - RGFileImport.RGRGMFile filergm = RGRGMStore.GetRGM(RGM); + RGFileImport.RGRGMFile filergm = FFIModelLoader.CurrentRgmData; // Create scripted object RGFileImport.RGRGMFile.RGMMPOBItem cyrus_data = new RGFileImport.RGRGMFile.RGMMPOBItem(); @@ -134,7 +142,11 @@ static void LoadPlayer(string RGM, int playerSpawnLocation, int playerSpawnOrien playerLoaded = true; } - Vector3 playerSpawnPos = RGObjectStore.mapMarkerList[playerSpawnLocation]; + Vector3 playerSpawnPos = Vector3.zero; + if (RGObjectStore.mapMarkerList != null && playerSpawnLocation >= 0 && playerSpawnLocation < RGObjectStore.mapMarkerList.Count) + { + playerSpawnPos = RGObjectStore.mapMarkerList[playerSpawnLocation]; + } Quaternion playerSpawnRot = Quaternion.AngleAxis(((float)playerSpawnOrientation)*DA2DG, Vector3.up); RGObjectStore.GetPlayerMain().SetPositionAndRotation(playerSpawnPos, playerSpawnRot); diff --git a/Assets/Scripts/FFI.meta b/Assets/Scripts/FFI.meta new file mode 100644 index 00000000..a372bd12 --- /dev/null +++ b/Assets/Scripts/FFI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3cd8a981516ddbd4e8167b47b5843201 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/FFI/FFIGxaLoader.cs b/Assets/Scripts/FFI/FFIGxaLoader.cs new file mode 100644 index 00000000..7fe9da93 --- /dev/null +++ b/Assets/Scripts/FFI/FFIGxaLoader.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using UnityEngine; + +public static class FFIGxaLoader +{ + private static readonly Dictionary materialCache = new Dictionary(); + private static Shader gxaShader; + + public static Material GetMaterial_GXA(string gxaName, int frame) + { + string upperName = string.IsNullOrEmpty(gxaName) ? string.Empty : gxaName.ToUpperInvariant(); + string key = upperName + "/" + frame.ToString("D3"); + if (materialCache.TryGetValue(key, out Material cachedMaterial)) + { + return cachedMaterial; + } + + string path = ResolveGxaPath(upperName); + if (string.IsNullOrEmpty(path)) + { + Debug.LogWarning("[FFI] GXA file not found for " + gxaName); + return null; + } + + IntPtr decodedPtr = RgpreBindings.DecodeGxa(path, frame); + if (decodedPtr == IntPtr.Zero) + { + Debug.LogWarning("[FFI] rg_decode_gxa failed for " + gxaName + ": " + RgpreBindings.GetLastErrorMessage()); + return null; + } + + RgpreBindings.NativeBuffer buffer = RgpreBindings.ReadBuffer(decodedPtr); + try + { + int headerSize = Marshal.SizeOf(); + if (buffer.data == IntPtr.Zero || buffer.len < headerSize) + { + return null; + } + + var header = Marshal.PtrToStructure(buffer.data); + if (header.width <= 0 || header.height <= 0 || header.rgbaSize <= 0 || buffer.len < headerSize + header.rgbaSize) + { + return null; + } + + byte[] rgba = new byte[header.rgbaSize]; + Marshal.Copy(buffer.data + headerSize, rgba, 0, header.rgbaSize); + + var texture = new Texture2D(header.width, header.height, TextureFormat.RGBA32, false) + { + name = "GXA_" + upperName + "_" + frame, + filterMode = FilterMode.Point, + wrapMode = TextureWrapMode.Clamp + }; + texture.LoadRawTextureData(rgba); + texture.Apply(); + + var material = new Material(GetShader()); + material.mainTexture = texture; + materialCache[key] = material; + return material; + } + finally + { + RgpreBindings.FreeBuffer(buffer.handle); + } + } + + public static void ClearCache() + { + foreach (var mat in materialCache.Values) + { + if (mat != null) + { + if (mat.mainTexture != null) + UnityEngine.Object.Destroy(mat.mainTexture); + UnityEngine.Object.Destroy(mat); + } + } + materialCache.Clear(); + } + + private static string ResolveGxaPath(string gxaName) + => FFIPathUtils.ResolveFile(Game.pathManager.GetSystemFolder(), gxaName, ".GXA"); + + private static Shader GetShader() + { + if (gxaShader == null) + { + gxaShader = Shader.Find("Universal Render Pipeline/Simple Lit"); + } + + return gxaShader; + } +} diff --git a/Assets/Scripts/FFI/FFIGxaLoader.cs.meta b/Assets/Scripts/FFI/FFIGxaLoader.cs.meta new file mode 100644 index 00000000..074db24d --- /dev/null +++ b/Assets/Scripts/FFI/FFIGxaLoader.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4af4377384761d940a60056677dfdd50 \ No newline at end of file diff --git a/Assets/Scripts/FFI/FFIModelLoader.cs b/Assets/Scripts/FFI/FFIModelLoader.cs new file mode 100644 index 00000000..daac3151 --- /dev/null +++ b/Assets/Scripts/FFI/FFIModelLoader.cs @@ -0,0 +1,815 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using UnityEngine; +using RGFileImport; +using Debug = UnityEngine.Debug; + +public static class FFIModelLoader +{ + private static Shader defaultShader; + public static Dictionary ScriptedObjects = new Dictionary(); + public static RGRGMFile CurrentRgmData { get; private set; } = new RGRGMFile(); + + // Global caches — persist across LoadArea/LoadModel/TryGetMeshData calls + private static readonly Dictionary meshCache + = new Dictionary(StringComparer.OrdinalIgnoreCase); + + private static readonly Dictionary> robCache + = new Dictionary>(StringComparer.OrdinalIgnoreCase); + + private static readonly Dictionary materialCache + = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public static void ClearCache() + { + foreach (var entry in meshCache.Values) + { + if (entry.mesh != null) + UnityEngine.Object.Destroy(entry.mesh); + } + meshCache.Clear(); + + foreach (var segments in robCache.Values) + { + foreach (var seg in segments) + { + if (seg.mesh != null) + UnityEngine.Object.Destroy(seg.mesh); + } + } + robCache.Clear(); + + foreach (var mat in materialCache.Values) + { + if (mat != null) + UnityEngine.Object.Destroy(mat); + } + materialCache.Clear(); + + FFITextureLoader.ClearCache(); + FFIGxaLoader.ClearCache(); + } + + public static GameObject Load3D(string modelName, string colName) + { + return LoadModel(modelName, ".3D", colName, "3D_" + modelName); + } + + public static GameObject Load3DC(string modelName, string colName) + { + return LoadModel(modelName, ".3DC", colName, "3DC_" + modelName); + } + + public static List LoadROB(string robName, string colName) + { + string artFolder = Game.pathManager.GetArtFolder(); + string robPath = Path.Combine(artFolder, robName + ".ROB"); + if (!File.Exists(robPath)) + { + Debug.LogError("[FFI] ROB not found: " + robPath); + return new List(); + } + + var cachedSegments = EnsureRobCached(robName, robPath); + if (cachedSegments == null) + { + Debug.LogError("[FFI] Failed to parse ROB " + robName + ": " + RgpreBindings.GetLastErrorMessage()); + return new List(); + } + + var objects = new List(cachedSegments.Count); + foreach (var seg in cachedSegments) + { + List materials = CreateMaterials(seg.materials, colName); + objects.Add(CreateGameObject(seg.name, seg.mesh, materials, seg.frameCount)); + } + + return objects; + } + + /// + /// Parses and caches ROB segments. Returns null only if the native parse fails. + /// + private static List<(string name, Mesh mesh, RgmdDeserializer.SubmeshMaterialInfo[] materials, int frameCount)> EnsureRobCached(string robName, string robPath) + { + if (robCache.TryGetValue(robName, out var cachedSegments)) + { + return cachedSegments; + } + + IntPtr resultPtr = RgpreBindings.ParseRobData(robPath, Game.pathManager.GetRootFolder()); + if (resultPtr == IntPtr.Zero) + { + return null; + } + + var segments = RgmdDeserializer.DeserializeRobWithMaterials(resultPtr, out _, out _); + cachedSegments = new List<(string, Mesh, RgmdDeserializer.SubmeshMaterialInfo[], int)>(segments.Count); + foreach (var seg in segments) + { + int fc = seg.mesh != null ? seg.mesh.blendShapeCount : 0; + cachedSegments.Add((seg.name, seg.mesh, seg.materials, fc)); + } + robCache[robName] = cachedSegments; + return cachedSegments; + } + + public static List LoadArea(string areaName, string paletteName, string wldName) + { + var swTotal = Stopwatch.StartNew(); + var objects = new List(); + ScriptedObjects = new Dictionary(); + CurrentRgmData = new RGRGMFile(); + string artFolder = Game.pathManager.GetArtFolder(); + string mapsFolder = Game.pathManager.GetMapsFolder(); + + string robPath = Path.Combine(artFolder, areaName + ".ROB"); + var meshDict = new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (File.Exists(robPath)) + { + var cachedSegments = EnsureRobCached(areaName, robPath); + if (cachedSegments != null) + { + foreach (var seg in cachedSegments) + { + if (!meshDict.ContainsKey(seg.name)) + { + meshDict[seg.name] = (seg.mesh, seg.materials, seg.frameCount); + meshCache[seg.name] = (seg.mesh, seg.materials, seg.frameCount); + } + } + } + } + + string rgmPath = Path.Combine(mapsFolder, areaName + ".RGM"); + if (File.Exists(rgmPath)) + { + LoadRgmSections(rgmPath, paletteName, meshDict, objects); + } + + if (File.Exists(rgmPath)) + { + PlaceRgplObjects(rgmPath, paletteName, meshDict, objects); + } + + LoadTerrain(mapsFolder, wldName, paletteName, objects); + + swTotal.Stop(); + Debug.Log($"[FFI LoadArea] Total={swTotal.ElapsedMilliseconds}ms"); + + return objects; + } + + private static void PlaceRgplObjects( + string rgmPath, + string paletteName, + Dictionary meshDict, + List objects) + { + IntPtr resultPtr = RgpreBindings.ParseRgmPlacements(rgmPath); + if (resultPtr == IntPtr.Zero) + { + return; + } + + var rgpl = RgplDeserializer.Deserialize(resultPtr); + + int skippedEmpty = 0; + int skippedNoMesh = 0; + int placed = 0; + var missingModels = new HashSet(); + + foreach (var placement in rgpl.placements) + { + string modelName = placement.modelName; + + // Flat sprite — create a textured quad + if (placement.textureId > 0 && string.IsNullOrEmpty(modelName)) + { + GameObject quad = CreateFlatSprite(placement, paletteName); + if (quad != null) + { + objects.Add(quad); + placed++; + } + else + { + skippedNoMesh++; + } + continue; + } + + if (string.IsNullOrEmpty(modelName)) + { + skippedEmpty++; + continue; + } + + Mesh mesh = null; + RgmdDeserializer.SubmeshMaterialInfo[] matInfos = null; + int frameCount = 0; + + if (meshDict.TryGetValue(modelName, out var cached)) + { + mesh = cached.mesh; + matInfos = cached.materials; + frameCount = cached.frameCount; + } + else + { + var loaded = TryLoadModelFile(modelName); + if (loaded.HasValue) + { + mesh = loaded.Value.mesh; + matInfos = loaded.Value.materials; + frameCount = loaded.Value.frameCount; + meshDict[modelName] = loaded.Value; + } + } + + if (mesh == null) + { + skippedNoMesh++; + missingModels.Add(modelName); + continue; + } + + List materials = CreateMaterials(matInfos, paletteName); + GameObject obj = CreateGameObject(modelName, mesh, materials, frameCount); + ApplyMatrix(obj.transform, placement.transform); + objects.Add(obj); + placed++; + } + + Debug.Log($"[FFI LoadArea] {rgpl.placements.Count} placements, {rgpl.lights.Count} lights. Placed: {placed}, Skipped (empty): {skippedEmpty}, Skipped (no mesh): {skippedNoMesh}"); + if (missingModels.Count > 0) + Debug.Log($"[FFI LoadArea] Missing models: {string.Join(", ", missingModels)}"); + + CreateLights(rgpl.lights, objects); + } + + private static void CreateLights(List lights, List objects) + { + foreach (var light in lights) + { + GameObject lightObj = new GameObject("Light_" + light.name); + Light lightComp = lightObj.AddComponent(); + lightComp.type = LightType.Point; + lightComp.color = light.color; + lightComp.range = light.range; + lightObj.transform.position = light.position; + objects.Add(lightObj); + } + } + + private static void LoadTerrain(string mapsFolder, string wldName, string paletteName, List objects) + { + if (string.IsNullOrEmpty(wldName)) + { + return; + } + + string wldPath = Path.Combine(mapsFolder, wldName + ".WLD"); + if (!File.Exists(wldPath)) + { + return; + } + + IntPtr resultPtr = RgpreBindings.ParseWldTerrainData(wldPath); + if (resultPtr == IntPtr.Zero) + { + Debug.LogWarning("[FFI] Failed to parse WLD terrain: " + RgpreBindings.GetLastErrorMessage()); + return; + } + + var (mesh, matInfos, _) = RgmdDeserializer.DeserializeModel(resultPtr, "Terrain"); + if (mesh == null) + { + return; + } + + List materials = CreateMaterials(matInfos, paletteName); + GameObject terrainObj = new GameObject("Terrain"); + terrainObj.isStatic = true; + + MeshRenderer renderer = terrainObj.AddComponent(); + MeshFilter filter = terrainObj.AddComponent(); + filter.sharedMesh = mesh; + renderer.SetMaterials(materials); + + MeshCollider collider = terrainObj.AddComponent(); + collider.sharedMesh = mesh; + + objects.Add(terrainObj); + } + + private static (Mesh mesh, RgmdDeserializer.SubmeshMaterialInfo[] materials, int frameCount)? TryLoadModelFile(string modelName) + { + if (meshCache.TryGetValue(modelName, out var cached)) + return cached; + + string artFolder = Game.pathManager.GetArtFolder(); + + foreach (string ext in new[] { ".3DC", ".3D" }) + { + string path = Path.Combine(artFolder, modelName + ext); + if (!File.Exists(path)) + { + continue; + } + + IntPtr resultPtr = RgpreBindings.ParseModelData(path, Game.pathManager.GetRootFolder()); + if (resultPtr == IntPtr.Zero) + { + continue; + } + + var (mesh, matInfos, frameCount) = RgmdDeserializer.DeserializeModel(resultPtr, modelName); + if (mesh != null) + { + var result = (mesh, matInfos, frameCount); + meshCache[modelName] = result; + return result; + } + } + + return null; + } + + private static void ApplyMatrix(Transform transform, Matrix4x4 matrix) + { + transform.position = new Vector3(matrix.m03, matrix.m13, matrix.m23); + + Vector3 scale = new Vector3( + new Vector3(matrix.m00, matrix.m10, matrix.m20).magnitude, + new Vector3(matrix.m01, matrix.m11, matrix.m21).magnitude, + new Vector3(matrix.m02, matrix.m12, matrix.m22).magnitude + ); + transform.localScale = scale; + + if (scale.x > 0f && scale.y > 0f && scale.z > 0f) + { + Matrix4x4 rotMatrix = Matrix4x4.identity; + rotMatrix.SetColumn(0, matrix.GetColumn(0) / scale.x); + rotMatrix.SetColumn(1, matrix.GetColumn(1) / scale.y); + rotMatrix.SetColumn(2, matrix.GetColumn(2) / scale.z); + transform.rotation = rotMatrix.rotation; + } + } + + private static Mesh flatQuadMesh; + + private static GameObject CreateFlatSprite(RgplDeserializer.Placement placement, string paletteName) + { + Texture2D tex = FFITextureLoader.DecodeTexture(placement.textureId, placement.imageId, paletteName); + if (tex == null) return null; + + if (flatQuadMesh == null) + { + flatQuadMesh = new Mesh(); + flatQuadMesh.name = "FlatQuad"; + flatQuadMesh.vertices = new Vector3[] + { + new Vector3(-0.5f, 0f, 0f), + new Vector3( 0.5f, 0f, 0f), + new Vector3( 0.5f, 1f, 0f), + new Vector3(-0.5f, 1f, 0f) + }; + flatQuadMesh.uv = new Vector2[] + { + new Vector2(0, 0), + new Vector2(1, 0), + new Vector2(1, 1), + new Vector2(0, 1) + }; + flatQuadMesh.triangles = new int[] { 0, 2, 1, 0, 3, 2 }; + flatQuadMesh.normals = new Vector3[] + { + Vector3.back, Vector3.back, Vector3.back, Vector3.back + }; + } + + string label = !string.IsNullOrEmpty(placement.sourceId) ? placement.sourceId : "Flat_" + placement.textureId + "_" + placement.imageId; + GameObject obj = new GameObject(label); + + MeshFilter mf = obj.AddComponent(); + mf.sharedMesh = flatQuadMesh; + + MeshRenderer mr = obj.AddComponent(); + Material mat = new Material(GetDefaultShader()); + mat.mainTexture = tex; + mr.sharedMaterial = mat; + + ApplyMatrix(obj.transform, placement.transform); + + // Scale quad to match texture aspect ratio + float aspect = (float)tex.width / tex.height; + obj.transform.localScale = new Vector3( + obj.transform.localScale.x * aspect, + obj.transform.localScale.y, + obj.transform.localScale.z + ); + + return obj; + } + + private static GameObject LoadModel(string modelName, string extension, string colName, string displayName) + { + var loaded = TryLoadModelFile(modelName); + if (!loaded.HasValue || loaded.Value.mesh == null) + { + string artFolder = Game.pathManager.GetArtFolder(); + string modelPath = Path.Combine(artFolder, modelName + extension); + if (!File.Exists(modelPath)) + { + Debug.LogError("[FFI] Model not found: " + modelPath); + return new GameObject(displayName); + } + + IntPtr resultPtr = RgpreBindings.ParseModelData(modelPath, Game.pathManager.GetRootFolder()); + if (resultPtr == IntPtr.Zero) + { + Debug.LogError("[FFI] Failed to parse " + modelName + extension + ": " + RgpreBindings.GetLastErrorMessage()); + return new GameObject(displayName); + } + + var (mesh, materialInfos, frameCount) = RgmdDeserializer.DeserializeModel(resultPtr, displayName); + if (mesh == null) + { + return new GameObject(displayName); + } + + meshCache[modelName] = (mesh, materialInfos, frameCount); + List mats = CreateMaterials(materialInfos, colName); + return CreateGameObject(displayName, mesh, mats, frameCount); + } + + List materials = CreateMaterials(loaded.Value.materials, colName); + return CreateGameObject(displayName, loaded.Value.mesh, materials, loaded.Value.frameCount); + } + + private static List CreateMaterials(RgmdDeserializer.SubmeshMaterialInfo[] materialInfos, string colName) + { + int materialCount = materialInfos != null ? materialInfos.Length : 0; + var materials = new List(Math.Max(materialCount, 1)); + + if (materialCount == 0) + { + var fallback = new Material(GetDefaultShader()); + fallback.color = Color.magenta; + materials.Add(fallback); + return materials; + } + + for (int i = 0; i < materialInfos.Length; i++) + { + var info = materialInfos[i]; + string matKey; + if (info.isSolidColor) + matKey = "solid_" + info.solidColor.r + "_" + info.solidColor.g + "_" + info.solidColor.b; + else + matKey = colName + "_tex_" + info.textureId + "_" + info.imageId; + + if (materialCache.TryGetValue(matKey, out Material cachedMat)) + { + materials.Add(cachedMat); + continue; + } + + var material = new Material(GetDefaultShader()); + + if (info.isSolidColor) + { + material.color = info.solidColor; + } + else + { + List frames = FFITextureLoader.DecodeTextureAllFrames(info.textureId, info.imageId, colName); + if (frames != null && frames.Count > 0) + { + material.mainTexture = frames[0]; + + for (int f = 0; f < frames.Count; f++) + { + material.SetTexture("FRAME_" + f, frames[f]); + } + } + else + { + material.color = Color.magenta; + } + } + + materialCache[matKey] = material; + materials.Add(material); + } + + return materials; + } + + private static GameObject CreateGameObject(string name, Mesh mesh, List materials, int frameCount) + { + var obj = new GameObject(name); + if (mesh == null) + { + return obj; + } + + if (frameCount > 0) + { + SkinnedMeshRenderer smr = obj.AddComponent(); + smr.sharedMesh = mesh; + smr.SetMaterials(materials); + + BlendShapeAnimator animator = obj.AddComponent(); + animator.Initialize(smr); + } + else + { + MeshRenderer meshRenderer = obj.AddComponent(); + MeshFilter meshFilter = obj.AddComponent(); + meshFilter.sharedMesh = mesh; + meshRenderer.SetMaterials(materials); + } + + MeshCollider meshCollider = obj.AddComponent(); + meshCollider.sharedMesh = mesh; + return obj; + } + + public static bool TryGetMeshData(string modelName, string colName, out Mesh mesh, out List materials, out int frameCount) + { + mesh = null; + materials = null; + frameCount = 0; + + var loaded = TryLoadModelFile(modelName); + if (!loaded.HasValue || loaded.Value.mesh == null) + { + return false; + } + + mesh = loaded.Value.mesh; + frameCount = loaded.Value.frameCount; + materials = CreateMaterials(loaded.Value.materials, colName); + return true; + } + + private static byte[] GetSection(string rgmPath, string tag) + { + IntPtr ptr = RgpreBindings.GetRgmSection(rgmPath, tag, 0); + if (ptr == IntPtr.Zero) return null; + return RgpreBindings.ExtractBytesAndFree(ptr); + } + + private static void LoadRgmSections( + string rgmPath, + string paletteName, + Dictionary meshDict, + List objects) + { + CurrentRgmData = new RGRGMFile(); + + PopulateRawSections(rgmPath); + ParseRAHD(GetSection(rgmPath, "RAHD")); + ParseMPOB(GetSection(rgmPath, "MPOB"), paletteName, meshDict, objects); + + // Initialize animation and script stores + try + { + RGRGMAnimStore.ReadAnim(CurrentRgmData); + } + catch (Exception ex) + { + Debug.LogWarning("[FFI] AnimStore init: " + ex.Message); + } + + try + { + RGRGMScriptStore.ReadScript(CurrentRgmData); + } + catch (Exception ex) + { + Debug.LogWarning("[FFI] ScriptStore init: " + ex.Message); + } + } + + private static void PopulateRawSections(string rgmPath) + { + byte[] raatBytes = GetSection(rgmPath, "RAAT"); + byte[] ranmBytes = GetSection(rgmPath, "RANM"); + byte[] ralcBytes = GetSection(rgmPath, "RALC"); + byte[] raanBytes = GetSection(rgmPath, "RAAN"); + byte[] ragrBytes = GetSection(rgmPath, "RAGR"); + byte[] rastBytes = GetSection(rgmPath, "RAST"); + byte[] rasbBytes = GetSection(rgmPath, "RASB"); + byte[] ravaBytes = GetSection(rgmPath, "RAVA"); + byte[] rascBytes = GetSection(rgmPath, "RASC"); + byte[] rahkBytes = GetSection(rgmPath, "RAHK"); + + if (raatBytes != null) CurrentRgmData.RAAT.attributes = raatBytes; + if (raanBytes != null) CurrentRgmData.RAAN.data = raanBytes; + if (ragrBytes != null) CurrentRgmData.RAGR.data = ragrBytes; + if (rahkBytes != null) CurrentRgmData.RAHK.data = rahkBytes; + if (rascBytes != null) CurrentRgmData.RASC.scripts = rascBytes; + + if (ranmBytes != null) + { + CurrentRgmData.RANM.data = new char[ranmBytes.Length]; + for (int i = 0; i < ranmBytes.Length; i++) + CurrentRgmData.RANM.data[i] = (char)ranmBytes[i]; + } + + if (rastBytes != null) + { + CurrentRgmData.RAST.text = new char[rastBytes.Length]; + for (int i = 0; i < rastBytes.Length; i++) + CurrentRgmData.RAST.text[i] = (char)rastBytes[i]; + } + + if (rasbBytes != null && rasbBytes.Length >= 4) + { + CurrentRgmData.RASB.offsets = new int[rasbBytes.Length / 4]; + for (int i = 0; i < CurrentRgmData.RASB.offsets.Length; i++) + CurrentRgmData.RASB.offsets[i] = BitConverter.ToInt32(rasbBytes, i * 4); + } + + if (ravaBytes != null && ravaBytes.Length >= 4) + { + CurrentRgmData.RAVA.data = new int[ravaBytes.Length / 4]; + for (int i = 0; i < CurrentRgmData.RAVA.data.Length; i++) + CurrentRgmData.RAVA.data[i] = BitConverter.ToInt32(ravaBytes, i * 4); + } + + const int RalcItemSize = 12; // 3x int32 + if (ralcBytes != null && ralcBytes.Length >= RalcItemSize) + { + int count = ralcBytes.Length / RalcItemSize; + CurrentRgmData.RALC.items = new List(count); + for (int i = 0; i < count; i++) + { + int off = i * RalcItemSize; + CurrentRgmData.RALC.items.Add(new RGRGMFile.RGMRALCItem + { + offsetX = BitConverter.ToInt32(ralcBytes, off), + offsetY = BitConverter.ToInt32(ralcBytes, off + 4), + offsetZ = BitConverter.ToInt32(ralcBytes, off + 8) + }); + } + } + } + + private static void ParseRAHD(byte[] rahdBytes) + { + if (rahdBytes == null || rahdBytes.Length <= 4) + { + return; + } + + var reader = new RGFileImport.MemoryReader(rahdBytes); + uint numItems = (uint)reader.ReadInt32(); + for (int i = 0; i < (int)numItems; i++) + { + var item = new RGRGMFile.RGMRAHDItem(); + item.index = i; + item.unknown0 = reader.ReadInt32(); + item.unknown1 = reader.ReadInt32(); + char[] nameChars = reader.ReadChars(9); + item.scriptName = new string(nameChars).Split('\0')[0]; + item.MPOBCount = reader.ReadInt32(); + item.unknown2 = reader.ReadInt32(); + item.RANMLength = reader.ReadInt32(); + item.RANMOffset = reader.ReadInt32(); + item.RAATOffset = reader.ReadInt32(); + item.RAANCount = reader.ReadInt32(); + item.RAANLength = reader.ReadInt32(); + item.RAANOffset = reader.ReadInt32(); + item.RAGRMaxGroup = reader.ReadInt32(); + item.RAGROffset = reader.ReadInt32(); + item.unknown3 = reader.ReadInt32(); + item.unknown4 = reader.ReadInt32(); + item.unknown5 = reader.ReadInt32(); + item.RASBCount = reader.ReadInt32(); + item.RASBLength = reader.ReadInt32(); + item.RASBOffset = reader.ReadInt32(); + item.RASCLength = reader.ReadInt32(); + item.RASCOffset = reader.ReadInt32(); + item.RASCThreadStart = reader.ReadInt32(); + item.RAHKLength = reader.ReadInt32(); + item.RAHKOffset = reader.ReadInt32(); + item.RALCCount = reader.ReadInt32(); + item.RALCLength = reader.ReadInt32(); + item.RALCOffset = reader.ReadInt32(); + item.RAEXLength = reader.ReadInt32(); + item.RAEXOffset = reader.ReadInt32(); + item.RAVACount = reader.ReadInt32(); + item.RAVALength = reader.ReadInt32(); + item.RAVAOffset = reader.ReadInt32(); + item.unknown6 = reader.ReadInt32(); + item.frameCount = reader.ReadInt32(); + item.MPSZNormalId = reader.ReadInt32(); + item.MPSZCombatId = reader.ReadInt32(); + item.MPSZDeadId = reader.ReadInt32(); + item.unknown7 = reader.ReadInt16(); + item.unknown8 = reader.ReadInt16(); + item.unknown9 = reader.ReadInt16(); + item.textureId = reader.ReadInt16(); + item.RAVCOffset = reader.ReadInt32(); + + CurrentRgmData.RAHD.dict[item.scriptName] = item; + } + } + + private static void ParseMPOB( + byte[] mpobBytes, + string paletteName, + Dictionary meshDict, + List objects) + { + if (mpobBytes == null || mpobBytes.Length <= 4) + { + return; + } + + var reader = new RGFileImport.MemoryReader(mpobBytes); + uint numItems = (uint)reader.ReadInt32(); + for (int i = 0; i < (int)numItems; i++) + { + var mpob = new RGRGMFile.RGMMPOBItem(); + mpob.id = (uint)reader.ReadInt32(); // 4 bytes + mpob.type = (RGRGMFile.ObjectType)reader.ReadByte(); // 1 byte + reader.ReadByte(); // isActive 1 byte + + char[] scriptChars = reader.ReadChars(9); // 9 bytes + mpob.scriptName = new string(scriptChars).Split('\0')[0]; + + char[] modelChars = reader.ReadChars(9); // 9 bytes + string rawModel = new string(modelChars).Split('\0')[0]; + mpob.modelName = NormalizeModelName(rawModel); + + reader.ReadByte(); // isStatic 1 byte + reader.ReadInt16(); // unknown1 2 bytes + + // positions are 24-bit + 1 padding byte each + mpob.posX = reader.ReadByte() | (reader.ReadByte() << 8) | (reader.ReadByte() << 16); + reader.ReadByte(); + mpob.posY = reader.ReadByte() | (reader.ReadByte() << 8) | (reader.ReadByte() << 16); + reader.ReadByte(); + mpob.posZ = reader.ReadByte() | (reader.ReadByte() << 8) | (reader.ReadByte() << 16); + + mpob.anglex = reader.ReadInt32(); // 4 bytes + mpob.angley = reader.ReadInt32(); // 4 bytes + mpob.anglez = reader.ReadInt32(); // 4 bytes + + reader.ReadInt16(); // textureData 2 bytes + mpob.intensity = reader.ReadInt16(); // 2 bytes + mpob.radius = reader.ReadInt16(); // 2 bytes + reader.ReadInt16(); // modelId 2 bytes + reader.ReadInt16(); // worldId 2 bytes + mpob.red = reader.ReadInt16(); // 2 bytes + mpob.green = reader.ReadInt16(); // 2 bytes + mpob.blue = reader.ReadInt16(); // 2 bytes + + try + { + string objectName = "B_" + i.ToString("D3") + "_" + (mpob.scriptName ?? "OBJ"); + GameObject go = new GameObject(objectName); + RGScriptedObject scripted = go.AddComponent(); + scripted.Instanciate(mpob, CurrentRgmData, paletteName); + ScriptedObjects[mpob.id] = scripted; + objects.Add(go); + } + catch (Exception ex) + { + Debug.LogWarning("[FFI] Failed to spawn scripted object " + mpob.scriptName + ": " + ex.Message); + } + + if (!string.IsNullOrEmpty(mpob.modelName) && !meshDict.ContainsKey(mpob.modelName)) + { + var loaded = TryLoadModelFile(mpob.modelName); + if (loaded.HasValue) + meshDict[mpob.modelName] = loaded.Value; + } + } + } + + private static string NormalizeModelName(string modelName) + => FFIPathUtils.NormalizeModelName(modelName); + + private static Shader GetDefaultShader() + { + if (defaultShader == null) + { + defaultShader = Shader.Find("Universal Render Pipeline/Lit"); + if (defaultShader == null) + { + defaultShader = Shader.Find("Standard"); + } + } + + return defaultShader; + } +} diff --git a/Assets/Scripts/FFI/FFIModelLoader.cs.meta b/Assets/Scripts/FFI/FFIModelLoader.cs.meta new file mode 100644 index 00000000..d0556ee0 --- /dev/null +++ b/Assets/Scripts/FFI/FFIModelLoader.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a18f5730b58357c4c8328cdecda32396 \ No newline at end of file diff --git a/Assets/Scripts/FFI/FFIPathUtils.cs b/Assets/Scripts/FFI/FFIPathUtils.cs new file mode 100644 index 00000000..5581fa4d --- /dev/null +++ b/Assets/Scripts/FFI/FFIPathUtils.cs @@ -0,0 +1,47 @@ +using System.IO; + +/// +/// Shared path resolution and name normalization utilities for FFI loaders. +/// +public static class FFIPathUtils +{ + /// + /// Resolves a case-insensitive file path by trying the uppercase extension first, + /// then the lowercase variant. Returns null if neither exists. + /// + public static string ResolveFile(string folder, string name, string extension) + { + string upper = Path.Combine(folder, name + extension); + if (File.Exists(upper)) + { + return upper; + } + + string lower = Path.Combine(folder, name.ToLowerInvariant() + extension.ToLowerInvariant()); + if (File.Exists(lower)) + { + return lower; + } + + return null; + } + + /// + /// Strips directory and extension from a model path, returning the uppercase stem. + /// e.g. "ART\models\CYRUS.3DC" → "CYRUS" + /// + public static string NormalizeModelName(string modelName) + { + if (string.IsNullOrEmpty(modelName)) + { + return string.Empty; + } + + string normalized = modelName.Replace('\\', '/'); + int slash = normalized.LastIndexOf('/'); + string file = slash >= 0 ? normalized.Substring(slash + 1) : normalized; + int dot = file.LastIndexOf('.'); + string stem = dot > 0 ? file.Substring(0, dot) : file; + return stem.ToUpperInvariant(); + } +} diff --git a/Assets/Scripts/FFI/FFIPathUtils.cs.meta b/Assets/Scripts/FFI/FFIPathUtils.cs.meta new file mode 100644 index 00000000..f5e520ab --- /dev/null +++ b/Assets/Scripts/FFI/FFIPathUtils.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a7c3e9f1b2d84e6a9f0c1d2e3f4a5b6c diff --git a/Assets/Scripts/FFI/FFISoundStore.cs b/Assets/Scripts/FFI/FFISoundStore.cs new file mode 100644 index 00000000..77af9831 --- /dev/null +++ b/Assets/Scripts/FFI/FFISoundStore.cs @@ -0,0 +1,223 @@ +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; + +public static class FFISoundStore +{ + private const int WavMinHeaderSize = 44; + private const float Pcm8Scale = 128f; + private const float Pcm16Scale = 32768f; + + public class RTXEntry + { + public string subtitle; + public AudioClip audio; + + public RTXEntry(string subtitleIn, AudioClip audioIn) + { + subtitle = subtitleIn; + audio = audioIn; + } + } + + public static List SfxClips { get; } = new List(); + public static Dictionary RtxEntries { get; } = new Dictionary(); + + public static RTXEntry GetRTX(int id) + { + if (RtxEntries.TryGetValue(id, out RTXEntry entry)) + { + return entry; + } + + throw new KeyNotFoundException("RTX id not loaded: " + id); + } + + public static AudioClip GetSFX(int id) + { + if (id >= 0 && id < SfxClips.Count) + { + return SfxClips[id]; + } + + throw new IndexOutOfRangeException("SFX id not loaded: " + id); + } + + // NOTE: Each ConvertRtxEntryToWav / GetRtxSubtitle call re-parses the full file + // on the native side. This is slow for large RTX files but acceptable as a one-time load. + public static void LoadRTX(string rtxName) + { + RtxEntries.Clear(); + + string soundFolder = Game.pathManager.GetSoundFolder(); + string path = FFIPathUtils.ResolveFile(soundFolder, rtxName, ".RTX"); + + if (string.IsNullOrEmpty(path) || !File.Exists(path)) + { + Debug.LogWarning("[FFI] RTX file not found: " + rtxName); + return; + } + + int count = RgpreBindings.RtxEntryCount(path); + for (int entryIndex = 0; entryIndex < count; entryIndex++) + { + IntPtr wavPtr = RgpreBindings.ConvertRtxEntryToWav(path, entryIndex); + AudioClip clip = null; + + if (wavPtr != IntPtr.Zero) + { + byte[] wavBytes = RgpreBindings.ExtractBytesAndFree(wavPtr); + clip = WavToAudioClip(wavBytes, "RTX_" + entryIndex); + } + + string subtitle = ""; + IntPtr subPtr = RgpreBindings.GetRtxSubtitle(path, entryIndex); + if (subPtr != IntPtr.Zero) + { + byte[] subBytes = RgpreBindings.ExtractBytesAndFree(subPtr); + if (subBytes.Length > 0) + subtitle = System.Text.Encoding.UTF8.GetString(subBytes); + } + + RtxEntries[entryIndex] = new RTXEntry(subtitle, clip); + } + + Debug.Log($"[FFI] Loaded {count} RTX entries from {rtxName}"); + } + + public static void LoadSFX(string sfxName) + { + SfxClips.Clear(); + + string soundFolder = Game.pathManager.GetSoundFolder(); + string path = FFIPathUtils.ResolveFile(soundFolder, sfxName, ".SFX"); + + if (string.IsNullOrEmpty(path) || !File.Exists(path)) + { + Debug.LogWarning("[FFI] SFX file not found: " + sfxName); + return; + } + + int count = RgpreBindings.SfxEffectCount(path); + for (int effectIndex = 0; effectIndex < count; effectIndex++) + { + IntPtr wavPtr = RgpreBindings.ConvertSfxToWav(path, effectIndex); + if (wavPtr == IntPtr.Zero) + { + SfxClips.Add(null); + continue; + } + + byte[] wavBytes = RgpreBindings.ExtractBytesAndFree(wavPtr); + SfxClips.Add(WavToAudioClip(wavBytes, "SFX_" + effectIndex)); + } + } + + private static AudioClip WavToAudioClip(byte[] wavBytes, string clipName) + { + if (!TryReadWav(wavBytes, out int channels, out int sampleRate, out int bitsPerSample, out byte[] pcmData)) + { + return null; + } + + float[] samples = bitsPerSample switch + { + 8 => Pcm8ToFloat(pcmData), + 16 => Pcm16ToFloat(pcmData), + _ => null + }; + + if (samples == null || channels <= 0) + { + return null; + } + + int sampleCount = samples.Length / channels; + if (sampleCount <= 0) + { + return null; + } + + AudioClip clip = AudioClip.Create(clipName, sampleCount, channels, sampleRate, false); + clip.SetData(samples, 0); + return clip; + } + + private static bool TryReadWav(byte[] wavBytes, out int channels, out int sampleRate, out int bitsPerSample, out byte[] pcmData) + { + channels = 0; + sampleRate = 0; + bitsPerSample = 0; + pcmData = null; + + if (wavBytes == null || wavBytes.Length < WavMinHeaderSize) + { + return false; + } + + using var stream = new MemoryStream(wavBytes); + using var reader = new BinaryReader(stream); + + string riff = new string(reader.ReadChars(4)); + reader.ReadInt32(); + string wave = new string(reader.ReadChars(4)); + if (riff != "RIFF" || wave != "WAVE") + { + return false; + } + + while (reader.BaseStream.Position + 8 <= reader.BaseStream.Length) + { + string chunkId = new string(reader.ReadChars(4)); + int chunkSize = reader.ReadInt32(); + long nextChunk = reader.BaseStream.Position + chunkSize; + + if (chunkId == "fmt ") + { + short format = reader.ReadInt16(); + channels = reader.ReadInt16(); + sampleRate = reader.ReadInt32(); + reader.ReadInt32(); + reader.ReadInt16(); + bitsPerSample = reader.ReadInt16(); + if (format != 1) + { + return false; + } + } + else if (chunkId == "data") + { + pcmData = reader.ReadBytes(chunkSize); + } + + reader.BaseStream.Position = nextChunk; + } + + return channels > 0 && sampleRate > 0 && bitsPerSample > 0 && pcmData != null; + } + + private static float[] Pcm8ToFloat(byte[] pcm) + { + float[] output = new float[pcm.Length]; + for (int i = 0; i < pcm.Length; i++) + { + output[i] = (pcm[i] - Pcm8Scale) / Pcm8Scale; + } + + return output; + } + + private static float[] Pcm16ToFloat(byte[] pcm) + { + int count = pcm.Length / 2; + float[] output = new float[count]; + for (int i = 0; i < count; i++) + { + short value = BitConverter.ToInt16(pcm, i * 2); + output[i] = value / Pcm16Scale; + } + + return output; + } +} diff --git a/Assets/Scripts/FFI/FFISoundStore.cs.meta b/Assets/Scripts/FFI/FFISoundStore.cs.meta new file mode 100644 index 00000000..07f6abd3 --- /dev/null +++ b/Assets/Scripts/FFI/FFISoundStore.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 24a1d4bb337b35a4cb6d1238b8a5856a \ No newline at end of file diff --git a/Assets/Scripts/FFI/FFITextureLoader.cs b/Assets/Scripts/FFI/FFITextureLoader.cs new file mode 100644 index 00000000..3d36d2e9 --- /dev/null +++ b/Assets/Scripts/FFI/FFITextureLoader.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using UnityEngine; + +public static class FFITextureLoader +{ + private const int PaletteHeaderSize = 8; + private const int PaletteColorCount = 256; + private const int PaletteStride = 3; + + private static readonly Dictionary textureCache = new Dictionary(); + private static readonly Dictionary> allFramesCache = new Dictionary>(); + private static readonly Dictionary paletteCache = new Dictionary(); + + public static void ClearCache() + { + foreach (var tex in textureCache.Values) + { + if (tex != null) + UnityEngine.Object.Destroy(tex); + } + foreach (var frames in allFramesCache.Values) + { + foreach (var tex in frames) + { + if (tex != null) + UnityEngine.Object.Destroy(tex); + } + } + textureCache.Clear(); + allFramesCache.Clear(); + paletteCache.Clear(); + } + + public static Texture2D DecodeTexture(ushort texbsiId, byte imageId, string paletteName) + { + string cacheKey = paletteName + "_" + texbsiId + "_" + imageId; + if (textureCache.TryGetValue(cacheKey, out var cachedTexture)) + return cachedTexture; + + string assetsDir = Game.pathManager.GetRootFolder(); + + IntPtr resultPtr = RgpreBindings.DecodeTexture(assetsDir, texbsiId, imageId); + if (resultPtr == IntPtr.Zero) + { + Debug.LogWarning("[FFI] Failed to decode texture TEXBSI." + texbsiId.ToString("D3") + + " image " + imageId + ": " + RgpreBindings.GetLastErrorMessage()); + return null; + } + + RgpreBindings.NativeBuffer buffer = RgpreBindings.ReadBuffer(resultPtr); + byte[] rgbaData; + int width; + int height; + try + { + int headerSize = Marshal.SizeOf(); + if (buffer.data == IntPtr.Zero || buffer.len < headerSize) + { + return null; + } + + var header = Marshal.PtrToStructure(buffer.data); + width = header.width; + height = header.height; + int rgbaSize = header.rgbaSize; + if (width <= 0 || height <= 0 || rgbaSize <= 0 || buffer.len < headerSize + rgbaSize) + { + return null; + } + + rgbaData = new byte[rgbaSize]; + Marshal.Copy(buffer.data + headerSize, rgbaData, 0, rgbaSize); + } + finally + { + RgpreBindings.FreeBuffer(buffer.handle); + } + + var texture = new Texture2D(width, height, TextureFormat.RGBA32, false) + { + name = "TEXBSI_" + texbsiId + "_" + imageId, + filterMode = FilterMode.Point, + wrapMode = TextureWrapMode.Repeat + }; + + texture.LoadRawTextureData(rgbaData); + texture.Apply(); + + textureCache[cacheKey] = texture; + return texture; + } + + public static List DecodeTextureAllFrames(ushort texbsiId, byte imageId, string paletteName) + { + string cacheKey = paletteName + "_" + texbsiId + "_" + imageId; + if (allFramesCache.TryGetValue(cacheKey, out var cached)) + return cached; + + string assetsDir = Game.pathManager.GetRootFolder(); + + IntPtr resultPtr = RgpreBindings.DecodeTextureAllFrames(assetsDir, texbsiId, imageId); + if (resultPtr == IntPtr.Zero) + { + Debug.LogWarning("[FFI] Failed to decode all frames for TEXBSI." + texbsiId.ToString("D3") + + " image " + imageId + ": " + RgpreBindings.GetLastErrorMessage()); + return null; + } + + RgpreBindings.NativeBuffer buffer = RgpreBindings.ReadBuffer(resultPtr); + var frames = new List(); + try + { + int headerSize = Marshal.SizeOf(); + if (buffer.data == IntPtr.Zero || buffer.len < headerSize) + return null; + + var header = Marshal.PtrToStructure(buffer.data); + if (header.width <= 0 || header.height <= 0 || header.frameCount <= 0) + return null; + + IntPtr ptr = buffer.data + headerSize; + IntPtr end = buffer.data + buffer.len; + + for (int i = 0; i < header.frameCount; i++) + { + if ((end.ToInt64() - ptr.ToInt64()) < 4) + break; + + int rgbaSize = Marshal.ReadInt32(ptr); + ptr += 4; + + if (rgbaSize <= 0 || (end.ToInt64() - ptr.ToInt64()) < rgbaSize) + break; + + byte[] rgbaData = new byte[rgbaSize]; + Marshal.Copy(ptr, rgbaData, 0, rgbaSize); + ptr += rgbaSize; + + var texture = new Texture2D(header.width, header.height, TextureFormat.RGBA32, false) + { + name = "TEXBSI_" + texbsiId + "_" + imageId + "_F" + i, + filterMode = FilterMode.Point, + wrapMode = TextureWrapMode.Repeat + }; + texture.LoadRawTextureData(rgbaData); + texture.Apply(); + frames.Add(texture); + } + } + finally + { + RgpreBindings.FreeBuffer(buffer.handle); + } + + allFramesCache[cacheKey] = frames; + return frames; + } + + public static Color GetPaletteColor(byte colorIndex, string paletteName) + { + if (TryGetPalette(paletteName, out Color32[] colors) && colorIndex < colors.Length) + return colors[colorIndex]; + + return Color.magenta; + } + + private static bool TryGetPalette(string paletteName, out Color32[] colors) + { + if (paletteCache.TryGetValue(paletteName, out colors)) + return true; + + string artFolder = Game.pathManager.GetArtFolder(); + string palettePath = ResolvePalettePath(artFolder, paletteName); + if (string.IsNullOrEmpty(palettePath)) + { + colors = null; + return false; + } + + byte[] bytes = File.ReadAllBytes(palettePath); + int dataOffset = bytes.Length >= PaletteHeaderSize + PaletteColorCount * PaletteStride ? PaletteHeaderSize : 0; + if (bytes.Length < dataOffset + PaletteColorCount * PaletteStride) + { + colors = null; + return false; + } + + colors = new Color32[PaletteColorCount]; + for (int i = 0; i < PaletteColorCount; i++) + { + int start = dataOffset + i * PaletteStride; + colors[i] = new Color32(bytes[start], bytes[start + 1], bytes[start + 2], 255); + } + + paletteCache[paletteName] = colors; + return true; + } + + private static string ResolvePalettePath(string artFolder, string paletteName) + => FFIPathUtils.ResolveFile(artFolder, paletteName, ".COL"); +} diff --git a/Assets/Scripts/FFI/FFITextureLoader.cs.meta b/Assets/Scripts/FFI/FFITextureLoader.cs.meta new file mode 100644 index 00000000..dce2b96e --- /dev/null +++ b/Assets/Scripts/FFI/FFITextureLoader.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1cd0f8df5be40d14ead4381a759faca4 \ No newline at end of file diff --git a/Assets/Scripts/FFI/FFIWorldStore.cs b/Assets/Scripts/FFI/FFIWorldStore.cs new file mode 100644 index 00000000..40073dbb --- /dev/null +++ b/Assets/Scripts/FFI/FFIWorldStore.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; + +public static class FFIWorldStore +{ + public struct WorldData + { + public string RGM; + public string WLD; + public string COL; + public string skyBoxGXA; + public string skyBoxBSI; + public string sunImage; + public string loadScreen; + } + + private static Dictionary cachedWorldList; + + public static Dictionary GetWorldList() + { + if (cachedWorldList != null) + return cachedWorldList; + + cachedWorldList = new Dictionary(); + string worldIniPath = Path.Combine(Game.pathManager.GetRootFolder(), "WORLD.INI"); + if (!File.Exists(worldIniPath)) + { + Debug.LogWarning("[FFIWorldStore] WORLD.INI not found: " + worldIniPath); + return cachedWorldList; + } + + var worlds = new Dictionary(); + string[] lines = File.ReadAllLines(worldIniPath); + + foreach (string rawLine in lines) + { + string line = rawLine.Trim(); + if (line.Length == 0 || line[0] == ';' || line[0] == '[') + continue; + + int eqIndex = line.IndexOf('='); + if (eqIndex < 0) + continue; + + string key = line.Substring(0, eqIndex).Trim(); + string val = line.Substring(eqIndex + 1).Trim(); + + // Parse indexed keys like world_map[1] + int bracketOpen = key.IndexOf('['); + if (bracketOpen < 0) + continue; + + int bracketClose = key.IndexOf(']', bracketOpen); + if (bracketClose < 0) + continue; + + if (!int.TryParse(key.Substring(bracketOpen + 1, bracketClose - bracketOpen - 1), out int worldIndex)) + continue; + + string memberName = key.Substring(0, bracketOpen); + + if (!worlds.TryGetValue(worldIndex, out WorldData w)) + { + w = new WorldData + { + RGM = string.Empty, + WLD = string.Empty, + COL = string.Empty, + skyBoxGXA = string.Empty, + skyBoxBSI = string.Empty, + sunImage = string.Empty, + loadScreen = string.Empty + }; + } + + switch (memberName) + { + case "world_map": + w.RGM = ToUpperStem(val); + break; + case "world_world": + w.WLD = ToUpperStem(val); + break; + case "world_palette": + w.COL = ToUpperStem(val); + break; + case "world_sky": + w.skyBoxGXA = ToUpperStem(val); + break; + case "world_skyfx": + w.skyBoxBSI = ToUpperStem(val); + break; + case "world_sunimg": + w.sunImage = ToUpperStem(val); + break; + case "world_flash_filename": + w.loadScreen = ToUpperStem(val); + break; + } + + worlds[worldIndex] = w; + } + + cachedWorldList = worlds; + return cachedWorldList; + } + + public static void ClearCache() + { + cachedWorldList = null; + } + + private static string ToUpperStem(string path) + => FFIPathUtils.NormalizeModelName(path); +} diff --git a/Assets/Scripts/FFI/FFIWorldStore.cs.meta b/Assets/Scripts/FFI/FFIWorldStore.cs.meta new file mode 100644 index 00000000..4cf5c1fa --- /dev/null +++ b/Assets/Scripts/FFI/FFIWorldStore.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 17c8ae5f1fd2ac243810e717f9fe35fb \ No newline at end of file diff --git a/Assets/Scripts/FFI/RgmdDeserializer.cs b/Assets/Scripts/FFI/RgmdDeserializer.cs new file mode 100644 index 00000000..69126406 --- /dev/null +++ b/Assets/Scripts/FFI/RgmdDeserializer.cs @@ -0,0 +1,236 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using UnityEngine; +using UnityEngine.Rendering; + +public static class RgmdDeserializer +{ + private const int RgmdMagic = 0x444D4752; // "RGMD" + private static readonly int RobHeaderSize = Marshal.SizeOf(); + private static readonly int RobSegmentHeaderSize = Marshal.SizeOf(); + private static readonly int RgmdHeaderSize = Marshal.SizeOf(); + private static readonly int RgmdSubmeshHeaderSize = Marshal.SizeOf(); + private static readonly int RgmdVertexSize = Marshal.SizeOf(); + + public struct SubmeshMaterialInfo + { + public bool isSolidColor; + public Color32 solidColor; // resolved RGB from native (solid colors only) + public ushort textureId; + public byte imageId; + } + + public static List<(string name, Mesh mesh)> DeserializeRob(IntPtr bufferHandle, out int segmentCount, out int segmentsWithModel) + { + var data = DeserializeRobWithMaterials(bufferHandle, out segmentCount, out segmentsWithModel); + var result = new List<(string name, Mesh mesh)>(data.Count); + for (int i = 0; i < data.Count; i++) + { + result.Add((data[i].name, data[i].mesh)); + } + + return result; + } + + public static List<(string name, Mesh mesh, SubmeshMaterialInfo[] materials)> DeserializeRobWithMaterials(IntPtr bufferHandle, out int segmentCount, out int segmentsWithModel) + { + var result = new List<(string name, Mesh mesh, SubmeshMaterialInfo[] materials)>(); + segmentCount = 0; + segmentsWithModel = 0; + + RgpreBindings.NativeBuffer buffer = RgpreBindings.ReadBuffer(bufferHandle); + try + { + if (buffer.data == IntPtr.Zero || buffer.len < RobHeaderSize) + { + return result; + } + + IntPtr ptr = buffer.data; + IntPtr end = buffer.data + buffer.len; + var robHeader = Marshal.PtrToStructure(ptr); + segmentCount = robHeader.segmentCount; + ptr += RobHeaderSize; + + for (int i = 0; i < segmentCount; i++) + { + EnsureReadable(ptr, RobSegmentHeaderSize, end, "ROB segment header"); + var segment = Marshal.PtrToStructure(ptr); + ptr += RobSegmentHeaderSize; + + string name = DecodeRobSegmentName(segment.segmentName); + int modelSize = segment.modelDataSize; + + if (segment.hasModel == 0 || modelSize <= 0) + { + continue; + } + + segmentsWithModel++; + EnsureReadable(ptr, modelSize, end, $"ROB segment '{name}' RGMD payload"); + + var (mesh, materials, _) = ParseRgmdMesh(ptr, ptr + modelSize, name); + ptr += modelSize; + if (mesh != null) + { + result.Add((name, mesh, materials)); + } + } + } + finally + { + RgpreBindings.FreeBuffer(buffer.handle); + } + + return result; + } + + public static (Mesh mesh, SubmeshMaterialInfo[] materials, int frameCount) DeserializeModel(IntPtr bufferHandle, string name) + { + RgpreBindings.NativeBuffer buffer = RgpreBindings.ReadBuffer(bufferHandle); + try + { + if (buffer.data == IntPtr.Zero || buffer.len < RgmdHeaderSize) + { + return (null, Array.Empty(), 0); + } + + return ParseRgmdMesh(buffer.data, buffer.data + buffer.len, name); + } + finally + { + RgpreBindings.FreeBuffer(buffer.handle); + } + } + + private static (Mesh mesh, SubmeshMaterialInfo[] materials, int frameCount) ParseRgmdMesh(IntPtr ptr, IntPtr end, string segmentName) + { + EnsureReadable(ptr, RgmdHeaderSize, end, $"RGMD header for '{segmentName}'"); + var rgmdHeader = Marshal.PtrToStructure(ptr); + if (rgmdHeader.magic != RgmdMagic) + { + string magicText = Encoding.ASCII.GetString(BitConverter.GetBytes(rgmdHeader.magic)); + throw new InvalidOperationException($"Segment '{segmentName}' has invalid RGMD magic '{magicText}'."); + } + + int submeshCount = rgmdHeader.submeshCount; + int frameCount = rgmdHeader.frameCount; + int totalVertexCount = rgmdHeader.totalVertexCount; + int totalIndexCount = rgmdHeader.totalIndexCount; + + if (submeshCount <= 0 || totalVertexCount <= 0 || totalIndexCount <= 0) + { + return (null, Array.Empty(), frameCount); + } + + var vertices = new List(Math.Max(totalVertexCount, 0)); + var normals = new List(Math.Max(totalVertexCount, 0)); + var uvs = new List(Math.Max(totalVertexCount, 0)); + var allIndices = new List(Math.Max(totalIndexCount, 0)); + var subMeshes = new List(Math.Max(submeshCount, 0)); + var submeshMaterials = new SubmeshMaterialInfo[submeshCount]; + + ptr += RgmdHeaderSize; + for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) + { + EnsureReadable(ptr, RgmdSubmeshHeaderSize, end, $"submesh header {submeshIndex} in '{segmentName}'"); + var submeshHeader = Marshal.PtrToStructure(ptr); + ptr += RgmdSubmeshHeaderSize; + + submeshMaterials[submeshIndex] = new SubmeshMaterialInfo + { + isSolidColor = submeshHeader.textured == 0, + solidColor = new Color32(submeshHeader.colorR, submeshHeader.colorG, submeshHeader.colorB, 255), + textureId = submeshHeader.textureId, + imageId = submeshHeader.imageId + }; + + int vertexCount = submeshHeader.vertexCount; + int indexCount = submeshHeader.indexCount; + if (vertexCount < 0 || indexCount < 0) + { + throw new InvalidOperationException($"Segment '{segmentName}' has negative counts in submesh {submeshIndex}."); + } + + int submeshVertexStart = vertices.Count; + for (int v = 0; v < vertexCount; v++) + { + EnsureReadable(ptr, RgmdVertexSize, end, $"vertex {v} in submesh {submeshIndex} of '{segmentName}'"); + var vertex = Marshal.PtrToStructure(ptr); + ptr += RgmdVertexSize; + + vertices.Add(new Vector3(vertex.px, vertex.py, vertex.pz)); + normals.Add(new Vector3(vertex.nx, vertex.ny, vertex.nz)); + uvs.Add(new Vector2(vertex.u, vertex.v)); + } + + int submeshIndexStart = allIndices.Count; + if (indexCount > 0) + { + EnsureReadable(ptr, indexCount * sizeof(uint), end, $"indices in submesh {submeshIndex} of '{segmentName}'"); + var indexWords = new int[indexCount]; + Marshal.Copy(ptr, indexWords, 0, indexCount); + ptr += indexCount * sizeof(uint); + + for (int idx = 0; idx < indexCount; idx++) + { + uint raw = unchecked((uint)indexWords[idx]); + if (raw > int.MaxValue) + { + throw new InvalidOperationException($"Segment '{segmentName}' has index value too large in submesh {submeshIndex}."); + } + + allIndices.Add(submeshVertexStart + (int)raw); + } + } + + subMeshes.Add(new SubMeshDescriptor(submeshIndexStart, indexCount, MeshTopology.Triangles)); + } + + var mesh = new Mesh + { + name = segmentName + }; + + mesh.indexFormat = vertices.Count > 65535 ? IndexFormat.UInt32 : IndexFormat.UInt16; + mesh.SetVertices(vertices); + mesh.SetNormals(normals); + mesh.SetUVs(0, uvs); + + mesh.SetIndexBufferParams(allIndices.Count, mesh.indexFormat); + if (mesh.indexFormat == IndexFormat.UInt16) + { + var indices16 = new ushort[allIndices.Count]; + for (int i = 0; i < allIndices.Count; i++) + { + indices16[i] = (ushort)allIndices[i]; + } + + mesh.SetIndexBufferData(indices16, 0, 0, indices16.Length); + } + else + { + mesh.SetIndexBufferData(allIndices, 0, 0, allIndices.Count); + } + + mesh.subMeshCount = subMeshes.Count; + for (int i = 0; i < subMeshes.Count; i++) + { + mesh.SetSubMesh(i, subMeshes[i], MeshUpdateFlags.DontRecalculateBounds); + } + + mesh.RecalculateBounds(); + return (mesh, submeshMaterials, frameCount); + } + + private static string DecodeRobSegmentName(long rawName) + { + byte[] nameBytes = BitConverter.GetBytes(rawName); + return Encoding.ASCII.GetString(nameBytes).TrimEnd('\0').Trim(); + } + + private static void EnsureReadable(IntPtr ptr, int bytesNeeded, IntPtr end, string label) + => RgpreBindings.EnsureReadable(ptr, bytesNeeded, end, label); +} diff --git a/Assets/Scripts/FFI/RgmdDeserializer.cs.meta b/Assets/Scripts/FFI/RgmdDeserializer.cs.meta new file mode 100644 index 00000000..eb124214 --- /dev/null +++ b/Assets/Scripts/FFI/RgmdDeserializer.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 896f0ebdc6135d44a8f91c8ffee65aed \ No newline at end of file diff --git a/Assets/Scripts/FFI/RgplDeserializer.cs b/Assets/Scripts/FFI/RgplDeserializer.cs new file mode 100644 index 00000000..da1f6239 --- /dev/null +++ b/Assets/Scripts/FFI/RgplDeserializer.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using UnityEngine; + +public static class RgplDeserializer +{ + private const int RgplMagic = 0x4C504752; // "RGPL" + private static readonly int RgplHeaderSize = Marshal.SizeOf(); + private static readonly int RgplPlacementSize = Marshal.SizeOf(); + private static readonly int RgplLightSize = Marshal.SizeOf(); + + public struct Placement + { + public string modelName; + public string sourceId; + public Matrix4x4 transform; + public byte type; + public ushort textureId; + public byte imageId; + } + + public struct LightData + { + public string name; + public Color color; + public Vector3 position; + public float range; + } + + public struct RgplData + { + public List placements; + public List lights; + } + + public static RgplData Deserialize(IntPtr bufferHandle) + { + var result = new RgplData(); + result.placements = new List(); + result.lights = new List(); + + RgpreBindings.NativeBuffer buffer = RgpreBindings.ReadBuffer(bufferHandle); + try + { + if (buffer.data == IntPtr.Zero || buffer.len < RgplHeaderSize) + { + return result; + } + + IntPtr ptr = buffer.data; + IntPtr end = buffer.data + buffer.len; + + var header = Marshal.PtrToStructure(ptr); + if (header.magic != RgplMagic) + { + string magicText = Encoding.ASCII.GetString(BitConverter.GetBytes(header.magic)); + throw new InvalidOperationException("Invalid RGPL magic: " + magicText); + } + + int placementCount = header.placementCount; + int lightCount = header.lightCount; + ptr += RgplHeaderSize; + + for (int i = 0; i < placementCount; i++) + { + EnsureReadable(ptr, RgplPlacementSize, end, "RGPL placement"); + var src = Marshal.PtrToStructure(ptr); + ptr += RgplPlacementSize; + + result.placements.Add(new Placement + { + modelName = ReadFixedString(src.modelName), + sourceId = ReadFixedString(src.sourceId), + transform = ReadTransform(src.transform), + textureId = src.textureId, + imageId = src.imageId, + type = src.objectType + }); + } + + for (int i = 0; i < lightCount; i++) + { + EnsureReadable(ptr, RgplLightSize, end, "RGPL light"); + var src = Marshal.PtrToStructure(ptr); + ptr += RgplLightSize; + + result.lights.Add(new LightData + { + name = ReadFixedString(ReadLightNameBytes(src)), + color = new Color(src.r, src.g, src.b), + position = new Vector3(src.posX, src.posY, src.posZ), + range = src.range + }); + } + } + finally + { + RgpreBindings.FreeBuffer(buffer.handle); + } + + return result; + } + + private static string ReadFixedString(byte[] raw) + { + if (raw == null || raw.Length == 0) + { + return string.Empty; + } + + int end = Array.IndexOf(raw, (byte)0); + if (end < 0) + { + end = raw.Length; + } + + return Encoding.ASCII.GetString(raw, 0, end).Trim(); + } + + private static Matrix4x4 ReadTransform(float[] values) + { + var matrix = new Matrix4x4(); + if (values == null || values.Length < 16) + { + return matrix; + } + + int index = 0; + for (int c = 0; c < 4; c++) + { + for (int r = 0; r < 4; r++) + { + matrix[r, c] = values[index++]; + } + } + + return matrix; + } + + private static byte[] ReadLightNameBytes(RgpreBindings.RgplLight light) + { + byte[] name = new byte[32]; + Buffer.BlockCopy(BitConverter.GetBytes(light.name0), 0, name, 0, 8); + Buffer.BlockCopy(BitConverter.GetBytes(light.name1), 0, name, 8, 8); + Buffer.BlockCopy(BitConverter.GetBytes(light.name2), 0, name, 16, 8); + Buffer.BlockCopy(BitConverter.GetBytes(light.name3), 0, name, 24, 8); + return name; + } + + private static void EnsureReadable(IntPtr ptr, int bytesNeeded, IntPtr end, string label) + => RgpreBindings.EnsureReadable(ptr, bytesNeeded, end, label); +} diff --git a/Assets/Scripts/FFI/RgplDeserializer.cs.meta b/Assets/Scripts/FFI/RgplDeserializer.cs.meta new file mode 100644 index 00000000..841d53f8 --- /dev/null +++ b/Assets/Scripts/FFI/RgplDeserializer.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 179d1fb8563e33148ac6bdd621f439aa \ No newline at end of file diff --git a/Assets/Scripts/FFI/RgpreBindings.cs b/Assets/Scripts/FFI/RgpreBindings.cs new file mode 100644 index 00000000..65eee6b5 --- /dev/null +++ b/Assets/Scripts/FFI/RgpreBindings.cs @@ -0,0 +1,359 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; + +public static class RgpreBindings +{ + [StructLayout(LayoutKind.Sequential)] + public struct ByteBuffer + { + public IntPtr ptr; + public int len; + } + + public struct NativeBuffer + { + public IntPtr data; + public int len; + public IntPtr handle; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct TextureHeader + { + public int width; + public int height; + public int frameCount; + public int rgbaSize; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct AllFramesHeader + { + public int width; + public int height; + public int frameCount; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct RgmdHeader + { + public int magic; + public int version; + public int submeshCount; + public int frameCount; + public int totalVertexCount; + public int totalIndexCount; + public uint radius; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct RgmdSubmeshHeader + { + public byte textured; // 0 = solid color (color holds resolved RGB), 1 = textured + public byte colorR; // resolved RGB red (solid) or 0 (textured) + public byte colorG; // resolved RGB green (solid) or 0 (textured) + public byte colorB; // resolved RGB blue (solid) or 0 (textured) + public ushort textureId; // TEXBSI id (textured) or 0 (solid) + public byte imageId; // TEXBSI image (textured) or 0 (solid) + private byte _pad; + public int vertexCount; + public int indexCount; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct RgmdVertex + { + public float px; + public float py; + public float pz; + public float nx; + public float ny; + public float nz; + public float u; + public float v; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct RobHeader + { + public int segmentCount; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct RobSegmentHeader + { + public long segmentName; + public byte hasModel; + private byte _pad0; + private byte _pad1; + private byte _pad2; + public int modelDataSize; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct RgplHeader + { + public int magic; + public int placementCount; + public int lightCount; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct RgplPlacement + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] modelName; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] sourceId; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public float[] transform; + public ushort textureId; + public byte imageId; + public byte objectType; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct RgplLight + { + public long name0; + public long name1; + public long name2; + public long name3; + public float r; + public float g; + public float b; + public float posX; + public float posY; + public float posZ; + public float range; + } + + // ========== Native imports ========== + + [DllImport("rgpre", EntryPoint = "rg_free_buffer", CallingConvention = CallingConvention.Cdecl)] + private static extern void FreeBufferNative(IntPtr buffer); + + [DllImport("rgpre", EntryPoint = "rg_last_error", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr LastErrorNative(); + + // --- Scene data (RGMD binary) --- + + [DllImport("rgpre", EntryPoint = "rg_parse_model_data", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ParseModelDataNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath, + [MarshalAs(UnmanagedType.LPUTF8Str)] string assetsDir); + + [DllImport("rgpre", EntryPoint = "rg_parse_rob_data", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ParseRobDataNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath, + [MarshalAs(UnmanagedType.LPUTF8Str)] string assetsDir); + + [DllImport("rgpre", EntryPoint = "rg_parse_wld_terrain_data", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ParseWldTerrainDataNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath); + + [DllImport("rgpre", EntryPoint = "rg_parse_rgm_placements", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ParseRgmPlacementsNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath); + + // --- RGM section access --- + + [DllImport("rgpre", EntryPoint = "rg_rgm_section_count", CallingConvention = CallingConvention.Cdecl)] + private static extern int RgmSectionCountNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath, + [MarshalAs(UnmanagedType.LPUTF8Str)] string sectionTag); + + [DllImport("rgpre", EntryPoint = "rg_get_rgm_section", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr GetRgmSectionNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath, + [MarshalAs(UnmanagedType.LPUTF8Str)] string sectionTag, + int sectionIndex); + + // --- GLB export --- + + [DllImport("rgpre", EntryPoint = "rg_convert_model_from_path", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ConvertModelFromPathNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath, + [MarshalAs(UnmanagedType.LPUTF8Str)] string assetsDir); + + [DllImport("rgpre", EntryPoint = "rg_convert_rgm_from_path", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ConvertRgmFromPathNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath, + [MarshalAs(UnmanagedType.LPUTF8Str)] string assetsDir); + + [DllImport("rgpre", EntryPoint = "rg_convert_wld_from_path", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ConvertWldFromPathNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath, + [MarshalAs(UnmanagedType.LPUTF8Str)] string assetsDir); + + // --- Texture --- + + [DllImport("rgpre", EntryPoint = "rg_decode_texture", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr DecodeTextureNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string assetsDir, + ushort textureId, + byte imageId); + + [DllImport("rgpre", EntryPoint = "rg_decode_texture_all_frames", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr DecodeTextureAllFramesNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string assetsDir, + ushort textureId, + byte imageId); + + [DllImport("rgpre", EntryPoint = "rg_texbsi_image_count", CallingConvention = CallingConvention.Cdecl)] + private static extern int TexbsiImageCountNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string assetsDir, + ushort textureId); + + // --- Audio --- + + [DllImport("rgpre", EntryPoint = "rg_sfx_effect_count", CallingConvention = CallingConvention.Cdecl)] + private static extern int SfxEffectCountNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath); + + [DllImport("rgpre", EntryPoint = "rg_convert_sfx_to_wav", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ConvertSfxToWavNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath, + int effectIndex); + + [DllImport("rgpre", EntryPoint = "rg_rtx_entry_count", CallingConvention = CallingConvention.Cdecl)] + private static extern int RtxEntryCountNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath); + + [DllImport("rgpre", EntryPoint = "rg_convert_rtx_entry_to_wav", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ConvertRtxEntryToWavNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath, + int entryIndex); + + [DllImport("rgpre", EntryPoint = "rg_get_rtx_subtitle", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr GetRtxSubtitleNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath, + int entryIndex); + + // --- GXA --- + + [DllImport("rgpre", EntryPoint = "rg_gxa_frame_count", CallingConvention = CallingConvention.Cdecl)] + private static extern int GxaFrameCountNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath); + + [DllImport("rgpre", EntryPoint = "rg_decode_gxa", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr DecodeGxaNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath, + int frame); + + // --- Font --- + + [DllImport("rgpre", EntryPoint = "rg_convert_fnt_to_ttf", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ConvertFntToTtfNative( + [MarshalAs(UnmanagedType.LPUTF8Str)] string filePath); + + // ========== Public API ========== + + // --- Scene data --- + + public static IntPtr ParseModelData(string filePath, string assetsDir) => ParseModelDataNative(filePath, assetsDir); + public static IntPtr ParseRobData(string filePath, string assetsDir) => ParseRobDataNative(filePath, assetsDir); + public static IntPtr ParseWldTerrainData(string filePath) => ParseWldTerrainDataNative(filePath); + public static IntPtr ParseRgmPlacements(string filePath) => ParseRgmPlacementsNative(filePath); + + // --- RGM sections --- + + public static int RgmSectionCount(string filePath, string sectionTag) => RgmSectionCountNative(filePath, sectionTag); + public static IntPtr GetRgmSection(string filePath, string sectionTag, int sectionIndex = 0) => GetRgmSectionNative(filePath, sectionTag, sectionIndex); + + // --- GLB export --- + + public static IntPtr ConvertModelFromPath(string filePath, string assetsDir) => ConvertModelFromPathNative(filePath, assetsDir); + public static IntPtr ConvertRgmFromPath(string filePath, string assetsDir) => ConvertRgmFromPathNative(filePath, assetsDir); + public static IntPtr ConvertWldFromPath(string filePath, string assetsDir) => ConvertWldFromPathNative(filePath, assetsDir); + + // --- Texture --- + + public static IntPtr DecodeTexture(string assetsDir, ushort textureId, byte imageId) => DecodeTextureNative(assetsDir, textureId, imageId); + public static IntPtr DecodeTextureAllFrames(string assetsDir, ushort textureId, byte imageId) => DecodeTextureAllFramesNative(assetsDir, textureId, imageId); + public static int TexbsiImageCount(string assetsDir, ushort textureId) => TexbsiImageCountNative(assetsDir, textureId); + + // --- Audio --- + + public static int SfxEffectCount(string filePath) => SfxEffectCountNative(filePath); + public static IntPtr ConvertSfxToWav(string filePath, int effectIndex) => ConvertSfxToWavNative(filePath, effectIndex); + public static int RtxEntryCount(string filePath) => RtxEntryCountNative(filePath); + public static IntPtr ConvertRtxEntryToWav(string filePath, int entryIndex) => ConvertRtxEntryToWavNative(filePath, entryIndex); + public static IntPtr GetRtxSubtitle(string filePath, int entryIndex) => GetRtxSubtitleNative(filePath, entryIndex); + + // --- GXA --- + + public static int GxaFrameCount(string filePath) => GxaFrameCountNative(filePath); + public static IntPtr DecodeGxa(string filePath, int frame) => DecodeGxaNative(filePath, frame); + + // --- Font --- + + public static IntPtr ConvertFntToTtf(string filePath) => ConvertFntToTtfNative(filePath); + + // ========== Utilities ========== + + public static byte[] ExtractBytesAndFree(IntPtr bufferPtr) + { + if (bufferPtr == IntPtr.Zero) + return Array.Empty(); + + ByteBuffer buffer = Marshal.PtrToStructure(bufferPtr); + byte[] managed = buffer.len > 0 ? new byte[buffer.len] : Array.Empty(); + if (buffer.len > 0 && buffer.ptr != IntPtr.Zero) + Marshal.Copy(buffer.ptr, managed, 0, buffer.len); + + FreeBufferNative(bufferPtr); + return managed; + } + + public static NativeBuffer ReadBuffer(IntPtr bufferPtr) + { + if (bufferPtr == IntPtr.Zero) + { + return new NativeBuffer { data = IntPtr.Zero, len = 0, handle = IntPtr.Zero }; + } + + ByteBuffer buffer = Marshal.PtrToStructure(bufferPtr); + return new NativeBuffer { data = buffer.ptr, len = buffer.len, handle = bufferPtr }; + } + + public static void FreeBuffer(IntPtr handle) + { + if (handle != IntPtr.Zero) + { + FreeBufferNative(handle); + } + } + + public static string GetLastErrorMessage() + { + IntPtr errPtr = LastErrorNative(); + if (errPtr == IntPtr.Zero) + return string.Empty; + + byte[] bytes = ExtractBytesAndFree(errPtr); + return bytes.Length == 0 ? string.Empty : Encoding.UTF8.GetString(bytes); + } + + // ========== Shared deserialization helpers ========== + + /// + /// Validates that bytes can be read from + /// without exceeding . Throws on failure. + /// + public static void EnsureReadable(IntPtr ptr, int bytesNeeded, IntPtr end, string label) + { + if (bytesNeeded < 0) + { + throw new System.InvalidOperationException($"Negative byte count for {label}: {bytesNeeded}."); + } + + long current = ptr.ToInt64(); + long final_ = end.ToInt64(); + if (current < 0 || final_ < current || current + bytesNeeded > final_) + { + throw new System.InvalidOperationException($"Unexpected end of data while reading {label}."); + } + } +} diff --git a/Assets/Scripts/FFI/RgpreBindings.cs.meta b/Assets/Scripts/FFI/RgpreBindings.cs.meta new file mode 100644 index 00000000..3e388c99 --- /dev/null +++ b/Assets/Scripts/FFI/RgpreBindings.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9d1e0b4fa8eafef499263b438aa73f47 \ No newline at end of file diff --git a/Assets/Scripts/GameTest/LoadWorld.cs b/Assets/Scripts/GameTest/LoadWorld.cs index f4013fbd..0f3b2285 100644 --- a/Assets/Scripts/GameTest/LoadWorld.cs +++ b/Assets/Scripts/GameTest/LoadWorld.cs @@ -23,16 +23,7 @@ void Start() // WorldLoader.RequestLoadWorld(1,22,1024); WorldLoader.LoadWorldIfRequested(); -RG3DStore.LoadMeshIntermediatesROB("INVENTRY"); -/* -Dictionary itemdict; - itemdict = RGINIStore.GetItemList(); -foreach(KeyValuePair entry in itemdict) -{ - Debug.LogWarning($"item {entry.Key}: {entry.Value.name} | {entry.Value.description}"); -} -*/ - } + } // Update is called once per frame void Update() { @@ -42,8 +33,11 @@ void Update() Game.uiManager.HideLoadingScreen(); if(Input.GetKeyDown("space")) { - Material loadScreenMat = RGTexStore.GetMaterial_GXA("ISLAND", 0); - Game.uiManager.ShowLoadingScreen(loadScreenMat.mainTexture); + Material loadScreenMat = FFIGxaLoader.GetMaterial_GXA("ISLAND", 0); + if (loadScreenMat != null) + { + Game.uiManager.ShowLoadingScreen(loadScreenMat.mainTexture); + } } } } diff --git a/Assets/Scripts/GameTest/MenuTest.cs b/Assets/Scripts/GameTest/MenuTest.cs index 7ea80a17..c9511ce4 100644 --- a/Assets/Scripts/GameTest/MenuTest.cs +++ b/Assets/Scripts/GameTest/MenuTest.cs @@ -1,25 +1,10 @@ using UnityEngine; using UnityEngine.UI; -using RGFileImport; using System.Collections.Generic; public class MenuTest : MonoBehaviour { // Start is called once before the first execution of Update after the MonoBehaviour is created - /* - RGINIFile iniData; - Canvas canvas; - List textObjects; - List textContents; - GameObject text1; - Text text1text; - void Start() - { - iniData = new RGINIFile(); - iniData.LoadFile($"{Game.pathManager.GetRootFolder()}/MENU.INI"); - } - */ - // Update is called once per frame void Start() { diff --git a/Assets/Scripts/GameTest/PlayerMain.cs b/Assets/Scripts/GameTest/PlayerMain.cs index b34e174c..78deebef 100644 --- a/Assets/Scripts/GameTest/PlayerMain.cs +++ b/Assets/Scripts/GameTest/PlayerMain.cs @@ -113,8 +113,11 @@ private void doAnimStateMachine() player = RGObjectStore.GetPlayer(); currentAnimState = AnimState.state_panic; wantAnimState = AnimState.state_panic; - player.animations.shouldExitFcn = animShouldExitFcn; - player.animations.doExitFcn = animDoExitFcn; + if (player != null && player.animations != null) + { + player.animations.shouldExitFcn = animShouldExitFcn; + player.animations.doExitFcn = animDoExitFcn; + } animDoExitFcn(); break; diff --git a/Assets/Scripts/ModelViewer/Export/Export.asmdef b/Assets/Scripts/ModelViewer/Export/Export.asmdef deleted file mode 100644 index 7c0f8635..00000000 --- a/Assets/Scripts/ModelViewer/Export/Export.asmdef +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "Export", - "rootNamespace": "", - "references": [ - "GUID:a42927d1d4a3b4cda9b076a7adecb9cc", - "GUID:5e7ba8d6d26c84943b64dd07bd1c9510", - "GUID:052addc0dd368b14b8a1312c050831fb" - ], - "includePlatforms": [], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": false, - "precompiledReferences": [], - "autoReferenced": true, - "defineConstraints": [], - "versionDefines": [], - "noEngineReferences": false -} \ No newline at end of file diff --git a/Assets/Scripts/ModelViewer/Export/GLTFExporter.cs b/Assets/Scripts/ModelViewer/Export/GLTFExporter.cs index faa0ae82..7c7a0c8b 100644 --- a/Assets/Scripts/ModelViewer/Export/GLTFExporter.cs +++ b/Assets/Scripts/ModelViewer/Export/GLTFExporter.cs @@ -1,55 +1,136 @@ using System; using System.IO; -using System.Threading.Tasks; using UnityEngine; -using GLTFast.Export; using SFB; public class GLTFExporter : MonoBehaviour { - public async Task ExportGLTF(GameObject obj, string objectName) + [HideInInspector] public string modelName; + [HideInInspector] public ModelFileType fileType; + [HideInInspector] public string colName; + [HideInInspector] public string rgmName; + [HideInInspector] public string wldName; + [HideInInspector] public bool isAreaExport; + + public void ExportGLTF(string objectName) { - var extensionList = new [] { + var extensionList = new[] { new ExtensionFilter("glTF Binary", "glb"), - new ExtensionFilter("glTF Separate (unpacked mesh & textures)", "gltf"), }; - var filePath = StandaloneFileBrowser.SaveFilePanel("Save File", "", objectName, extensionList); - - if (filePath == String.Empty) + var filePath = StandaloneFileBrowser.SaveFilePanel("Export GLB", "", objectName, extensionList); + if (string.IsNullOrEmpty(filePath)) { return; } - // Define objects to export - var objectsToExport = new GameObject[] {obj}; + bool success = ExportToPath(filePath); + if (!success) + { + Debug.LogError("[Export] Failed to export " + objectName); + } + } - var settings = new ExportSettings() + public bool ExportToPath(string filePath) + { + try { - Format = GltfFormat.Json, + byte[] glbBytes; + if (isAreaExport) + { + glbBytes = ExportArea(); + } + else + { + glbBytes = ExportModel(); + } + + if (glbBytes == null || glbBytes.Length == 0) + { + Debug.LogError("[Export] FFI returned empty GLB data. " + RgpreBindings.GetLastErrorMessage()); + return false; + } + + File.WriteAllBytes(filePath, glbBytes); + Debug.Log("[Export] Exported " + filePath + " (" + (glbBytes.Length / 1024) + " KB)"); + return true; + } + catch (Exception ex) + { + Debug.LogError("[Export] " + ex.Message); + return false; + } + } + + public System.Threading.Tasks.Task ExportToPath(GameObject _, string filePath) + { + return System.Threading.Tasks.Task.FromResult(ExportToPath(filePath)); + } + + private byte[] ExportModel() + { + string artFolder = Game.pathManager.GetArtFolder(); + string assetsDir = Game.pathManager.GetRootFolder(); + string extension = fileType switch { + ModelFileType.file3D => ".3D", + ModelFileType.file3DC => ".3DC", + ModelFileType.fileROB => ".ROB", + _ => ".3D" }; - - if (filePath.EndsWith("gltf")) + string modelPath = Path.Combine(artFolder, modelName + extension); + if (!File.Exists(modelPath)) { - settings.Format = GltfFormat.Json; + Debug.LogError("[Export] Model file not found: " + modelPath); + return null; } - else if (filePath.EndsWith("glb")) + + IntPtr resultPtr = RgpreBindings.ConvertModelFromPath(modelPath, assetsDir); + if (resultPtr == IntPtr.Zero) { - settings.Format = GltfFormat.Binary; + Debug.LogError("[Export] FFI conversion failed: " + RgpreBindings.GetLastErrorMessage()); + return null; } - - var export = new GameObjectExport(settings); - export.AddScene(objectsToExport); - - // Async glTF export - var success = await export.SaveToFileAndDispose(filePath); - - if (success) + + return RgpreBindings.ExtractBytesAndFree(resultPtr); + } + + private byte[] ExportArea() + { + string mapsFolder = Game.pathManager.GetMapsFolder(); + string assetsDir = Game.pathManager.GetRootFolder(); + + if (!string.IsNullOrEmpty(wldName)) { - print("Exported " + filePath); + string wldPath = Path.Combine(mapsFolder, wldName + ".WLD"); + if (!File.Exists(wldPath)) + { + Debug.LogError("[Export] WLD file not found: " + wldPath); + return null; + } + + IntPtr wldResult = RgpreBindings.ConvertWldFromPath(wldPath, assetsDir); + if (wldResult == IntPtr.Zero) + { + Debug.LogError("[Export] FFI area conversion failed: " + RgpreBindings.GetLastErrorMessage()); + return null; + } + + return RgpreBindings.ExtractBytesAndFree(wldResult); } - else + + string rgmPath = Path.Combine(mapsFolder, rgmName + ".RGM"); + if (!File.Exists(rgmPath)) { - Debug.LogError("Something went wrong trying to export the model." + filePath); + Debug.LogError("[Export] RGM file not found: " + rgmPath); + return null; } + + IntPtr rgmResult = RgpreBindings.ConvertRgmFromPath(rgmPath, assetsDir); + if (rgmResult == IntPtr.Zero) + { + Debug.LogError("[Export] FFI area conversion failed: " + RgpreBindings.GetLastErrorMessage()); + return null; + } + + return RgpreBindings.ExtractBytesAndFree(rgmResult); } } diff --git a/Assets/Scripts/ModelViewer/ModelViewer.cs b/Assets/Scripts/ModelViewer/ModelViewer.cs index 83c23163..1cd8ee06 100644 --- a/Assets/Scripts/ModelViewer/ModelViewer.cs +++ b/Assets/Scripts/ModelViewer/ModelViewer.cs @@ -169,25 +169,32 @@ public void SpawnModel(string f3Dname, ModelFileType fileType, string colname) } PrepareLoad(); + FFIModelLoader.ClearCache(); loadedObjects = new List(); switch (fileType) { case ModelFileType.file3D: - loadedObjects.Add(ModelLoader.Load3D(f3Dname, colname)); + loadedObjects.Add(FFIModelLoader.Load3D(f3Dname, colname)); loadedFileName = "/Redguard/fxart/" + f3Dname + ".3D"; break; case ModelFileType.file3DC: - loadedObjects.Add(ModelLoader.Load3DC(f3Dname, colname)); + loadedObjects.Add(FFIModelLoader.Load3DC(f3Dname, colname)); loadedFileName = "/Redguard/fxart/" + f3Dname + ".3DC"; break; case ModelFileType.fileROB: - loadedObjects = ModelLoader.LoadROB(f3Dname, colname); + loadedObjects = FFIModelLoader.LoadROB(f3Dname, colname); loadedFileName = "/Redguard/fxart/" + f3Dname + ".ROB"; break; } minimalLoadedFileName = f3Dname; + glTFExporter.modelName = f3Dname; + glTFExporter.fileType = fileType; + glTFExporter.colName = colname; + glTFExporter.isAreaExport = false; + glTFExporter.rgmName = ""; + glTFExporter.wldName = ""; SpreadObjects(loadedObjects); @@ -204,12 +211,18 @@ public void SpawnArea(string RGM, string WLD, string COL, string prettyAreaName) } PrepareLoad(); + FFIModelLoader.ClearCache(); // Load Area objects - loadedObjects = ModelLoader.LoadArea(RGM, COL, WLD); + loadedObjects = FFIModelLoader.LoadArea(RGM, COL, WLD); minimalLoadedFileName = RGM; loadedWLD = WLD; loadedCOL = COL; + glTFExporter.rgmName = RGM; + glTFExporter.wldName = WLD; + glTFExporter.colName = COL; + glTFExporter.isAreaExport = true; + glTFExporter.modelName = ""; if (WLD.Equals(string.Empty)) { @@ -310,20 +323,21 @@ public void ResetIsolation() public void ApplyTextureFilterSetting() { - if (RGTexStore.MaterialDict == null) + if (loadedObjects == null || loadedObjects.Count == 0) { return; } - foreach (var mat in RGTexStore.MaterialDict) + foreach (var renderer in _objectRootGenerated.GetComponentsInChildren()) { - if (settings.useTextureFiltering) + foreach (Material mat in renderer.materials) { - mat.Value.mainTexture.filterMode = FilterMode.Bilinear; - } - else - { - mat.Value.mainTexture.filterMode = FilterMode.Point; + if (mat == null || mat.mainTexture == null) + { + continue; + } + + mat.mainTexture.filterMode = settings.useTextureFiltering ? FilterMode.Bilinear : FilterMode.Point; } } } @@ -399,8 +413,7 @@ public void ReloadWithPalette(string paletteName) string fileName = minimalLoadedFileName; // Clear caches so it rebuilds with the new palette - RGMeshStore.ClearCache(); - RGTexStore.ClearCache(); + FFIModelLoader.ClearCache(); // Force reload by clearing the name check string savedLoadedFileName = loadedFileName; diff --git a/Assets/Scripts/ModelViewer/ModelViewer_GUI.cs b/Assets/Scripts/ModelViewer/ModelViewer_GUI.cs index b79af81c..932cd322 100644 --- a/Assets/Scripts/ModelViewer/ModelViewer_GUI.cs +++ b/Assets/Scripts/ModelViewer/ModelViewer_GUI.cs @@ -44,7 +44,7 @@ public class ModelViewer_GUI : MonoBehaviour, IPointerEnterHandler, IPointerExit private List areaButtonList = new(); private List modelButtonList = new(); - private List areaList = new(); + private List areaList = new(); private List modelList = new(); private int selectedButtonIndex = -1; @@ -344,7 +344,7 @@ private void BuildButtonList_Areas() // generate the area list, if it is missing if (areaList.Count <= 0) { - areaList = RGINIStore.GetWorldList().Values.ToList(); + areaList = FFIWorldStore.GetWorldList().Values.ToList(); for(int i=0;i(); + position = 0; + } + + public int Position + { + get => position; + set => position = Math.Clamp(value, 0, data.Length); + } + + public void Seek(uint offset, int origin) + { + int basePos = origin switch + { + 1 => position, + 2 => data.Length, + _ => 0 + }; + + Position = basePos + unchecked((int)offset); + } + + public byte ReadByte() + { + EnsureAvailable(1); + return data[position++]; + } + + public byte[] ReadBytes(int count) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + EnsureAvailable(count); + byte[] output = new byte[count]; + Buffer.BlockCopy(data, position, output, 0, count); + position += count; + return output; + } + + public char[] ReadChars(int count) + { + byte[] bytes = ReadBytes(count); + char[] chars = new char[bytes.Length]; + for (int i = 0; i < bytes.Length; i++) + { + chars[i] = (char)bytes[i]; + } + + return chars; + } + + public short ReadInt16() + { + EnsureAvailable(2); + short value = BitConverter.ToInt16(data, position); + position += 2; + return value; + } + + public ushort ReadUInt16() + { + EnsureAvailable(2); + ushort value = BitConverter.ToUInt16(data, position); + position += 2; + return value; + } + + public int ReadInt32() + { + EnsureAvailable(4); + int value = BitConverter.ToInt32(data, position); + position += 4; + return value; + } + + public short PeekInt16() + { + EnsureAvailable(2); + return BitConverter.ToInt16(data, position); + } + + private void EnsureAvailable(int count) + { + if (position < 0 || position + count > data.Length) + { + throw new EndOfStreamException("Attempted to read past end of MemoryReader data."); + } + } + } +} diff --git a/Assets/Scripts/RGFileImport/MemoryReader.cs.meta b/Assets/Scripts/RGFileImport/MemoryReader.cs.meta new file mode 100644 index 00000000..027ffb93 --- /dev/null +++ b/Assets/Scripts/RGFileImport/MemoryReader.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4fb01b93883cf7e40bf6b01ef1e14b59 \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/Q4_28.cs b/Assets/Scripts/RGFileImport/Q4_28.cs deleted file mode 100644 index edee26cc..00000000 --- a/Assets/Scripts/RGFileImport/Q4_28.cs +++ /dev/null @@ -1,67 +0,0 @@ -// AI-generated, and i had no time to go through it yet -// might be broken somewhere -using System; -namespace RGFileImport -{ - public struct Q4_28 - { - private int value; - - public Q4_28(int value) - { - this.value = value; - } - - public Q4_28(float value) - { - this.value = (int)(value * 268435456); - } - - public static Q4_28 FromInt(int value) - { - return new Q4_28(value << 28); - } - - public static Q4_28 FromFloat(float value) - { - return new Q4_28(value); - } - - public float ToFloat() - { - return (float)value / 268435456.0f; - } - - public int ToInt() - { - return value >> 28; - } - - public static Q4_28 operator +(Q4_28 a, Q4_28 b) - { - return new Q4_28(a.value + b.value); - } - - public static Q4_28 operator -(Q4_28 a, Q4_28 b) - { - return new Q4_28(a.value - b.value); - } - - public static Q4_28 operator *(Q4_28 a, Q4_28 b) - { - // Multiply the two Q4.28 numbers, then shift right by 28 to maintain the Q4.28 format - return new Q4_28((int)(((long)a.value * b.value) >> 28)); - } - - public static Q4_28 operator /(Q4_28 a, Q4_28 b) - { - // Divide the two Q4.28 numbers, then shift left by 28 to maintain the Q4.28 format - return new Q4_28((int)(((long)a.value << 28) / b.value)); - } - - public override string ToString() - { - return ToFloat().ToString(); - } - } -} diff --git a/Assets/Scripts/RGFileImport/Q4_28.cs.meta b/Assets/Scripts/RGFileImport/Q4_28.cs.meta deleted file mode 100644 index 076047f8..00000000 --- a/Assets/Scripts/RGFileImport/Q4_28.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: c40983dcb14a91f38858ed60f9cc742c \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RG3DStore.cs b/Assets/Scripts/RGFileImport/RG3DStore.cs deleted file mode 100644 index 0a6eba2c..00000000 --- a/Assets/Scripts/RGFileImport/RG3DStore.cs +++ /dev/null @@ -1,423 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing.Printing; -using UnityEngine; -using UnityEngine.Rendering; -using Assets.Scripts.RGFileImport.RGGFXImport; - -public static class RG3DStore -{ - const float MESH_SCALE_FACTOR = 1/5120.0f; - static public Vector3 MESH_VERT_FLIP = new Vector3(1.0f, -1.0f, 1.0f); - static public Vector3 MESH_ROT_FLIP = new Vector3(-1.0f, 1.0f, -1.0f); - public struct Mesh3D_intermediate - { - public int subMeshCount; - public int framecount; - public List vertices; - public List uv; - public List normals; - public List[] frameDeltaVertices; - public List[] frameDeltaNormals; - - public Dictionary> submeshes; // dict key is: - // texture/imageid - // -1/colorid - } - - - public static Dictionary MeshIntermediateDict; // key: meshname - - static RG3DStore() - { - MeshIntermediateDict = new Dictionary(); - } - public static void DumpDict() - { - Debug.Log($"INTERS:"); - List keys = new List(MeshIntermediateDict.Keys); - for(int i=0;i(); - o.uv = new List(); - o.normals = new List(); - o.submeshes = new Dictionary>(); - - o.vertices.Add(new Vector3(0.0f,0.0f,0.0f)); - o.vertices.Add(new Vector3(1.0f,0.0f,0.0f)); - o.vertices.Add(new Vector3(1.0f,1.0f,0.0f)); - o.vertices.Add(new Vector3(0.0f,1.0f,0.0f)); - - o.uv.Add(new Vector2(1.0f,1.0f)); - o.uv.Add(new Vector2(0.0f,1.0f)); - o.uv.Add(new Vector2(0.0f,0.0f)); - o.uv.Add(new Vector2(1.0f,0.0f)); - - o.normals = new List(); - o.normals.Add(new Vector3(0.0f,0.0f,-1.0f)); - o.normals.Add(new Vector3(0.0f,0.0f,-1.0f)); - o.normals.Add(new Vector3(0.0f,0.0f,-1.0f)); - o.normals.Add(new Vector3(0.0f,0.0f,-1.0f)); - - List tris = new List(); - tris.Add(0); - tris.Add(1); - tris.Add(2); - - tris.Add(2); - tris.Add(3); - tris.Add(0); - - o.submeshes.Add(flatDesc, tris); - - MeshIntermediateDict.Add(flatDesc, o); - return MeshIntermediateDict[flatDesc]; - } - } - - // for now, assuming we only want to explicitly load 3dc files and that all 3d files are in the ROB files - public static Mesh3D_intermediate LoadMeshIntermediate3DC(string meshname) - { - Mesh3D_intermediate o; - if(MeshIntermediateDict.TryGetValue(meshname, out o)) - { - return o; - } - else - { - string filename = Game.pathManager.GetArtFolder() + meshname + ".3DC"; - - RGFileImport.RG3DFile file_3d = new RGFileImport.RG3DFile(); - file_3d.LoadFile(filename); - - MeshIntermediateDict.Add(meshname, LoadMesh_3D_intermediate(file_3d)); - return MeshIntermediateDict[meshname]; - } - } - - public static Mesh3D_intermediate LoadMeshIntermediate3D(string meshname) - { - Mesh3D_intermediate o; - if(MeshIntermediateDict.TryGetValue(meshname, out o)) - { - return o; - } - else - { - string filename = Game.pathManager.GetArtFolder() + meshname + ".3D"; - - RGFileImport.RG3DFile file_3d = new RGFileImport.RG3DFile(); - file_3d.LoadFile(filename); - - MeshIntermediateDict.Add(meshname, LoadMesh_3D_intermediate(file_3d)); - return MeshIntermediateDict[meshname]; - } - } - public static void LoadMeshIntermediatesROB(string ROBname) - { - string filename = Game.pathManager.GetArtFolder() + ROBname+ ".ROB"; - RGFileImport.RGROBFile file_rob = new RGFileImport.RGROBFile(); - file_rob.LoadFile(filename); - for(int i=0;i 0) // probably the 512 thing? - { - RGFileImport.RG3DFile file_3d = new RGFileImport.RG3DFile(); - file_3d.LoadMemory(file_rob.segments[i].Data); - MeshIntermediateDict.Add(file_rob.segments[i].SegmentID, LoadMesh_3D_intermediate(file_3d)); - } - else - { - try{ - LoadMeshIntermediate3DC(file_rob.segments[i].SegmentID); - } - catch(Exception ex) - { - Debug.Log($"Failed to load 3DC file from rob with error {ex.Message}"); - } - } - } - } - } - - -// intermediate mesh loading - public struct Face_3DC - { - public int vert_cnt; - public List verts; - public List uvs; - public Vector3 norm; - public List[] frameverts; - public List[] framenorms; - public List vertNorms; - public string texid; // key for intermediate mesh submesh dict - } - - private static Mesh3D_intermediate LoadMesh_3D_intermediate(RGFileImport.RG3DFile file_3d) - { - Mesh3D_intermediate mesh = new Mesh3D_intermediate(); -// 1st pass: load verts/normals/faces - List vec_tmp_lst = new List(); - List tri_tmp_lst = new List(); - List norm_tmp_lst = new List(); - List uv_tmp_lst = new List(); - - mesh.framecount = (int)file_3d.header.numFrames; - List[] frame_vec_tmp_lst = new List[mesh.framecount]; - - for(int i=0;i 0 - && file_3d.frameDataList.frameData[0].u2 == 4; - - for(int f=0;f(); - for(int i=0;i 0; - bool indirectNormals = hasVertexNormals - && file_3d.vertexNormalData.normals.Count != (int)file_3d.header.numVertices; - List vertNorm_tmp_lst = new List(); - if(hasVertexNormals) - { - for(int i=0;i face_lst = new List(); - int cumulativeFaceVertex = 0; - for(int i=0;i(); - cur_face.uvs = new List(); - cur_face.norm = Vector3.Scale(norm_tmp_lst[i], MESH_VERT_FLIP); - cur_face.frameverts = new List[mesh.framecount]; - cur_face.framenorms = new List[mesh.framecount]; - - if(file_3d.faceDataList.faceData[i].solidColor) - { - cur_face.texid = $"-1/{file_3d.faceDataList.faceData[i].colorIndex}"; - } - else - { - cur_face.texid = $"{file_3d.faceDataList.faceData[i].textureId}/{file_3d.faceDataList.faceData[i].imageId}"; - } - // regular verts - for(int j=0;j(); - for(int j=0;j= 0 && vnIdx < vertNorm_tmp_lst.Count) - { - Vector3 candidate = vertNorm_tmp_lst[vnIdx]; - if(!float.IsNaN(candidate.x) && !float.IsNaN(candidate.y) && !float.IsNaN(candidate.z)) - vn = Vector3.Scale(candidate, MESH_VERT_FLIP); - } - } - cur_face.vertNorms.Add(vn); - } - cumulativeFaceVertex += file_3d.faceDataList.faceData[i].vertexData.Count; - for(int f=0;f(); - cur_face.framenorms[f] = new List(); - for(int j=0;j 0 && i < file_3d.frameNormalData.faceNormals[f].Count) - { - var fn = file_3d.frameNormalData.faceNormals[f][i]; - Vector3 frameNorm = new Vector3(fn.x, fn.y, fn.z); - frameNorm.Normalize(); - frameNorm = Vector3.Scale(frameNorm, MESH_VERT_FLIP); - normDelta = frameNorm - cur_face.norm; - } - else - { - normDelta = Vector3.zero; - } - cur_face.framenorms[f].Add(normDelta); - } - } - - for(int j=0;j vec_lst = new List(); - List norm_lst = new List(); - List[] framevec_lst = new List[mesh.framecount]; - List[] framenorm_lst = new List[mesh.framecount]; - List uv_lst = new List(); - Dictionary> tri_lst = new Dictionary>(); - - for(int f=0;f(); - framenorm_lst[f] = new List(); - } - int tri_cnt = 0; - for(int i=0;i l; - if(!tri_lst.TryGetValue(face_lst[i].texid, out l)) - { - tri_lst.Add(face_lst[i].texid, new List()); - } - - tri_lst[face_lst[i].texid].Add(tri_cnt*3+2); - tri_lst[face_lst[i].texid].Add(tri_cnt*3+1); - tri_lst[face_lst[i].texid].Add(tri_cnt*3+0); - tri_cnt++; - } - } - - mesh.subMeshCount = tri_lst.Count; - mesh.vertices = vec_lst; - mesh.uv = uv_lst; - mesh.normals = norm_lst; - mesh.submeshes = tri_lst; - mesh.frameDeltaVertices = framevec_lst; - mesh.frameDeltaNormals = framenorm_lst; - - return mesh; - } - static Vector3 calculateTriNormal(Vector3 p1, Vector3 p2, Vector3 p3) - { - Vector3 o = new Vector3(); - Vector3 a = p2-p1; - Vector3 b = p3-p1; - - o.x = a.y*b.z - a.z*b.y; - o.y = a.z*b.x - a.z*b.z; - o.z = a.x*b.y - a.z*b.x; - return o; - } -} diff --git a/Assets/Scripts/RGFileImport/RG3DStore.cs.meta b/Assets/Scripts/RGFileImport/RG3DStore.cs.meta deleted file mode 100644 index 4dfce85e..00000000 --- a/Assets/Scripts/RGFileImport/RG3DStore.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 3e48c5bcda5b116f3a2bab0cb82ca519 \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/GraphicsConverter.cs b/Assets/Scripts/RGFileImport/RGGFXImport/GraphicsConverter.cs deleted file mode 100644 index 87751eb6..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/GraphicsConverter.cs +++ /dev/null @@ -1,179 +0,0 @@ -using UnityEngine; -using System.Collections.Generic; -using RGFileImport; - -namespace Assets.Scripts.RGFileImport.RGGFXImport -{ - public class GraphicsConverter - { - public static Texture2D RGPaletteColorToTexture2D(RGCOLFile palette, int colorid) - { - int width = 8; - int height = 8; - Texture2D texture = new Texture2D(width, height, TextureFormat.RGBA32, false); - byte[] pixels = new byte[width*height*4]; - - for (int i=0; i> RGTEXBSIToTexture2D(RGTEXBSIFile texbsi, RGCOLFile palette) - { - List> textures = new List>(); - for (int i=0; i RGBSIToTexture2D(RGBSIFile bsi, RGCOLFile palette) - { - int width = (int)bsi.BHDR.width; - int height = (int)bsi.BHDR.height; - List textures = new List(); - byte[] pixels = new byte[width*height*4]; - - for (int f=0; f RGGXAToTexture2D(RGGXAFile gxa) - { - List textures = new List(); - - for (int f=0; f RGFNTToTexture2D(RGFNTFile fnt) - { - // TODO: add character/font data in struct along with texture - List textures = new List(); - - for (int f=0; f Length) - throw new Exception($"MemoryReader: seek address 0x{Position:X} is greater than buffer length 0x{Length:X}"); - } - - public uint ReadUInt32() - { - - mark_usage(Position, 4); - if(Position+4 > Length) - throw new Exception($"MemoryReader: read address 0x{Position:X} + {4} is greater than buffer length 0x{Length:X}"); - uint o = BitConverter.ToUInt32(buffer, Position); - Position+=4; - return o; - } - public int ReadInt24() - { - mark_usage(Position, 3); - if(Position+3 > Length) - throw new Exception($"MemoryReader: read address 0x{Position:X} + {4} is greater than buffer length 0x{Length:X}"); - - List b = new List(); - b.Add((byte)0x00); - for(int i=0;i<3;i++) - { - b.Add((byte)buffer[Position]); - Position+=1; - } - int o = BitConverter.ToInt32(b.ToArray(), 0); - return o; - } - - public byte ReadByte() - { - mark_usage(Position, 1); - if(Position+1 > Length) - throw new Exception($"MemoryReader: read address 0x{Position:X} + {1} is greater than buffer length 0x{Length:X}"); - byte o = buffer[Position]; - Position+=1; - return o; - } - - public char ReadChar() - { - mark_usage(Position, 1); - if(Position+1 > Length) - throw new Exception($"MemoryReader: read address 0x{Position:X} + {1} is greater than buffer length 0x{Length:X}"); - char o = (char)buffer[Position]; - Position+=1; - return o; - } - - public char[] ReadChars(int cnt) - { - mark_usage(Position, cnt); - if(Position+cnt > Length) - throw new Exception($"MemoryReader: read address 0x{Position:X} + {cnt} (0x{cnt:X}) is greater than buffer length 0x{Length:X}"); - List o = new List(); - for(int i=0;i Length) - throw new Exception($"MemoryReader: read address 0x{Position:X} + {cnt} (0x{cnt:X}) is greater than buffer length 0x{Length:X}"); - List o = new List(); - for(int i=0;i Length) - throw new Exception($"MemoryReader: read address 0x{Position:X} + {cnt} (0x{cnt:X}) is greater than buffer length 0x{Length:X}"); - List o = new List(); - for(int i=0;i Length) - throw new Exception($"MemoryReader: read address 0x{Position:X} + {4}*{cnt} (0x{4*cnt}) is greater than buffer length 0x{Length:X}"); - List o = new List(); - for(int i=0;i Length) - throw new Exception($"MemoryReader: read address 0x{Position:X} + {2}*{cnt} (0x{2*cnt}) is greater than buffer length 0x{Length:X}"); - List o = new List(); - for(int i=0;i Length) - throw new Exception($"MemoryReader: read address 0x{Position:X} + {2} is greater than buffer length 0x{Length:X}"); - ushort o = BitConverter.ToUInt16(buffer, Position); - Position+=2; - return o; - } - public short ReadInt16() - { - mark_usage(Position, 2); - if(Position+2 > Length) - throw new Exception($"MemoryReader: read address 0x{Position:X} + {2} is greater than buffer length 0x{Length:X}"); - short o = BitConverter.ToInt16(buffer, Position); - Position+=2; - return o; - } - public int ReadInt32() - { - mark_usage(Position, 4); - if(Position+4 > Length) - throw new Exception($"MemoryReader: read address 0x{Position:X} + {4} is greater than buffer length 0x{Length:X}"); - int o = BitConverter.ToInt32(buffer, Position); - Position+=4; - return o; - } - public float ReadSingle() - { - mark_usage(Position, 4); - if(Position+4 > Length) - throw new Exception($"MemoryReader: read address 0x{Position:X} + {4} is greater than buffer length 0x{Length:X}"); - float o = BitConverter.ToSingle(buffer, Position); - Position+=4; - return o; - } - - public short PeekInt16() - { - short o = ReadInt16(); - Position-=2; - return o; - } - - public static uint ReverseBytes(uint n) - { - var bytes = BitConverter.GetBytes(n); - Array.Reverse(bytes, 0, bytes.Length); - return BitConverter.ToUInt32(bytes, 0); - } - - private void mark_usage(int start, int num) - { - if(use_debug_buffer) - { - for(int i=0;i frameData; - - public FrameDataList(MemoryReader memoryReader, RG3DHeader header) - { - try - { - frameData = new List(); - - memoryReader.Seek(header.offsetFrameData,0); - for(int i=0;i vertexData; - - // calculated values - public uint textureId; - public uint imageId; - public bool solidColor; - public byte colorIndex; - - public FaceDataItem(MemoryReader memoryReader, RG3DHeader header) - { - try - { - vertexCount = memoryReader.ReadByte(); - u1 = memoryReader.ReadByte(); - if(header.version > 27) - textureData = memoryReader.ReadUInt32(); - else - textureData = memoryReader.ReadUInt16(); - u2 = memoryReader.ReadUInt32(); - // vertexdata - vertexData = new List(); - short v_total = 0; - short u_total = 0; - for(int i=0;i 27) - { - if((textureData >> 20) == 0x0FFF) - { - textureId = 0; - imageId = 0; - colorIndex = (byte)(textureData >> 8); - solidColor = true; - } - else - { - uint tmp = (textureData >> 8) - 4000000; - uint t_one = (tmp/250)%40; - uint t_ten = ((tmp-(t_one*250))/1000)%100; - uint t_hundred = (tmp-(t_one*250)-(t_ten*1000))/4000; - - uint i_one = (textureData & 0xFF)%10; - uint i_ten = ((textureData & 0xFF)/40)*10; - - textureId = t_one+t_ten+t_hundred; - imageId = i_one+i_ten; - colorIndex = 0; - solidColor = false; - - } - } - else - { - textureId = (textureData >> 7); - if(textureId < 2) - { - imageId = 0; - colorIndex = (byte)(textureData); - solidColor = true; - } - else - { - imageId = (byte)(textureData & 0x7F); - colorIndex = 0; - solidColor = false; - } - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load 3D(C) FaceDataItem with error:\n{ex.Message}"); - } - } - } - public struct FaceDataList - { - public List faceData; - - public FaceDataList(MemoryReader memoryReader, RG3DHeader header) - { - try - { - faceData = new List(); - memoryReader.Seek(header.offsetFaceData,0); - for(int i=0;i coords; - public VertexData(MemoryReader memoryReader, RG3DHeader header) - { - try - { - coords = new List(); - memoryReader.Seek(header.offsetVertexCoords,0); - for(int i=0;i coords; - public NormalData(MemoryReader memoryReader, RG3DHeader header) - { - try - { - coords = new List(); - memoryReader.Seek(header.offsetFaceNormals,0); - for(int i=0;i> coords; - public FrameVertexData(MemoryReader memoryReader, RG3DHeader header, FrameDataList frameDataList) - { - try - { - coords = new List>(); - bool useInt32 = header.numFrames > 0 && frameDataList.frameData[0].u2 == 4; - - for(int i=0;i()); - - memoryReader.Seek(frameDataList.frameData[i].frameVertexOffset,0); - if(i == 0 || useInt32) - { - for(int j=0;j> faceNormals; // [frame][face] - - public static Coord3DFloat Decode101010_2(uint packed) - { - int nx = (int)(packed & 0x3FF); if(nx >= 512) nx -= 1024; - int ny = (int)((packed >> 10) & 0x3FF); if(ny >= 512) ny -= 1024; - int nz = (int)((packed >> 20) & 0x3FF); if(nz >= 512) nz -= 1024; - return new Coord3DFloat(nx / 256.0f, ny / 256.0f, nz / 256.0f); - } - - public FrameNormalData(MemoryReader memoryReader, RG3DHeader header, FrameDataList frameDataList) - { - try - { - faceNormals = new List>(); - - for(int i=0;i()); - - if(frameDataList.frameData[i].frameNormalOffset == 0) - continue; - - memoryReader.Seek(frameDataList.frameData[i].frameNormalOffset,0); - - if(i == 0) - continue; - for(int j=0;j normals; - - public VertexNormalData(MemoryReader memoryReader, RG3DHeader header) - { - try - { - normals = new List(); - - if(header.offsetUVData == 0) - return; - - if(header.offsetUVOffsets == 0) - { - memoryReader.Seek(header.offsetUVData,0); - for(int i=0;i offsets = new List(); - memoryReader.Seek(header.offsetUVOffsets,0); - for(int i=0;i faceRefs; - - public SubObjectEntry(MemoryReader memoryReader) - { - centerX = memoryReader.ReadInt32(); - centerY = memoryReader.ReadInt32(); - centerZ = memoryReader.ReadInt32(); - radius = memoryReader.ReadUInt32(); - ushort faceCount = memoryReader.ReadUInt16(); - float ex = memoryReader.ReadSingle(); - float ey = memoryReader.ReadSingle(); - float ez = memoryReader.ReadSingle(); - extents = new Coord3DFloat(ex, ey, ez); - faceRefs = new List(); - for(int i = 0; i < faceCount; i++) - faceRefs.Add(new SubObjectFaceRef(memoryReader)); - } - } - - public struct SubObjectData - { - public List entries; - - public SubObjectData(MemoryReader memoryReader, RG3DHeader header) - { - entries = new List(); - if(header.offsetSection4 == 0 || header.Section4Count == 0) - return; - try - { - memoryReader.Seek(header.offsetSection4, 0); - for(int i = 0; i < header.Section4Count; i++) - entries.Add(new SubObjectEntry(memoryReader)); - } - catch(Exception ex) - { - throw new Exception($"Failed to load 3D(C) SubObjectData with error:\n{ex.Message}"); - } - } - } - - public long fileSize; - public RG3DHeader header; - public FrameDataList frameDataList; - public FaceDataList faceDataList; - public VertexData vertexData; - public NormalData normalData; - public FrameVertexData frameVertexData; - public FrameNormalData frameNormalData; - public VertexNormalData vertexNormalData; - public SubObjectData subObjectData; - - public void LoadFile(string filename) - { - try - { - byte[] buffer; - BinaryReader binaryReader = new BinaryReader(File.OpenRead(filename)); - fileSize = binaryReader.BaseStream.Length; - buffer = binaryReader.ReadBytes((int)fileSize); - binaryReader.Close(); - LoadMemory(buffer); - } - catch(Exception ex) - { - throw new Exception($"Failed to load 3D(C) file {filename} with error:\n{ex.Message}"); - } - } - public void LoadMemory(byte[] buffer) - { - try - { - MemoryReader memoryReader = new MemoryReader(buffer); - header = new RG3DHeader(memoryReader); - frameDataList = new FrameDataList(memoryReader, header); - frameVertexData = new FrameVertexData(memoryReader, header, frameDataList); - frameNormalData = new FrameNormalData(memoryReader, header, frameDataList); - faceDataList = new FaceDataList(memoryReader, header); - vertexData = new VertexData(memoryReader, header); - normalData = new NormalData(memoryReader, header); - vertexNormalData = new VertexNormalData(memoryReader, header); - subObjectData = new SubObjectData(memoryReader, header); - } - catch(Exception ex) - { - throw new Exception($"Failed to load 3D(C) file from memory with error:\n{ex.Message}"); - } - } - - } -} diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RG3DFile.cs.meta b/Assets/Scripts/RGFileImport/RGGFXImport/RG3DFile.cs.meta deleted file mode 100644 index edb0f7e2..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RG3DFile.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e63e630564c078b49b24519715f80998 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGBSIFile.cs b/Assets/Scripts/RGFileImport/RGGFXImport/RGBSIFile.cs deleted file mode 100644 index a0fdd1b5..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGBSIFile.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; - -namespace RGFileImport -{ - public class RGBSIFile - { - public struct BSISectionHeader - { - public string sectionName; - public int sectionSize; - - public BSISectionHeader(MemoryReader memoryReader) - { - try - { - sectionName = new string(memoryReader.ReadChars(4)); - if(sectionName == "END ") // space at the end! - { - sectionSize = 0; - } - else - { - sectionSize = (int)MemoryReader.ReverseBytes(memoryReader.ReadUInt32()); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load BSISectionHeader with error:\n{ex.Message}"); - } - } - } - - public struct IFHDSegment - { - public byte[] data; - - public IFHDSegment(MemoryReader memoryReader) - { - try - { - data = memoryReader.ReadBytes(44); - } - catch(Exception ex) - { - throw new Exception($"Failed to load IFHDSegment with error:\n{ex.Message}"); - } - } - } - public struct BSIFSegment - { - // nothing here :) - public BSIFSegment(MemoryReader memoryReader) - { - } - } - public struct BHDRSegment - { - public short xOffset; - public short yOffset; - public short width; - public short height; - public byte unknown1; - public byte unknown2; - public short unknown3; - public short unknown4; - public short frameCount; - public short unknown5; - public short unknown6; - public short unknown7; - public byte unknown8; - public byte unknown9; - public short unknown10; - - public BHDRSegment(MemoryReader memoryReader) - { - try - { - xOffset = memoryReader.ReadInt16(); - yOffset = memoryReader.ReadInt16(); - width = memoryReader.ReadInt16(); - height = memoryReader.ReadInt16(); - unknown1 = memoryReader.ReadByte(); - unknown2 = memoryReader.ReadByte(); - unknown3 = memoryReader.ReadInt16(); - unknown4 = memoryReader.ReadInt16(); - frameCount = memoryReader.ReadInt16(); - unknown5 = memoryReader.ReadInt16(); - unknown6 = memoryReader.ReadInt16(); - unknown7 = memoryReader.ReadInt16(); - unknown8 = memoryReader.ReadByte(); - unknown9 = memoryReader.ReadByte(); - unknown10 = memoryReader.ReadInt16(); - } - catch(Exception ex) - { - throw new Exception($"Failed to load BHDRSegment with error:\n{ex.Message}"); - } - } - } - public struct CMAPSegment - { - public byte[] data; - - public CMAPSegment(MemoryReader memoryReader) - { - try - { - data = memoryReader.ReadBytes(768); - } - catch(Exception ex) - { - throw new Exception($"Failed to load CMAPSegment with error:\n{ex.Message}"); - } - } - } - public struct DATASegment - { - public int[] frameOffsets; - public List data; - - public DATASegment(BHDRSegment BHDR,MemoryReader memoryReader) - { - try - { - data = new List(); - if(BHDR.frameCount == 1) - { - frameOffsets = new int[1]; - data.Add(memoryReader.ReadBytes(BHDR.width*BHDR.height)); - } - else - { - int frameOffsetSize = BHDR.height*BHDR.frameCount; - frameOffsets = new int[frameOffsetSize]; - int baseOffset = memoryReader.Position; - for(int i=0;i data_f = new List(); - for(int y=0;y sections = new List(); - bool end = false; - while(!end) - { - sections.Add(new BSISectionHeader(memoryReader)); - if(sections[sections.Count-1].sectionName == "END ") - { - end = true; - } - else if(sections[sections.Count-1].sectionName == "IFHD") - { - IFHD = new IFHDSegment(memoryReader); - } - else if(sections[sections.Count-1].sectionName == "BSIF") - { - BSIF = new BSIFSegment(memoryReader); - } - else if(sections[sections.Count-1].sectionName == "BHDR") - { - BHDR = new BHDRSegment(memoryReader); - } - else if(sections[sections.Count-1].sectionName == "CMAP") - { - CMAP = new CMAPSegment(memoryReader); - } - else if(sections[sections.Count-1].sectionName == "DATA") - { - int ofs = memoryReader.Position; - DATA = new DATASegment(BHDR, memoryReader); - // not sure why we have to seek here - memoryReader.Seek((uint)ofs, (uint)sections[sections.Count-1].sectionSize); - } - else - { - throw new Exception($"Unknown BSI segment name: \"{sections[sections.Count-1].sectionName}\""); - } - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load BSI file from memory with error:\n{ex.Message}"); - } - } - } -} diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGBSIFile.cs.meta b/Assets/Scripts/RGFileImport/RGGFXImport/RGBSIFile.cs.meta deleted file mode 100644 index 2ed13e74..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGBSIFile.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 8cf4504cebd0f14a5bc9903065d9aed7 \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGCOLFile.cs b/Assets/Scripts/RGFileImport/RGGFXImport/RGCOLFile.cs deleted file mode 100644 index ad4a4fd9..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGCOLFile.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; - -namespace RGFileImport -{ - public class RGCOLFile - { - public struct RGColor - { - public byte r; - public byte g; - public byte b; - - public RGColor(byte[] input) - { - r = input[0]; - g = input[1]; - b = input[2]; - } - } - void ReadColors(MemoryReader memoryReader) - { - colors = new List(); - try - { - for(int i=0;i<256;i++) - { - colors.Add(new RGColor(memoryReader.ReadBytes(3))); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load Colors with error:\n{ex.Message}"); - } - } - // data - public long fileSize; - public List colors; - private bool fromFile = false; // if we load COL from memory, we skip 2x4bytes - - public void LoadFile(string filename) - { - try - { - fromFile = true; - byte[] buffer; - BinaryReader binaryReader = new BinaryReader(File.OpenRead(filename)); - fileSize = binaryReader.BaseStream.Length; - buffer = binaryReader.ReadBytes((int)fileSize); - binaryReader.Close(); - LoadMemory(buffer); - } - catch(Exception ex) - { - throw new Exception($"Failed to load COL file {filename} with error:\n{ex.Message}"); - } - } - public void LoadMemory(byte[] buffer) - { - try - { - MemoryReader memoryReader = new MemoryReader(buffer); - if(fromFile) - { - memoryReader.ReadUInt32(); // filesize - memoryReader.ReadUInt32(); // unknown - } - ReadColors(memoryReader); - } - catch(Exception ex) - { - throw new Exception($"Failed to load COL file from memory with error:\n{ex.Message}"); - } - } - } -} diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGCOLFile.cs.meta b/Assets/Scripts/RGFileImport/RGGFXImport/RGCOLFile.cs.meta deleted file mode 100644 index 44ccea41..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGCOLFile.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 255e70d8cc8ced4028f48a643e0b7631 \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGFNTFile.cs b/Assets/Scripts/RGFileImport/RGGFXImport/RGFNTFile.cs deleted file mode 100644 index 28125cc0..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGFNTFile.cs +++ /dev/null @@ -1,261 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; - -namespace RGFileImport -{ - public class RGFNTFile - { - public struct FNTSectionHeader - { - public string sectionName; - public uint dataLength; - - public FNTSectionHeader(MemoryReader memoryReader) - { - try - { - char[] sectionName_char; - sectionName_char = memoryReader.ReadChars(4); - string[] name_strs = new string(sectionName_char).Split('\0'); - sectionName = name_strs[0]; - - if(sectionName == "END ") // yeah theres a space there - { - dataLength = 0; - } - else - { - dataLength = MemoryReader.ReverseBytes(memoryReader.ReadUInt32()); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load FNT section header with error:\n{ex.Message}"); - } - } - public override string ToString() - { - return $@"################################### -FNTHeader -################################### -name: {sectionName} -size: {dataLength:X} -###################################"; - } - } - public struct FNHDSection - { - - public string description; - public short unknown1; - public short hasRDAT; - public short unknown2; - public short unknown3; - public short unknown4; - public short maxWidth; - public short lineHeight; - public short firstCharacter; - public short numCharacters; - public short unknown5; - public short unknown6; - public short unknown7; - public FNHDSection(MemoryReader memoryReader, uint size) - { - try - { - char[] tchar = memoryReader.ReadChars(32); - description = new string(tchar); - unknown1 = memoryReader.ReadInt16(); - hasRDAT = memoryReader.ReadInt16(); - unknown2 = memoryReader.ReadInt16(); - unknown3 = memoryReader.ReadInt16(); - unknown4 = memoryReader.ReadInt16(); - maxWidth = memoryReader.ReadInt16(); - lineHeight = memoryReader.ReadInt16(); - firstCharacter = memoryReader.ReadInt16(); - numCharacters = memoryReader.ReadInt16(); - unknown5 = memoryReader.ReadInt16(); - unknown6 = memoryReader.ReadInt16(); - unknown7 = memoryReader.ReadInt16(); - } - catch(Exception ex) - { - throw new Exception($"Failed to load FNT FNHD section with error:\n{ex.Message}"); - } - } - } - public struct CharacterData - { - - public short enabled; - public short offsetLeft; - public short offsetTop; - public short width; - public short height; - public byte[] data; - public CharacterData(MemoryReader memoryReader) - { - try - { - enabled = memoryReader.ReadInt16(); - offsetLeft = memoryReader.ReadInt16(); - offsetTop = memoryReader.ReadInt16(); - width = memoryReader.ReadInt16(); - height = memoryReader.ReadInt16(); - data = memoryReader.ReadBytes(width*height); - - } - catch(Exception ex) - { - throw new Exception($"Failed to load FNT character data with error:\n{ex.Message}"); - } - } - } - - public struct FBMPSection - { - - public List characters; - public FBMPSection(MemoryReader memoryReader, FNHDSection FNHD) - { - try - { - characters = new List(); - for(int i=0;i colors; - - public BPALSection(MemoryReader memoryReader, uint size) - { - try - { - colors = new List(); - for(int i=0;i<256;i++) - { - colors.Add(new BPALColor(memoryReader.ReadBytes(3))); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load FNT BPAL section with error:\n{ex.Message}"); - } - } - } - public struct RDATSection - { - // quick and dirty since we dont use it, real format: - // char[136] description - // int[10?] unknown - public byte[] unknown; - - public RDATSection(MemoryReader memoryReader, uint size) - { - try - { - unknown = memoryReader.ReadBytes((int)size); - } - catch(Exception ex) - { - throw new Exception($"Failed to load FNT RDAT section with error:\n{ex.Message}"); - } - } - } - - // data - public long fileSize; - public List Sections; - public BPALSection BPAL; - public FNHDSection FNHD; - public FBMPSection FBMP; - public RDATSection RDAT; - - public void LoadFile(string filename) - { - try - { - byte[] buffer; - BinaryReader binaryReader = new BinaryReader(File.OpenRead(filename)); - fileSize = binaryReader.BaseStream.Length; - buffer = binaryReader.ReadBytes((int)fileSize); - binaryReader.Close(); - LoadMemory(buffer); - - } - catch(Exception ex) - { - throw new Exception($"Failed to load FNT file {filename} with error:\n{ex.Message}"); - } - } - - public void LoadMemory(byte[] buffer) - { - try - { - MemoryReader memoryReader = new MemoryReader(buffer); - Sections = new List(); - bool end = false; - while(!end) - { - Sections.Add(new FNTSectionHeader(memoryReader)); - if(Sections[Sections.Count-1].sectionName == "END ") - { - end = true; - } - else if(Sections[Sections.Count-1].sectionName == "FNHD") - { - FNHD = new FNHDSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - // BPAL/FPAL are identical - else if(Sections[Sections.Count-1].sectionName == "BPAL" || - Sections[Sections.Count-1].sectionName == "FPAL") - { - BPAL = new BPALSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "FBMP") - { - FBMP = new FBMPSection(memoryReader, FNHD); - } - else if(Sections[Sections.Count-1].sectionName == "RDAT") - { - RDAT = new RDATSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else - { - Console.WriteLine($"SEC: {Sections[Sections.Count-1].sectionName}"); - memoryReader.Seek(Sections[Sections.Count-1].dataLength, (uint)memoryReader.Position); - } - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load FNT file from memory with error:\n{ex.Message}"); - } - } - } -} diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGFNTFile.cs.meta b/Assets/Scripts/RGFileImport/RGGFXImport/RGFNTFile.cs.meta deleted file mode 100644 index 329a8f80..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGFNTFile.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: d2a3f6513c8448a99b2a6527e047921f \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGGXAFile.cs b/Assets/Scripts/RGFileImport/RGGFXImport/RGGXAFile.cs deleted file mode 100644 index 087720d7..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGGXAFile.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; - -namespace RGFileImport -{ - public class RGGXAFile - { - public struct GXASectionHeader - { - public string sectionName; - public uint dataLength; - - public GXASectionHeader(MemoryReader memoryReader) - { - try - { - char[] sectionName_char; - sectionName_char = memoryReader.ReadChars(4); - string[] name_strs = new string(sectionName_char).Split('\0'); - sectionName = name_strs[0]; - - if(sectionName == "END ") // yeah theres a space there - { - dataLength = 0; - } - else - { - dataLength = MemoryReader.ReverseBytes(memoryReader.ReadUInt32()); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load GXA section header with error:\n{ex.Message}"); - } - } - public override string ToString() - { - return $@"################################### -GXAHeader -################################### -name: {sectionName} -size: {dataLength:X} -###################################"; - } - } - - public struct BMHDSection - { - - public string title; - public byte[] unknown1; - public short numImages; - public BMHDSection(MemoryReader memoryReader, uint size) - { - try - { - title = new string(memoryReader.ReadChars(22)); - unknown1 = memoryReader.ReadBytes(10); - numImages = memoryReader.ReadInt16(); - } - catch(Exception ex) - { - throw new Exception($"Failed to load GXA BMHD section with error:\n{ex.Message}"); - } - } - } - public struct BPALSection - { - public struct BPALColor - { - public byte r; - public byte g; - public byte b; - public BPALColor(byte[] input) - { - r = input[0]; - g = input[1]; - b = input[2]; - } - } - - public List colors; - - public BPALSection(MemoryReader memoryReader, uint size) - { - try - { - colors = new List(); - for(int i=0;i<256;i++) - { - colors.Add(new BPALColor(memoryReader.ReadBytes(3))); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load GXA BPAL section with error:\n{ex.Message}"); - } - } - } - public struct BBMPItem - { - public short unknown; - // width and height are flipped down the line in GraphicsConverter - public short width; - public short height; - public short[] unknown2; - public byte[] data; - - public BBMPItem(MemoryReader memoryReader) - { - try - { - unknown = memoryReader.ReadInt16(); - width = memoryReader.ReadInt16(); - height = memoryReader.ReadInt16(); - unknown2 = memoryReader.ReadInt16s(6); - data = memoryReader.ReadBytes(width*height); - } - catch(Exception ex) - { - throw new Exception($"Failed to load GXA BBMP item with error:\n{ex.Message}"); - } - } - - } - - public struct BBMPSection - { - - public List BBMPItems; - - public BBMPSection(MemoryReader memoryReader, BMHDSection BMHD) - { - try - { - BBMPItems = new List(); - for(int i=0;i Sections; - public BMHDSection BMHD; - public BPALSection BPAL; - public BBMPSection BBMP; - - public void LoadFile(string filename) - { - try - { - byte[] buffer; - BinaryReader binaryReader = new BinaryReader(File.OpenRead(filename)); - fileSize = binaryReader.BaseStream.Length; - buffer = binaryReader.ReadBytes((int)fileSize); - binaryReader.Close(); - LoadMemory(buffer); - - } - catch(Exception ex) - { - throw new Exception($"Failed to load GXA file {filename} with error:\n{ex.Message}"); - } - } - - public void LoadMemory(byte[] buffer) - { - try - { - MemoryReader memoryReader = new MemoryReader(buffer); - Sections = new List(); - bool end = false; - while(!end) - { - Sections.Add(new GXASectionHeader(memoryReader)); - if(Sections[Sections.Count-1].sectionName == "END ") - { - end = true; - } - else if(Sections[Sections.Count-1].sectionName == "BMHD") - { - BMHD = new BMHDSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "BPAL") - { - BPAL = new BPALSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "BBMP") - { - BBMP = new BBMPSection(memoryReader, BMHD); - } - else - { - memoryReader.Seek(Sections[Sections.Count-1].dataLength, (uint)memoryReader.Position); - } - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load GXA file from memory with error:\n{ex.Message}"); - } - } - } -} diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGGXAFile.cs.meta b/Assets/Scripts/RGFileImport/RGGFXImport/RGGXAFile.cs.meta deleted file mode 100644 index 8a8fc012..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGGXAFile.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 336285096c27852319927202e6931bc4 \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGINIFile.cs b/Assets/Scripts/RGFileImport/RGGFXImport/RGINIFile.cs deleted file mode 100644 index 5f0520ac..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGINIFile.cs +++ /dev/null @@ -1,748 +0,0 @@ -using System; -using System.IO; -using System.Collections.Generic; -using UnityEngine; - -namespace RGFileImport -{ - public class RGINIFile - { - public enum INISection - { - inisection_empty = 0, - inisection_world, - inisection_menu_general, - inisection_menu_page0, - inisection_menu_page1, - inisection_menu_page2, - inisection_menu_page3, - inisection_menu_page4, - inisection_menu_page5, - inisection_menu_page6, - inisection_menu_page7, - inisection_items, - } - // ITEMS.INI - public struct INIItemData - { - public string bitmap_file; - public string bitmap_selected_file; - public List start_item_list; - public int start_item_select; - public int additional_length; - public int weapon_sphere_size; - public int default_weapon_item; - public int torch_time; - public Dictionary items; - } - public struct INIItem - { - public int type; - public int flags; - public int hide; - public int total; - public int player_max; - public int player_total; - public string name; - public string description; - - public string use_script; - public int script_instances; - - public int bitmap; - public string inventory_object_file; - public string game_object_file; - - public string hand_object_file; - public string hilt_object_file; - } - public void ParseItemLine(string line) - { - if(line.Length == 0) - return; - string[] values; - string[] parts = line.Split('='); - string member = parts[0].Trim(); - string val = parts[1].Trim(); - - if(member.Contains("[") && member.Contains("]")) - { - // item-specific data - int itemIndex = int.Parse(member.Substring(member.IndexOf('[')+1, - member.IndexOf(']')-member.IndexOf('[')-1)); - string memberName = member.Substring(0, member.IndexOf('[')); - INIItem tmp = new INIItem(); - if(!itemData.items.TryGetValue(itemIndex, out tmp)) - { - itemData.items.Add(itemIndex, tmp); - } - switch(memberName) - { - case "type": - tmp.type = int.Parse(val); - break; - case "flags": - tmp.flags = int.Parse(val); - break; - case "hide": - tmp.hide = int.Parse(val); - break; - case "total": - tmp.total = int.Parse(val); - break; - case "player_max": - tmp.player_max = int.Parse(val); - break; - case "player_total": - tmp.player_total = int.Parse(val); - break; - case "name": - tmp.name= val; - break; - case "description": - tmp.description = val; - break; - case "use_script": - tmp.use_script = val; - break; - case "script_instances": - tmp.script_instances = int.Parse(val); - break; - case "bitmap": - tmp.bitmap = int.Parse(val); - break; - case "inventory_object_file": - tmp.inventory_object_file = val; - break; - case "game_object_file": - tmp.game_object_file = val; - break; - case "hand_object_file": - tmp.hand_object_file = val; - break; - case "hilt_object_file": - tmp.hilt_object_file = val; - break; - default: - Console.WriteLine($"UNKNOWN: {memberName}"); - break; - } - itemData.items[itemIndex] = tmp; - } - else - { - // items metadata - switch(member) - { - case "bitmap_file": - itemData.bitmap_file = val; - break; - case "bitmap_selected_file": - itemData.bitmap_selected_file = val; - break; - case "start_item_list": - values = val.Split(","); - foreach( string v in values) - { - Console.WriteLine($"V: {v}"); - itemData.start_item_list.Add(int.Parse(v)); - } - break; - case "start_item_select": - itemData.start_item_select = int.Parse(val); - break; - case "additional_length": - itemData.additional_length = int.Parse(val); - break; - case "weapon_sphere_size": - itemData.weapon_sphere_size = int.Parse(val); - break; - case "default_weapon_item": - itemData.default_weapon_item = int.Parse(val); - break; - case "torch_time": - itemData.torch_time = int.Parse(val); - break; - - default: - break; - } - } - Console.WriteLine(line); - } - // WORLD.INI - public struct INIWorldData - { - public int start_world; - public int start_marker; - public int test_map_delay; - public List test_map_order; - public Dictionary worlds; - } - public struct INIWorld - { - public string map; - public string world; - public int redbook; - public string palette; - public int shade; - public int haze; - public string sky; - public Vector4 sun; - public int ambient; - public int background; - public int sky_xrotate; - public int sky_yrotate; - public int rain_delay; - public int rain_drops; - public int rain_start; - public int rain_end; - public Vector4 rain_sphere1; - public Vector4 rain_sphere2; - public Vector4 rain_sphere3; - public Vector4 rain_sphere4; - public int compass; - public string flash_filename; - public int ambientfx; - public Vector3 ambientrgb; - public int sunangle; - public int sunskew; - public string sunimg; - public Vector3 sunimgrgb; - public int sunscale; - public Vector4 sunrgb; - public Vector3 fogrgb; - public string skyfx; - public int skyscale; - public int skylevel; - public int skyspeed; - public Vector3 wave; - - } - - public void ParseWorldLine(string line) - { - if(line.Length == 0) - return; - string[] values; - string[] parts = line.Split('='); - string member = parts[0].Trim(); - string val = parts[1].Trim(); - - if(member.Contains("[") && member.Contains("]")) - { - // world-specific data - int worldIndex = int.Parse(member.Substring(member.IndexOf('[')+1, - member.IndexOf(']')-member.IndexOf('[')-1)); - string memberName = member.Substring(0, member.IndexOf('[')); - INIWorld tmp = new INIWorld(); - if(!worldData.worlds.TryGetValue(worldIndex, out tmp)) - { - worldData.worlds.Add(worldIndex, tmp); - } - switch(memberName) - { - case "world_map": - tmp.map = val; - break; - case "world_world": - tmp.world = val; - break; - case "world_redbook": - tmp.redbook = int.Parse(val); - break; - case "world_palette": - tmp.palette = val; - break; - case "world_shade": - tmp.shade = int.Parse(val); - break; - case "world_haze": - tmp.haze = int.Parse(val); - break; - case "world_sky": - tmp.sky = val; - break; - case "world_sun": - values = val.Split(","); - tmp.sun.x = int.Parse(values[0]); - tmp.sun.y = int.Parse(values[1]); - tmp.sun.z = int.Parse(values[2]); - tmp.sun.w = int.Parse(values[3]); - break; - case "world_ambient": - tmp.ambient = int.Parse(val); - break; - case "world_background": - tmp.background = int.Parse(val); - break; - case "world_sky_xrotate": - tmp.sky_xrotate = int.Parse(val); - break; - case "world_sky_yrotate": - tmp.sky_yrotate = int.Parse(val); - break; - case "world_rain_delay": - tmp.rain_delay = int.Parse(val); - break; - case "world_rain_drops": - tmp.rain_drops = int.Parse(val); - break; - case "world_rain_start": - tmp.rain_start = int.Parse(val); - break; - case "world_rain_end": - tmp.rain_end = int.Parse(val); - break; - case "world_rain_sphere1": - values = val.Split(","); - tmp.rain_sphere1.x = int.Parse(values[0]); - tmp.rain_sphere1.y = int.Parse(values[1]); - tmp.rain_sphere1.z = int.Parse(values[2]); - tmp.rain_sphere1.w = int.Parse(values[3]); - break; - case "world_rain_sphere2": - values = val.Split(","); - tmp.rain_sphere2.x = int.Parse(values[0]); - tmp.rain_sphere2.y = int.Parse(values[1]); - tmp.rain_sphere2.z = int.Parse(values[2]); - tmp.rain_sphere2.w = int.Parse(values[3]); - break; - case "world_rain_sphere3": - values = val.Split(","); - tmp.rain_sphere3.x = int.Parse(values[0]); - tmp.rain_sphere3.y = int.Parse(values[1]); - tmp.rain_sphere3.z = int.Parse(values[2]); - tmp.rain_sphere3.w = int.Parse(values[3]); - break; - case "world_rain_sphere4": - values = val.Split(","); - tmp.rain_sphere4.x = int.Parse(values[0]); - tmp.rain_sphere4.y = int.Parse(values[1]); - tmp.rain_sphere4.z = int.Parse(values[2]); - tmp.rain_sphere4.w = int.Parse(values[3]); - break; - case "world_compass": - tmp.compass = int.Parse(val); - break; - case "world_flash_filename": - tmp.flash_filename = val; - break; - case "world_ambientfx": - tmp.ambientfx = int.Parse(val); - break; - case "world_ambientrgb": - values = val.Split(","); - tmp.ambientrgb.x = int.Parse(values[0]); - tmp.ambientrgb.y = int.Parse(values[1]); - tmp.ambientrgb.z = int.Parse(values[2]); - break; - case "world_sunangle": - tmp.sunangle = int.Parse(val); - break; - case "world_sunskew": - tmp.sunskew = int.Parse(val); - break; - case "world_sunimg": - tmp.sunimg = val; - break; - case "world_sunimgrgb": - values = val.Split(","); - tmp.sunimgrgb.x = int.Parse(values[0]); - tmp.sunimgrgb.y = int.Parse(values[1]); - tmp.sunimgrgb.z = int.Parse(values[2]); - break; - case "world_sunscale": - tmp.sunscale = int.Parse(val); - break; - case "world_sunrgb": - values = val.Split(","); - tmp.sunrgb.x = int.Parse(values[0]); - tmp.sunrgb.y = int.Parse(values[1]); - tmp.sunrgb.z = int.Parse(values[2]); - tmp.sunrgb.w = int.Parse(values[3]); - break; - case "world_fogrgb": - values = val.Split(","); - tmp.fogrgb.x = int.Parse(values[0]); - tmp.fogrgb.y = int.Parse(values[1]); - tmp.fogrgb.z = int.Parse(values[2]); - break; - case "world_skyfx": - tmp.skyfx = val; - break; - case "world_skyscale": - tmp.skyscale = int.Parse(val); - break; - case "world_skylevel": - tmp.skylevel = int.Parse(val); - break; - case "world_skyspeed": - tmp.skyspeed = int.Parse(val); - break; - case "world_wave": - values = val.Split(","); - tmp.wave.x = int.Parse(values[0]); - tmp.wave.y = int.Parse(values[1]); - tmp.wave.z = int.Parse(values[2]); - break; - default: - Console.WriteLine($"UNKNOWN: {memberName}"); - break; - } - worldData.worlds[worldIndex] = tmp; - } - else - { - // worlds metadata - switch(member) - { - case "start_world": - worldData.start_world = int.Parse(val); - break; - case "start_marker": - worldData.start_marker = int.Parse(val); - break; - case "test_map_delay": - worldData.test_map_delay = int.Parse(val); - break; - case "test_map_order": - values = val.Split(","); - foreach( string v in values) - { - Console.WriteLine($"V: {v}"); - worldData.test_map_order.Add(int.Parse(v)); - } - break; - default: - break; - } - } - Console.WriteLine(line); - } - // MENU.INI - public struct INIMenuData - { - public int preload_sprites; - public int hi_res_menu; - public int ignore_autosave; - public int force_movies; - public Dictionary pages; - } - public struct INIMenuPage - { - public Dictionary texture_set; - public Dictionary texture_index; - public Dictionary menuItems; - - } - public class INIMenuItem - { - public int text_x; - public int text_y; - public string text; - public int justify; - public int selectable; - public int default_selected; - public int texture; - public int action; - public int grayed; - public int output_x; - public int output_y; - public int slider_min; - public int slider_max; - } - - - public void ParseMenuLine(string line, int page) - { - if(line.Length == 0) - return; - - INIMenuPage tmp; - if(!menuData.pages.TryGetValue(page, out tmp)) - { - - tmp = new INIMenuPage(); - tmp.texture_set = new Dictionary(); - tmp.texture_index = new Dictionary(); - tmp.menuItems = new Dictionary(); - menuData.pages.Add(page, tmp); - } - - string[] values; - string[] parts = line.Split('='); - string member = parts[0].Trim(); - string val = parts[1].Trim(); - if(member.Contains("[") && member.Contains("]")) - { - int itemIndex = int.Parse(member.Substring(member.IndexOf('[')+1, - member.IndexOf(']')-member.IndexOf('[')-1)); - string memberName = member.Substring(0, member.IndexOf('[')); - int tmpi; - INIMenuItem tmpItem; - switch(memberName) - { - case "texture_set": - if(!menuData.pages[page].texture_set.TryGetValue(itemIndex, out tmpi)) - { - menuData.pages[page].texture_set.Add(itemIndex, 0); - } - menuData.pages[page].texture_set[itemIndex] = int.Parse(val); - break; - case "texture_index": - if(!menuData.pages[page].texture_index.TryGetValue(itemIndex, out tmpi)) - { - menuData.pages[page].texture_index.Add(itemIndex, 0); - } - menuData.pages[page].texture_index[itemIndex] = int.Parse(val); - break; - case "text_x": - if(!menuData.pages[page].menuItems.TryGetValue(itemIndex, out tmpItem)) - { - menuData.pages[page].menuItems.Add(itemIndex, new INIMenuItem()); - } - menuData.pages[page].menuItems[itemIndex].text_x = int.Parse(val); - break; - case "text_y": - if(!menuData.pages[page].menuItems.TryGetValue(itemIndex, out tmpItem)) - { - menuData.pages[page].menuItems.Add(itemIndex, new INIMenuItem()); - } - menuData.pages[page].menuItems[itemIndex].text_y = int.Parse(val); - break; - case "text": - if(!menuData.pages[page].menuItems.TryGetValue(itemIndex, out tmpItem)) - { - menuData.pages[page].menuItems.Add(itemIndex, new INIMenuItem()); - } - menuData.pages[page].menuItems[itemIndex].text = val; - break; - case "justify": - if(!menuData.pages[page].menuItems.TryGetValue(itemIndex, out tmpItem)) - { - menuData.pages[page].menuItems.Add(itemIndex, new INIMenuItem()); - } - menuData.pages[page].menuItems[itemIndex].justify = int.Parse(val); - break; - case "selectable": - if(!menuData.pages[page].menuItems.TryGetValue(itemIndex, out tmpItem)) - { - menuData.pages[page].menuItems.Add(itemIndex, new INIMenuItem()); - } - menuData.pages[page].menuItems[itemIndex].selectable = int.Parse(val); - break; - case "default_selected": - if(!menuData.pages[page].menuItems.TryGetValue(itemIndex, out tmpItem)) - { - menuData.pages[page].menuItems.Add(itemIndex, new INIMenuItem()); - } - menuData.pages[page].menuItems[itemIndex].default_selected = int.Parse(val); - break; - case "texture": - if(!menuData.pages[page].menuItems.TryGetValue(itemIndex, out tmpItem)) - { - menuData.pages[page].menuItems.Add(itemIndex, new INIMenuItem()); - } - menuData.pages[page].menuItems[itemIndex].texture = int.Parse(val); - break; - case "action": - if(!menuData.pages[page].menuItems.TryGetValue(itemIndex, out tmpItem)) - { - menuData.pages[page].menuItems.Add(itemIndex, new INIMenuItem()); - } - menuData.pages[page].menuItems[itemIndex].action = int.Parse(val); - break; - case "grayed": - if(!menuData.pages[page].menuItems.TryGetValue(itemIndex, out tmpItem)) - { - menuData.pages[page].menuItems.Add(itemIndex, new INIMenuItem()); - } - menuData.pages[page].menuItems[itemIndex].grayed = int.Parse(val); - break; - case "output_x": - if(!menuData.pages[page].menuItems.TryGetValue(itemIndex, out tmpItem)) - { - menuData.pages[page].menuItems.Add(itemIndex, new INIMenuItem()); - } - menuData.pages[page].menuItems[itemIndex].output_x = int.Parse(val); - break; - case "output_y": - if(!menuData.pages[page].menuItems.TryGetValue(itemIndex, out tmpItem)) - { - menuData.pages[page].menuItems.Add(itemIndex, new INIMenuItem()); - } - menuData.pages[page].menuItems[itemIndex].output_y = int.Parse(val); - break; - case "slider_min": - if(!menuData.pages[page].menuItems.TryGetValue(itemIndex, out tmpItem)) - { - menuData.pages[page].menuItems.Add(itemIndex, new INIMenuItem()); - } - menuData.pages[page].menuItems[itemIndex].slider_min = int.Parse(val); - break; - case "slider_max": - if(!menuData.pages[page].menuItems.TryGetValue(itemIndex, out tmpItem)) - { - menuData.pages[page].menuItems.Add(itemIndex, new INIMenuItem()); - } - menuData.pages[page].menuItems[itemIndex].slider_max = int.Parse(val); - break; - default: - Console.WriteLine($"UNKNOWN: {memberName}"); - break; - } - menuData.pages[page] = tmp; - } - else - { - // menu metadata - switch(member) - { - case "preload_sprites": - menuData.preload_sprites = int.Parse(val); - break; - case "hi_res_menu": - menuData.hi_res_menu = int.Parse(val); - break; - case "ignore_autosave": - menuData.ignore_autosave = int.Parse(val); - break; - case "force_movies": - menuData.force_movies = int.Parse(val); - break; - default: - break; - } - } - Console.WriteLine(line); - } - - // general stuff - public void ParseLine(INISection section, string line) - { - switch(section) - { - - case INISection.inisection_items: - ParseItemLine(line); - break; - case INISection.inisection_world: - ParseWorldLine(line); - break; - case INISection.inisection_menu_general: - ParseMenuLine(line, 0); - break; - case INISection.inisection_menu_page0: - ParseMenuLine(line, 0); - break; - case INISection.inisection_menu_page1: - ParseMenuLine(line, 1); - break; - case INISection.inisection_menu_page2: - ParseMenuLine(line, 2); - break; - case INISection.inisection_menu_page3: - ParseMenuLine(line, 3); - break; - case INISection.inisection_menu_page4: - ParseMenuLine(line, 4); - break; - case INISection.inisection_menu_page5: - ParseMenuLine(line, 5); - break; - case INISection.inisection_menu_page6: - ParseMenuLine(line, 6); - break; - case INISection.inisection_menu_page7: - ParseMenuLine(line, 7); - break; - default: - Console.WriteLine("KAPUT"); - break; - } - } - - public INIItemData itemData; - public INIWorldData worldData; - public INIMenuData menuData; - - public void LoadFile(string filename) - { - try - { - StreamReader streamReader = new StreamReader(filename); - string line = streamReader.ReadLine(); - INISection currentSection = INISection.inisection_empty; - while(line != null) - { - - string[] parts = line.Split(';'); - string line_nocomment = parts[0]; - switch(line_nocomment) - { - // ITEM.INI - case "[items]": - currentSection = INISection.inisection_items; - itemData = new INIItemData(); - itemData.start_item_list = new List(); - itemData.items= new Dictionary(); - break; - // WORLD.INI - case "[world]": - currentSection = INISection.inisection_world; - worldData = new INIWorldData(); - worldData.test_map_order = new List(); - worldData.worlds = new Dictionary(); - break; - // MENU.INI - case "[general]": - currentSection = INISection.inisection_menu_general; - menuData = new INIMenuData(); - menuData.pages = new Dictionary(); - break; - case "[page0]": - currentSection = INISection.inisection_menu_page0; - break; - case "[page1]": - currentSection = INISection.inisection_menu_page1; - break; - case "[page2]": - currentSection = INISection.inisection_menu_page2; - break; - case "[page3]": - currentSection = INISection.inisection_menu_page3; - break; - case "[page4]": - currentSection = INISection.inisection_menu_page4; - break; - case "[page5]": - currentSection = INISection.inisection_menu_page5; - break; - case "[page6]": - currentSection = INISection.inisection_menu_page6; - break; - case "[page7]": - currentSection = INISection.inisection_menu_page7; - break; - default: - ParseLine(currentSection, line_nocomment); - break; - } - line = streamReader.ReadLine(); - } - streamReader.Close(); - } - catch(Exception ex) - { - Exception ex2 = ex; - while(ex2.InnerException != null) - { - Console.WriteLine($"ex: {ex2.Message}"); - ex2 = ex2.InnerException; - } - throw new Exception($"Failed to load INI file {filename} with error:\n{ex.Message}\nStackTrace:\n${ex.StackTrace}"); - } - } - } -} diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGINIFile.cs.meta b/Assets/Scripts/RGFileImport/RGGFXImport/RGINIFile.cs.meta deleted file mode 100644 index e63a220f..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGINIFile.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: adcdc5384bb1f57769628eca591bdf2b \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGRGMFile.cs b/Assets/Scripts/RGFileImport/RGGFXImport/RGRGMFile.cs deleted file mode 100644 index 089978d6..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGRGMFile.cs +++ /dev/null @@ -1,1336 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; - -namespace RGFileImport -{ - public class RGRGMFile - { - public enum ObjectType - { - object_unknown = 0, - object_3d = 1, - object_audio = 2, - object_flat = 3, - object_lightobject = 4, - object_light = 6, - } - public struct RGMSectionHeader - { - public string sectionName; - public uint dataLength; - - public RGMSectionHeader(MemoryReader memoryReader) - { - try - { - char[] sectionName_char; - sectionName_char = memoryReader.ReadChars(4); - string[] name_strs = new string(sectionName_char).Split('\0'); - sectionName = name_strs[0]; - - if(sectionName == "END ") // yeah theres a space there - { - dataLength = 0; - } - else - { - dataLength = MemoryReader.ReverseBytes(memoryReader.ReadUInt32()); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM section header with error:\n{ex.Message}"); - } - } - public override string ToString() - { - return $@"################################### -RGMHeader -################################### -name: {sectionName} -size: {dataLength:X} -###################################"; - } - } - public struct RGMRAHDSection - { - public uint num_items; // 4 bytes - public Dictionary dict; - public RGMRAHDSection(MemoryReader memoryReader, uint size) - { - try - { - num_items = memoryReader.ReadUInt32(); - dict = new Dictionary(); - for(int i=0;i<(int)num_items;i++) - { - RGMRAHDItem cur = new RGMRAHDItem(i, memoryReader); - dict.Add(cur.scriptName, cur); - } - memoryReader.ReadUInt32(); // no idea what this one is? - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM RAHD section with error:\n{ex.Message}"); - } - } - } - public struct RGMRAHDItem - { - public int index; // not in the file; we need this for attributes - public int unknown0; - public int unknown1; - public string scriptName; - public int MPOBCount; - public int unknown2; - - public int RANMLength; - public int RANMOffset; - - public int RAATOffset; - - public int RAANCount; - public int RAANLength; - public int RAANOffset; - - public int RAGRMaxGroup; - public int RAGROffset; - - public int unknown3; - public int unknown4; - public int unknown5; - - public int RASBCount; - public int RASBLength; - public int RASBOffset; - - public int RASCLength; - public int RASCOffset; - public int RASCThreadStart; - - public int RAHKLength; - public int RAHKOffset; - - public int RALCCount; - public int RALCLength; - public int RALCOffset; - - public int RAEXLength; - public int RAEXOffset; - - public int RAVACount; - public int RAVALength; - public int RAVAOffset; - - public int unknown6; - public int frameCount; - - public int MPSZNormalId; - public int MPSZCombatId; - public int MPSZDeadId; - - public short unknown7; - public short unknown8; - public short unknown9; - public short textureId; - public int RAVCOffset; - - public RGMRAHDItem(int i, MemoryReader memoryReader) - { - index = i; - try - { - unknown0 = memoryReader.ReadInt32(); - unknown1 = memoryReader.ReadInt32(); - - char[] name_char = memoryReader.ReadChars(9); - string[] name_strs = new string(name_char).Split('\0'); - scriptName = name_strs[0]; - MPOBCount = memoryReader.ReadInt32(); - unknown2 = memoryReader.ReadInt32(); - - RANMLength = memoryReader.ReadInt32(); - RANMOffset = memoryReader.ReadInt32(); - - RAATOffset = memoryReader.ReadInt32(); - - RAANCount = memoryReader.ReadInt32(); - RAANLength = memoryReader.ReadInt32(); - RAANOffset = memoryReader.ReadInt32(); - - RAGRMaxGroup = memoryReader.ReadInt32(); - RAGROffset = memoryReader.ReadInt32(); - - unknown3 = memoryReader.ReadInt32(); - unknown4 = memoryReader.ReadInt32(); - unknown5 = memoryReader.ReadInt32(); - - RASBCount = memoryReader.ReadInt32(); - RASBLength = memoryReader.ReadInt32(); - RASBOffset = memoryReader.ReadInt32(); - - RASCLength = memoryReader.ReadInt32(); - RASCOffset = memoryReader.ReadInt32(); - RASCThreadStart= memoryReader.ReadInt32(); - - RAHKLength = memoryReader.ReadInt32(); - RAHKOffset = memoryReader.ReadInt32(); - - RALCCount = memoryReader.ReadInt32(); - RALCLength = memoryReader.ReadInt32(); - RALCOffset = memoryReader.ReadInt32(); - - RAEXLength = memoryReader.ReadInt32(); - RAEXOffset = memoryReader.ReadInt32(); - - RAVACount = memoryReader.ReadInt32(); - RAVALength = memoryReader.ReadInt32(); - RAVAOffset = memoryReader.ReadInt32(); - - unknown6 = memoryReader.ReadInt32(); - frameCount = memoryReader.ReadInt32(); - - MPSZNormalId = memoryReader.ReadInt32(); - MPSZCombatId = memoryReader.ReadInt32(); - MPSZDeadId = memoryReader.ReadInt32(); - - unknown7 = memoryReader.ReadInt16(); - unknown8 = memoryReader.ReadInt16(); - unknown9 = memoryReader.ReadInt16(); - textureId = memoryReader.ReadInt16(); - RAVCOffset = memoryReader.ReadInt32(); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM RAHD item with error:\n{ex.Message}"); - } - } - } - public struct RGMRASTSection - { - public char[] text; // its one big string - public RGMRASTSection(MemoryReader memoryReader, uint size) - { - try - { - text = memoryReader.ReadChars((int)size); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM RAST section with error:\n{ex.Message}"); - } - } - public override string ToString() - { - string o = new String($"###################################\nRAST Data\n###################################"); - for(int i=0;i items; - const int MPOBItem_size = 66; // heres that magic number again - public RGMMPOBSection(MemoryReader memoryReader, uint size) - { - try - { - num_items = memoryReader.ReadUInt32(); - - items = new List(); - for(int i=0;i<(int)num_items;i++) - { - items.Add(new RGMMPOBItem(memoryReader)); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM MPOB section with error:\n{ex.Message}"); - } - } - public override string ToString() - { - string o = new String($"###################################\nMPOB Data\n###################################"); - for(int i=0;i> 7); - imageId = (byte)(textureData&127); - intensity = memoryReader.ReadInt16(); - radius = memoryReader.ReadInt16(); - modelId = memoryReader.ReadInt16(); - worldId = memoryReader.ReadInt16(); - red = memoryReader.ReadInt16(); - green = memoryReader.ReadInt16(); - blue = memoryReader.ReadInt16(); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM MPOB item with error:\n{ex.Message}"); - } - } - public override string ToString() - { - return $@"{scriptName}"; - } - } - - public struct RGMMPSOItem - { - public uint id; // 4 bytes - public string name; // 12 bytes - public int posX; // 4 bytes; increasing moves position east - public int posY; // 4 bytes increasing moves position up - public int posZ; // 4 bytes increasing moves position north - public int[] rotation_matrix; // 36 bytes => 3x3 matrix, uses Q4.28 fixed-point - public byte[] unknown; // 2 bytes always 0 - // for whoevers keeping track: 66 bytes - // this is not 4-byte aligned, so if - // youre implementing this in c/c++ thats - // why you're getting garbage data - - public RGMMPSOItem(MemoryReader memoryReader) - { - try - { - id = memoryReader.ReadUInt32(); - char[] name_char; - name_char = memoryReader.ReadChars(12); - string[] name_strs = new string(name_char).Split('\0'); - name = name_strs[0]; - posX = memoryReader.ReadInt24(); - memoryReader.ReadByte(); // 4 bytes for that s24 above - posY = memoryReader.ReadInt24(); - memoryReader.ReadByte(); - posZ = memoryReader.ReadInt24(); - memoryReader.ReadByte(); - rotation_matrix = new int[9]; - for(int i=0;i<9;i++) - { - rotation_matrix[i] = memoryReader.ReadInt32(); - } - unknown = memoryReader.ReadBytes(2); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM MPSO item with error:\n{ex.Message}"); - } - } - public override string ToString() - { - string o = new string($@"name: {name} "); - for(int i=0;i<9;i++) - { - o+= $" {rotation_matrix[i]},"; - } - return o; - } - } - public struct RGMMPSOSection - { - - public uint num_items; - public List items; - const int MPSOItem_size = 66; // heres that magic number again - public RGMMPSOSection(MemoryReader memoryReader, uint size) - { - try - { - num_items = memoryReader.ReadUInt32(); - items = new List(); - for(int i=0;i<(int)num_items;i++) - { - items.Add(new RGMMPSOItem(memoryReader)); - } - - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM MPSO section with error:\n{ex.Message}"); - } - } - public override string ToString() - { - string o = new String($"###################################\nMPSO Data\n###################################"); - for(int i=0;i items; - const int MPSZItem_size = 49; // heres that magic number again - public RGMMPSZSection(MemoryReader memoryReader, uint size) - { - try - { - num_items = size/MPSZItem_size; - items = new List(); - for(int i=0;i<(int)num_items;i++) - { - items.Add(new RGMMPSZItem(memoryReader)); - } - - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM MPSZ section with error:\n{ex.Message}"); - } - } - } - public struct RGMMPMKItem - { - public int posX; - public int posY; - public int posZ; - - public RGMMPMKItem(MemoryReader memoryReader) - { - try - { - posX = memoryReader.ReadInt24(); - memoryReader.ReadByte(); - posY = memoryReader.ReadInt24(); - memoryReader.ReadByte(); - posZ = memoryReader.ReadInt24(); - memoryReader.ReadByte(); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM MPMK item with error:\n{ex.Message}"); - } - } - } - public struct RGMMPMKSection - { - - public uint num_items; - public List items; - public RGMMPMKSection(MemoryReader memoryReader, uint size) - { - try - { - num_items = memoryReader.ReadUInt32(); - items = new List(); - for(int i=0;i<(int)num_items;i++) - { - items.Add(new RGMMPMKItem(memoryReader)); - } - // unused flags? - memoryReader.ReadBytes((int)num_items); - - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM MPMK section with error:\n{ex.Message}"); - } - } - } - public struct RGMMPSFItem - { - public uint id; - public int unknown0; - public int posX; - public int posY; - public int posZ; - public short textureId; - public short imageId; - public short unknown1; - - public RGMMPSFItem(MemoryReader memoryReader) - { - try - { - id = memoryReader.ReadUInt32(); - unknown0 = memoryReader.ReadInt32(); - - posX = memoryReader.ReadInt24(); - memoryReader.ReadByte(); - posY = memoryReader.ReadInt24(); - memoryReader.ReadByte(); - posZ = memoryReader.ReadInt24(); - memoryReader.ReadByte(); - - ushort textureData = memoryReader.ReadUInt16(); - textureId = (short)(textureData >> 7); - imageId = (short)(textureData&127); - unknown1 = memoryReader.ReadInt16(); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM MPSF item with error:\n{ex.Message}"); - } - } - } - public struct RGMMPSFSection - { - - public uint num_items; - public List items; - public RGMMPSFSection(MemoryReader memoryReader, uint size) - { - try - { - num_items = memoryReader.ReadUInt32(); - items = new List(); - for(int i=0;i<(int)num_items;i++) - { - items.Add(new RGMMPSFItem(memoryReader)); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM MPSF section with error:\n{ex.Message}"); - } - } - } - public struct RGMMPSLItem - { - public uint id; - public int worldId; - public int posX; - public int posY; - public int posZ; - public short radius; - public short intensity; - public short red; - public short green; - public short blue; - public byte[] unknown; // 12 bytes - - public RGMMPSLItem(MemoryReader memoryReader) - { - try - { - id = memoryReader.ReadUInt32(); - worldId = memoryReader.ReadInt32(); - posX = memoryReader.ReadInt32(); - posY = memoryReader.ReadInt32(); - posZ = memoryReader.ReadInt32(); - radius = memoryReader.ReadInt16(); - intensity= memoryReader.ReadInt16(); - red = memoryReader.ReadInt16(); - green = memoryReader.ReadInt16(); - blue = memoryReader.ReadInt16(); - unknown = memoryReader.ReadBytes(12); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM MPSL item with error:\n{ex.Message}"); - } - } - } - public struct RGMMPSLSection - { - - public uint num_items; - public List items; - public RGMMPSLSection(MemoryReader memoryReader, uint size) - { - try - { - num_items = memoryReader.ReadUInt32(); - items = new List(); - for(int i=0;i<(int)num_items;i++) - { - items.Add(new RGMMPSLItem(memoryReader)); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM MPSL section with error:\n{ex.Message}"); - } - } - } - public struct RGMMPRPItem - { - public uint id; - public byte unknown0; - public int posX; - public int posY; - public int posZ; - public int angleY; - public int type; - public int swing; - public int speed; - public short length; - public string staticModel; - public string ropeModel; - public int[] unknown1; // 28 bytes - - public RGMMPRPItem(MemoryReader memoryReader) - { - try - { - id = memoryReader.ReadUInt32(); - unknown0 = memoryReader.ReadByte(); - posX = memoryReader.ReadInt24(); - memoryReader.ReadByte(); - posY = memoryReader.ReadInt24(); - memoryReader.ReadByte(); - posZ = memoryReader.ReadInt24(); - angleY = memoryReader.ReadInt32(); - type = memoryReader.ReadInt32(); - swing = memoryReader.ReadInt32(); - speed = memoryReader.ReadInt32(); - length = memoryReader.ReadInt16(); - - char[] name_char; - name_char = memoryReader.ReadChars(9); - string[] name_strs = new string(name_char).Split('\0'); - staticModel = new string(name_strs[0]); - - name_char = memoryReader.ReadChars(9); - name_strs = new string(name_char).Split('\0'); - ropeModel = new string(name_strs[0]); - - unknown1 = memoryReader.ReadInt32s(7); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM MPRP item with error:\n{ex.Message}"); - } - } - } - public struct RGMMPRPSection - { - - public uint num_items; - public List items; - public RGMMPRPSection(MemoryReader memoryReader, uint size) - { - try - { - num_items = memoryReader.ReadUInt32(); - items = new List(); - for(int i=0;i<(int)num_items;i++) - { - items.Add(new RGMMPRPItem(memoryReader)); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM MPRP section with error:\n{ex.Message}"); - } - } - } - public struct RGMRALCItem - { - public int offsetX; - public int offsetY; - public int offsetZ; - - public RGMRALCItem(MemoryReader memoryReader) - { - try - { - offsetX = memoryReader.ReadInt32(); - offsetY = memoryReader.ReadInt32(); - offsetZ = memoryReader.ReadInt32(); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM RALC item with error:\n{ex.Message}"); - } - } - } - public struct RGMRALCSection - { - - public uint num_items; - public List items; - public RGMRALCSection(MemoryReader memoryReader, uint size) - { - try - { - num_items = size/12; - items = new List(); - for(int i=0;i<(int)num_items;i++) - { - items.Add(new RGMRALCItem(memoryReader)); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM RALC section with error:\n{ex.Message}"); - } - } - } - public struct RGMRAEXItem - { - public short grip0; - public short grip1; - public short scabbard0; - public short scabbard1; - public short unknown0; - public short textureId; - public short vVertex; - public short vSize; - public short tauntId; - public short unknown1; - public short unknown2; - public short unknown3; - public short rangeMin; - public short rangeIdeal; - public short rangeMax; - - public RGMRAEXItem(MemoryReader memoryReader) - { - try - { - grip0 = memoryReader.ReadInt16(); - grip1 = memoryReader.ReadInt16(); - scabbard0 = memoryReader.ReadInt16(); - scabbard1 = memoryReader.ReadInt16(); - unknown0 = memoryReader.ReadInt16(); - textureId = memoryReader.ReadInt16(); - vVertex = memoryReader.ReadInt16(); - vSize = memoryReader.ReadInt16(); - tauntId = memoryReader.ReadInt16(); - unknown1 = memoryReader.ReadInt16(); - unknown2 = memoryReader.ReadInt16(); - unknown3 = memoryReader.ReadInt16(); - rangeMin = memoryReader.ReadInt16(); - rangeIdeal = memoryReader.ReadInt16(); - rangeMax = memoryReader.ReadInt16(); - - - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM RAEX item with error:\n{ex.Message}"); - } - } - } - public struct RGMRAEXSection - { - - public uint num_items; - public List items; - public RGMRAEXSection(MemoryReader memoryReader, uint size) - { - try - { - num_items = size/30; - items = new List(); - for(int i=0;i<(int)num_items;i++) - { - items.Add(new RGMRAEXItem(memoryReader)); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM RAEX section with error:\n{ex.Message}"); - } - } - } - public struct RGMRAANSection - { - - public byte[] data; // we read bytes here, then further process it in animationstore - public RGMRAANSection(MemoryReader memoryReader, uint size) - { - try - { - data = memoryReader.ReadBytes((int)size); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM RAAN section with error:\n{ex.Message}"); - } - } - } - public struct RGMRAGRSection - { - - public byte[] data; // we read bytes here, then further process it in animationstore - public RGMRAGRSection(MemoryReader memoryReader, uint size) - { - try - { - data = memoryReader.ReadBytes((int)size); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM RAGR section with error:\n{ex.Message}"); - } - } - } - public struct RGMRANMSection - { - - public byte[] data; // we read bytes here, then further process it in animationstore - public RGMRANMSection(MemoryReader memoryReader, uint size) - { - try - { - data = memoryReader.ReadBytes((int)size); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM RANM section with error:\n{ex.Message}"); - } - } - } - public struct RGMRAVCItem - { - public byte offsetX; - public byte offsetY; - public byte offsetZ; - public short vertex; - public int radius; - - public RGMRAVCItem(MemoryReader memoryReader) - { - try - { - offsetX = memoryReader.ReadByte(); - offsetY = memoryReader.ReadByte(); - offsetZ = memoryReader.ReadByte(); - vertex = memoryReader.ReadInt16(); - radius = memoryReader.ReadInt32(); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM RAVC item with error:\n{ex.Message}"); - } - } - } - public struct RGMRAVCSection - { - - public uint num_items; - public List items; - public RGMRAVCSection(MemoryReader memoryReader, uint size) - { - try - { - num_items = size/9; - items = new List(); - for(int i=0;i<(int)num_items;i++) - { - items.Add(new RGMRAVCItem(memoryReader)); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM RAVC section with error:\n{ex.Message}"); - } - } - } - - - public struct RGMWDNMNodeRoute - { - public short targetNodeId; - public short cost; - public RGMWDNMNodeRoute(MemoryReader memoryReader) - { - try - { - targetNodeId = memoryReader.ReadInt16(); - cost = memoryReader.ReadInt16(); - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM WDNM noderoute with error:\n{ex.Message}"); - } - } - } - - public struct RGMWDNMWalkNode - { - public uint nodeLength; - public short posX; - public short posY; - public short posZ; - public byte unknown1; - public byte routeCount; - public RGMWDNMNodeRoute[] nodeRoutes; - public RGMWDNMWalkNode(MemoryReader memoryReader) - { - try - { - nodeLength = memoryReader.ReadUInt32(); - posX = memoryReader.ReadInt16(); - posY = memoryReader.ReadInt16(); - posZ = memoryReader.ReadInt16(); - unknown1 = memoryReader.ReadByte(); - routeCount = memoryReader.ReadByte(); - nodeRoutes = new RGMWDNMNodeRoute[routeCount]; - for(int i=0;i items; - public RGMWDNMSection(MemoryReader memoryReader, uint size) - { - try - { - num_items = memoryReader.ReadUInt32(); - items = new List(); - for(int i=0;i<(int)num_items;i++) - { - items.Add(new RGMWDNMItem(memoryReader)); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM WDNM section with error:\n{ex.Message}"); - } - } - } - - - - - - // data - public List Sections; - public RGMRAHDSection RAHD; - public RGMRASTSection RAST; - public RGMRASBSection RASB; - public RGMRAVASection RAVA; - public RGMRASCSection RASC; - public RGMRAHKSection RAHK; - public RGMRALCSection RALC; - public RGMRAEXSection RAEX; - public RGMRAATSection RAAT; - public RGMRAANSection RAAN; - public RGMRAGRSection RAGR; - public RGMRANMSection RANM; - public RGMRAVCSection RAVC; - public RGMMPOBSection MPOB; - public RGMMPRPSection MPRP; - public RGMMPSOSection MPSO; - public RGMMPSLSection MPSL; - public RGMMPSFSection MPSF; - public RGMMPMKSection MPMK; - public RGMMPSZSection MPSZ; - public RGMWDNMSection WDNM; - public long fileSize; - - public void LoadFile(string filename) - { - try - { - byte[] buffer; - BinaryReader binaryReader = new BinaryReader(File.OpenRead(filename)); - fileSize = binaryReader.BaseStream.Length; - buffer = binaryReader.ReadBytes((int)fileSize); - binaryReader.Close(); - LoadMemory(buffer); - - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM file {filename} with error:\n{ex.Message}"); - } - } - - public void LoadMemory(byte[] buffer) - { - try - { - MemoryReader memoryReader = new MemoryReader(buffer); - Sections = new List(); - bool end = false; - while(!end) - { - Sections.Add(new RGMSectionHeader(memoryReader)); - if(Sections[Sections.Count-1].sectionName == "END ") - { - end = true; - } - else if(Sections[Sections.Count-1].sectionName == "RAHD") - { - RAHD = new RGMRAHDSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "RAST") - { - RAST = new RGMRASTSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "RASB") - { - RASB = new RGMRASBSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "RAVA") - { - RAVA = new RGMRAVASection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "RASC") - { - RASC = new RGMRASCSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - - else if(Sections[Sections.Count-1].sectionName == "RAHK") - { - RAHK = new RGMRAHKSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "RAAT") - { - RAAT = new RGMRAATSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "MPOB") - { - MPOB = new RGMMPOBSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "MPSO") - { - MPSO = new RGMMPSOSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "MPSZ") - { - MPSZ = new RGMMPSZSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "MPMK") - { - MPMK = new RGMMPMKSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "MPSF") - { - MPSF = new RGMMPSFSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "MPSL") - { - MPSL = new RGMMPSLSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "MPRP") - { - MPRP = new RGMMPRPSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "RALC") - { - RALC = new RGMRALCSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "RAEX") - { - RAEX = new RGMRAEXSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "RAAN") - { - RAAN = new RGMRAANSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "RAGR") - { - RAGR = new RGMRAGRSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "RANM") - { - RANM = new RGMRANMSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "RAVC") - { - RAVC = new RGMRAVCSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else if(Sections[Sections.Count-1].sectionName == "WDNM") - { - WDNM = new RGMWDNMSection(memoryReader, Sections[Sections.Count-1].dataLength); - } - else - { - memoryReader.Seek(Sections[Sections.Count-1].dataLength, (uint)memoryReader.Position); - } - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load RGM file from memory with error:\n{ex.Message}"); - } - } - - - public void PrintRGM() - { - Console.WriteLine(MPSO); - Console.WriteLine(MPOB); - } - } -} diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGRGMFile.cs.meta b/Assets/Scripts/RGFileImport/RGGFXImport/RGRGMFile.cs.meta deleted file mode 100644 index c08084ce..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGRGMFile.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: d2f0c2246330b82a086869904bb9c8ed \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGROBFile.cs b/Assets/Scripts/RGFileImport/RGGFXImport/RGROBFile.cs deleted file mode 100644 index 5e47789c..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGROBFile.cs +++ /dev/null @@ -1,164 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; - -namespace RGFileImport -{ - public class RGROBFile - { - public struct ROBHeader - { - public char[] OARC; - public uint Unknown4; - public uint NumSegments; - public uint OARD; - public uint UnknownId; - - public ROBHeader(MemoryReader memoryReader) - { - try - { - OARC = memoryReader.ReadChars(4); - Unknown4 = memoryReader.ReadUInt32(); - NumSegments = memoryReader.ReadUInt32(); - OARD = memoryReader.ReadUInt32(); - UnknownId = memoryReader.ReadUInt32(); - } - catch(Exception ex) - { - throw new Exception($"Failed to load ROB header with error:\n{ex.Message}"); - } - } - public override string ToString() - { - return $@"################################### -ROBHeader -################################### -OARC: {new string(OARC)} -Unknown4: {Unknown4:X} -NumSegments: {NumSegments:X} -OARD: {OARD:X} -UnknownId: {UnknownId:X} -###################################"; - } - } - public struct ROBSegmentHeader - { - public uint Unknown0; - public string SegmentID; // read as char[] - public uint UnknownType; - public uint Unknown1; - public uint Unknown2; - public uint Unknown3; - public uint UnknownInt1; - public uint UnknownInt2; - public uint UnknownInt3; - public uint Unknown4; - public uint Unknown5; - public uint Unknown6; - public uint UnknownInt4; - public uint UnknownInt5; - public uint UnknownInt6; - public uint UnknownInt7; - public uint UnknownInt8; - public uint UnknownInt9; - public uint Size; - public byte[] Data; - - public ROBSegmentHeader(MemoryReader memoryReader) - { - try - { - char[] SegmentID_char; - Unknown0 = memoryReader.ReadUInt32(); - SegmentID_char = memoryReader.ReadChars(8); - string[] segment_strs = new string(SegmentID_char).Split('\0'); - SegmentID = segment_strs[0]; - - UnknownType = memoryReader.ReadUInt32(); - Unknown1 = memoryReader.ReadUInt32(); - Unknown2 = memoryReader.ReadUInt32(); - Unknown3 = memoryReader.ReadUInt32(); - UnknownInt1 = memoryReader.ReadUInt32(); - UnknownInt2 = memoryReader.ReadUInt32(); - UnknownInt3 = memoryReader.ReadUInt32(); - Unknown4 = memoryReader.ReadUInt32(); - Unknown5 = memoryReader.ReadUInt32(); - Unknown6 = memoryReader.ReadUInt32(); - UnknownInt4 = memoryReader.ReadUInt32(); - UnknownInt5 = memoryReader.ReadUInt32(); - UnknownInt6 = memoryReader.ReadUInt32(); - UnknownInt7 = memoryReader.ReadUInt32(); - UnknownInt8 = memoryReader.ReadUInt32(); - UnknownInt9 = memoryReader.ReadUInt32(); - Size = memoryReader.ReadUInt32(); - Data = memoryReader.ReadBytes((int)Size); - } - catch(Exception ex) - { - throw new Exception($"Failed to load ROB segment header with error:\n{ex.Message}"); - } - } - public override string ToString() - { - return $@"SegmentId: {Unknown0},{SegmentID},{UnknownType},{Unknown1},{Unknown2},{Unknown3},{UnknownInt1},{UnknownInt2},{UnknownInt3},{Unknown4},{Unknown5},{Unknown6},{UnknownInt4},{UnknownInt5},{UnknownInt6},{UnknownInt7},{UnknownInt8},{UnknownInt9}, {Size}"; - } - - } - - - // data - public ROBHeader hdr; - public ROBSegmentHeader[] segments; - public long fileSize; - - public void LoadFile(string filename) - { - try - { - byte[] buffer; - BinaryReader binaryReader = new BinaryReader(File.OpenRead(filename)); - fileSize = binaryReader.BaseStream.Length; - buffer = binaryReader.ReadBytes((int)fileSize); - binaryReader.Close(); - LoadMemory(buffer); - } - catch(Exception ex) - { - throw new Exception($"Failed to load ROB file {filename} with error:\n{ex.Message}"); - } - } - public void LoadMemory(byte[] buffer) - { - try - { - MemoryReader memoryReader = new MemoryReader(buffer); - hdr = new ROBHeader(memoryReader); - - segments = new ROBSegmentHeader[hdr.NumSegments]; - const int rob_seg_hdr_size = 80; - int ptr = 0; - for(int i=0;i rtxItemDict; - - public void LoadFile(string filename) - { - try - { - byte[] buffer; - BinaryReader binaryReader = new BinaryReader(File.OpenRead(filename)); - fileSize = binaryReader.BaseStream.Length; - buffer = binaryReader.ReadBytes((int)fileSize); - binaryReader.Close(); - LoadMemory(buffer); - - } - catch(Exception ex) - { - throw new Exception($"Failed to load RTX file {filename} with error:\n{ex.Message}"); - } - } - - public void LoadMemory(byte[] buffer) - { - try - { - MemoryReader memoryReader = new MemoryReader(buffer); - rtxItemDict = new Dictionary(); - bool end = false; - while(!end) - { - // we read the label as an int so we can skip the conversion to string - int label = memoryReader.ReadInt32(); - int size = memoryReader.ReadInt32(); - if(label == 0x20444E45) // "END " with the space as usual - end = true; - else - { - rtxItemDict.Add(label, new RTXItem(memoryReader)); - } - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load RTX file from memory with error:\n{ex.Message}"); - } - } - } -} diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGRTXFile.cs.meta b/Assets/Scripts/RGFileImport/RGGFXImport/RGRTXFile.cs.meta deleted file mode 100644 index 647be002..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGRTXFile.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: b912bce6547a995aca5036bdd16685cb \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGSFXFile.cs b/Assets/Scripts/RGFileImport/RGGFXImport/RGSFXFile.cs deleted file mode 100644 index a8c8d67f..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGSFXFile.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; - -namespace RGFileImport -{ - public class RGSFXFile - { - public struct SFXSectionHeader - { - public string sectionName; - public uint dataLength; - - public SFXSectionHeader(MemoryReader memoryReader) - { - try - { - char[] sectionName_char; - sectionName_char = memoryReader.ReadChars(4); - string[] name_strs = new string(sectionName_char).Split('\0'); - sectionName = name_strs[0]; - - if(sectionName == "END ") // yeah theres a space there - { - dataLength = 0; - } - else - { - dataLength = MemoryReader.ReverseBytes(memoryReader.ReadUInt32()); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load SFX section header with error:\n{ex.Message}"); - } - } - public override string ToString() - { - return $@"################################### -SFXHeader -################################### -name: {sectionName} -size: {dataLength:X} -###################################"; - } - } - public struct FXHDSection - { - public string description; - public int numSounds; - public FXHDSection(MemoryReader memoryReader) - { - try - { - char[] tchar = memoryReader.ReadChars(32); - description = new string(tchar); - numSounds = memoryReader.ReadInt32(); - } - catch(Exception ex) - { - throw new Exception($"Failed to load SFX FXHD section with error:\n{ex.Message}"); - } - } - } - public struct FXDTSection - { - public List soundEffectList; - - public FXDTSection(MemoryReader memoryReader, FXHDSection FXHD) - { - try - { - soundEffectList = new List(); - for(int i=0;i Sections; - public FXHDSection FXHD; - public FXDTSection FXDT; - - public void LoadFile(string filename) - { - try - { - byte[] buffer; - BinaryReader binaryReader = new BinaryReader(File.OpenRead(filename)); - fileSize = binaryReader.BaseStream.Length; - buffer = binaryReader.ReadBytes((int)fileSize); - binaryReader.Close(); - LoadMemory(buffer); - - } - catch(Exception ex) - { - throw new Exception($"Failed to load SFX file {filename} with error:\n{ex.Message}"); - } - } - - public void LoadMemory(byte[] buffer) - { - try - { - MemoryReader memoryReader = new MemoryReader(buffer); - Sections = new List(); - bool end = false; - while(!end) - { - Sections.Add(new SFXSectionHeader(memoryReader)); - if(Sections[Sections.Count-1].sectionName == "END ") - { - end = true; - } - else if(Sections[Sections.Count-1].sectionName == "FXHD") - { - FXHD = new FXHDSection(memoryReader); - } - else if(Sections[Sections.Count-1].sectionName == "FXDT") - { - FXDT = new FXDTSection(memoryReader, FXHD); - } - else - { - memoryReader.Seek(Sections[Sections.Count-1].dataLength, (uint)memoryReader.Position); - } - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load SFX file from memory with error:\n{ex.Message}"); - } - } - } -} diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGSFXFile.cs.meta b/Assets/Scripts/RGFileImport/RGGFXImport/RGSFXFile.cs.meta deleted file mode 100644 index 5c5bcc24..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGSFXFile.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: da71bc3d4553a7da2be91a9d4a268aa4 \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGSoundData.cs b/Assets/Scripts/RGFileImport/RGGFXImport/RGSoundData.cs deleted file mode 100644 index dae09a56..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGSoundData.cs +++ /dev/null @@ -1,70 +0,0 @@ -using UnityEngine; -using System; - -namespace RGFileImport -{ - // Audio is common between SFX and RTX files - public enum RGAudioType - { - audiotype_mono8 = 0, - audiotype_mono16 = 1, - audiotype_stereo8 = 2, - audiotype_stereo16 = 3, - } - public enum RGAudioBitDepth - { - audiodepth_8 = 0, - audiodepth_16 = 1, - } - public struct RGSoundEffect - { - public RGAudioType typeId; - public RGAudioBitDepth bitDepth; - public int sampleRate; - public char unknown1; - public char loopFlag; - public int loopOffset; - public int loopEnd; - public int dataLength; - public char unknown2; - public byte[] PCMData; - public RGSoundEffect(MemoryReader memoryReader, bool shouldRead=true) - { - if(!shouldRead) - { - typeId = 0; - bitDepth = 0; - sampleRate = 0; - unknown1 = (char)0; - loopFlag = (char)0; - loopOffset = 0; - loopEnd = 0; - dataLength = 0; - unknown2 = (char)0; - PCMData = new byte[1]; - } - else - { - try - { - typeId = (RGAudioType)memoryReader.ReadInt32(); - bitDepth = (RGAudioBitDepth)memoryReader.ReadInt32(); - sampleRate = memoryReader.ReadInt32(); - unknown1 = memoryReader.ReadChar(); - loopFlag = memoryReader.ReadChar(); - loopOffset = memoryReader.ReadInt32(); - loopEnd = memoryReader.ReadInt32(); - dataLength = memoryReader.ReadInt32(); - unknown2 = memoryReader.ReadChar(); - - PCMData = memoryReader.ReadBytes(dataLength); - } - catch(Exception ex) - { - throw new Exception($"Failed to load SFX sound effect with error:\n{ex.Message}"); - } - } - } - - } -} diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGSoundData.cs.meta b/Assets/Scripts/RGFileImport/RGGFXImport/RGSoundData.cs.meta deleted file mode 100644 index f57b785d..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGSoundData.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 490262a62df493cb2bb8eb7e2d4d483d \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGTEXBSIFile.cs b/Assets/Scripts/RGFileImport/RGGFXImport/RGTEXBSIFile.cs deleted file mode 100644 index 5cd0a2b3..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGTEXBSIFile.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; - -namespace RGFileImport -{ - public class RGTEXBSIFile - { - public struct ImageSection - { - public string imageName; - public int imageSize; - public RGBSIFile imageData; - - public ImageSection(MemoryReader memoryReader) - { - try - { - imageData = new RGBSIFile(); - - char[] ImageName_char; - ImageName_char = memoryReader.ReadChars(9); - string[] name_strs = new string(ImageName_char).Split('\0'); - imageName = name_strs[0]; - if(imageName.Length == 0) // END basically - { - imageSize = 0; - // no actual image - } - else - { - imageSize = memoryReader.ReadInt32(); - imageData.LoadMemory(memoryReader.ReadBytes(imageSize)); - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load ImageSection with error:\n{ex.Message}"); - } - } - } - - // data - public long fileSize; - public List images; - - public void LoadFile(string filename) - { - try - { - byte[] buffer; - BinaryReader binaryReader = new BinaryReader(File.OpenRead(filename)); - fileSize = binaryReader.BaseStream.Length; - buffer = binaryReader.ReadBytes((int)fileSize); - binaryReader.Close(); - LoadMemory(buffer); - } - catch(Exception ex) - { - throw new Exception($"Failed to load TEXBSI file {filename} with error:\n{ex.Message}"); - } - } - public void LoadMemory(byte[] buffer) - { - try - { - MemoryReader memoryReader = new MemoryReader(buffer); - images = new List(); - bool end = false; - while(!end) - { - ImageSection cur = new ImageSection(memoryReader); - if(cur.imageName.Length == 0) - { - end = true; - } - else - { - images.Add(cur); - } - } - } - catch(Exception ex) - { - throw new Exception($"Failed to load TEXBSI file from memory with error:\n{ex.Message}"); - } - } - } -} diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGTEXBSIFile.cs.meta b/Assets/Scripts/RGFileImport/RGGFXImport/RGTEXBSIFile.cs.meta deleted file mode 100644 index 575c1a84..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGTEXBSIFile.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 93d9d8321f876d3de982afa18ffb1b1c \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGWLDFile.cs b/Assets/Scripts/RGFileImport/RGGFXImport/RGWLDFile.cs deleted file mode 100644 index 6e987a8e..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGWLDFile.cs +++ /dev/null @@ -1,423 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Collections.Generic; -using UnityEngine; - -namespace RGFileImport -{ - public class RGWLDFile - { - // Engine uses 256 units per grid cell (grid→world scale stored as - // double at 0x00198dc4 in RGFX.EXE). Divided by the model vertex - // export scale to get the Unity-space cell size. - // Previous empirical value was 12.9; the exact ratio is 256/20 = 12.8. - const float WLD_SIZE_SCALE = 256.0f / 20.0f; - - - // Engine height lookup table (128 entries, indexed by 7-bit heightmap - // byte). Values are in engine units; divided by 20 at lookup time to - // match the model vertex export scale. The engine negates these at - // load time (-ABS(value)) so terrain sits below a water-level - // reference plane. For Unity (Y-up) we keep them positive. - // - // Replaces the previous quartic polynomial approximation which had a - // +1.93 base offset at index 0 (should be 0) and diverged at high - // indices (239 vs 388 at index 127). - // - // Full table documentation: - // https://michidk.github.io/redguard-preservation/formats/WLD.html#height-lookup-table - private static readonly float[] WLD_HEIGHT_TABLE = new float[128] - { - 0, 40, 40, 40, 80, 80, 80, 120, 120, 120, - 160, 160, 160, 200, 200, 200, 240, 240, 240, 280, - 280, 320, 320, 320, 360, 360, 400, 400, 400, 440, - 440, 480, 480, 480, 520, 520, 560, 560, 600, 600, - 600, 640, 640, 680, 680, 720, 720, 760, 760, 800, - 800, 840, 840, 880, 880, 920, 920, 960, 1000, 1000, - 1040, 1040, 1080, 1120, 1120, 1160, 1160, 1200, 1240, 1240, - 1280, 1320, 1320, 1360, 1400, 1440, 1440, 1480, 1520, 1560, - 1600, 1600, 1640, 1680, 1720, 1760, 1800, 1840, 1880, 1920, - 1960, 2000, 2040, 2080, 2120, 2200, 2240, 2280, 2320, 2400, - 2440, 2520, 2560, 2640, 2680, 2760, 2840, 2920, 3000, 3080, - 3160, 3240, 3360, 3440, 3560, 3680, 3800, 3960, 4080, 4280, - 4440, 4680, 4920, 5200, 5560, 6040, 6680, 7760, - }; - - private float WLD_HEIGHT_FUN(int y) - { - return WLD_HEIGHT_TABLE[y & 0x7F] / 20.0f; - } - - public struct WLDHeader - { - const int unknown1_size = 6; - const int unknown2_size = 28; - const int unknown3_size = 256; - const int num_sections = 4; - - public int[] unknown1; // 6*4bytes - public int sec_hdr_size; - public int file_size; - public int[] unknown2; // 28*4 bytes - public int[] sec_ofs; // 4*4 bytes - public int[] unknown3; // 256*4 bytes - - public WLDHeader(MemoryReader memoryReader) - { - try - { - unknown1 = memoryReader.ReadInt32s(unknown1_size); - sec_hdr_size = memoryReader.ReadInt32(); - file_size = memoryReader.ReadInt32(); - unknown2 = memoryReader.ReadInt32s(unknown2_size); - sec_ofs = memoryReader.ReadInt32s(num_sections); - unknown3 = memoryReader.ReadInt32s(unknown3_size); - } - catch(Exception ex) - { - throw new Exception($"Failed to load WLD header with error:\n{ex.Message}"); - } - } - public override string ToString() - { - return $@"################################### -IO_WLD_hdr_t -################################### -unknown1: [{string.Join(", ", unknown1)}] -sec_hdr_size: {sec_hdr_size} -file_size: {file_size} -unknown2: [{string.Join(", ", unknown2)}] -sec_1_ofs: {sec_ofs[0]} -sec_2_ofs: {sec_ofs[1]} -sec_3_ofs: {sec_ofs[2]} -sec_4_ofs: {sec_ofs[3]} -unknown3: [{string.Join(", ", unknown3)}] -###################################"; - } - } - public struct WLDSection - { - const int unknown1_size = 3; - const int unknown2_size = 6; - // header - public short[] unknown1; // 3*2 bytes - public short texbsi_file; - public int size; // always 256, assuming size - public short[] unknown2; // 6*2 bytes - // map data - public byte[] map1; - public byte[] map2; - public byte[] map3; - public byte[] map4; - - public WLDSection(MemoryReader memoryReader) - { - try - { - unknown1 = memoryReader.ReadInt16s(unknown1_size); - texbsi_file = memoryReader.ReadInt16(); - size = (int)memoryReader.ReadInt16(); - unknown2 = memoryReader.ReadInt16s(unknown2_size); - - int size_half = size/2; - map1 = memoryReader.ReadBytes(size_half*size_half); - map2 = memoryReader.ReadBytes(size_half*size_half); - map3 = memoryReader.ReadBytes(size_half*size_half); - map4 = memoryReader.ReadBytes(size_half*size_half); - } - catch(Exception ex) - { - throw new Exception($"Failed to load WLD section with error:\n{ex.Message}"); - } - } - public override string ToString() - { - return $@"################################### -IO_WLD_section_t -################################### -unknown1: [{string.Join(", ", unknown1)}] -texbsi_file: {texbsi_file} -size: {size} -unknown2: [{string.Join(", ", unknown2)}] -###################################"; - } - } - - public struct WLDMaps - { - // bitmaps - public int map_size; - public byte[] heightmap; - public byte[] heightmap_flag; - public byte[] texturemap; - public byte[] texturemap_flag; - - public WLDMaps(WLDHeader hdr, WLDSection[] sections) - { - map_size = sections[0].size; - int size = map_size; - int size_half = map_size/2; - heightmap = new byte[size*size]; - heightmap_flag = new byte[size*size]; - texturemap = new byte[size*size]; - texturemap_flag = new byte[size*size]; - - for(int s=0;s<4;s++) - { - for(int y=0;y=2?1:0)); - int mw = size_half*2; - Array.Copy(sections[s].map1, y*size_half, heightmap, mx+(my*mw), size_half); - Array.Copy(sections[s].map1, y*size_half, heightmap_flag, mx+(my*mw), size_half); - Array.Copy(sections[s].map3, y*size_half, texturemap, mx+(my*mw), size_half); - Array.Copy(sections[s].map3, y*size_half, texturemap_flag, mx+(my*mw), size_half); - } - } - for(int i=0;i>6); // last 2 bits are texture rotation - } - } - public override string ToString() - { - return $@"################################### -IO_WLD_data_t -################################### -###################################"; - } - } - public struct WLDMesh - { - public List vertices; - public List normals; - public List uv; - public List triangles; - public int TextureId; - - public WLDMesh(int texid) - { - TextureId = texid; - vertices = new List(); - normals = new List(); - uv = new List(); - triangles = new List(); - } - - public void AppendTri(Vector3 a, Vector3 b, Vector3 c, - Vector3 na, Vector3 nb, Vector3 nc, - Vector2 uva, Vector2 uvb, Vector2 uvc) - { - vertices.Add(a); - vertices.Add(b); - vertices.Add(c); - normals.Add(na); - normals.Add(nb); - normals.Add(nc); - uv.Add(uva); - uv.Add(uvb); - uv.Add(uvc); - triangles.Add(vertices.Count()-3); - triangles.Add(vertices.Count()-2); - triangles.Add(vertices.Count()-1); - } - public int GetTextureId() - { - return TextureId; - } - public Vector3[] GetVertices() - { - return vertices.ToArray(); - } - public Vector2[] GetUv() - { - return uv.ToArray(); - } - - } - - // data - WLDHeader hdr; - public WLDSection[] sec; - public WLDMaps maps_data; - public WLDMesh[] meshes; - public long fileSize; - - public void LoadFile(string filename) - { - try - { - byte[] buffer; - BinaryReader binaryReader = new BinaryReader(File.OpenRead(filename)); - fileSize = binaryReader.BaseStream.Length; - buffer = binaryReader.ReadBytes((int)fileSize); - binaryReader.Close(); - LoadMemory(buffer); - } - catch(Exception ex) - { - throw new Exception($"Failed to load WLD file {filename} with error:\n{ex.Message}"); - } - } - - public void LoadMemory(byte[] buffer) - { - try - { - MemoryReader memoryReader = new MemoryReader(buffer); - hdr = new WLDHeader(memoryReader); - sec = new WLDSection[4]; - for(int i=0;i<4;i++) - { - sec[i] = new WLDSection(memoryReader); - } - maps_data = new WLDMaps(hdr, sec); - - } - catch(Exception ex) - { - throw new Exception($"Failed to load WLD file from memory with error:\n{ex.Message}"); - } - } - static Vector3 TriNormal(Vector3 a, Vector3 b, Vector3 c) - { - return Vector3.Cross(b - a, c - a); - } - - public void BuildMeshes() - { - // assuming always 64 textures; safe bet? - const int texid_cnt = 64; - - // hardcoded to avoid rounding errors in UVs - Vector2[,] uv_rotations = - { - { // 00 - new Vector2(1.0f, 1.0f), - new Vector2(1.0f, 0.0f), - new Vector2(0.0f, 0.0f), - new Vector2(0.0f, 0.0f), - new Vector2(0.0f, 1.0f), - new Vector2(1.0f, 1.0f) - }, - { // 01 - new Vector2(0.0f, 1.0f), - new Vector2(1.0f, 1.0f), - new Vector2(1.0f, 0.0f), - new Vector2(1.0f, 0.0f), - new Vector2(0.0f, 0.0f), - new Vector2(0.0f, 1.0f) - }, - { // 10 - new Vector2(0.0f, 0.0f), - new Vector2(0.0f, 1.0f), - new Vector2(1.0f, 1.0f), - new Vector2(1.0f, 1.0f), - new Vector2(1.0f, 0.0f), - new Vector2(0.0f, 0.0f) - }, - { // 11 - new Vector2(1.0f, 0.0f), - new Vector2(0.0f, 0.0f), - new Vector2(0.0f, 1.0f), - new Vector2(0.0f, 1.0f), - new Vector2(1.0f, 1.0f), - new Vector2(1.0f, 0.0f) - }, - }; - - int map_size = maps_data.map_size; - int cells = map_size - 1; - - // Vertex position lookup - System.Func pos = (px,py) => - new Vector3((float)px*WLD_SIZE_SCALE, - WLD_HEIGHT_FUN(maps_data.heightmap[px+py*map_size]), - -(float)py*WLD_SIZE_SCALE); - - // Pass 1: face normals per cell (two triangles each) - // Cell (x,y) corners: TL=(x,y) TR=(x+1,y) BL=(x,y+1) BR=(x+1,y+1) - // Tri1: TR->BR->TL Tri2: BL->TL->BR - Vector3[] faceNormals1 = new Vector3[cells * cells]; - Vector3[] faceNormals2 = new Vector3[cells * cells]; - for(int y=0;y0 && gy>0) - acc += faceNormals1[(gx-1)+(gy-1)*cells] + faceNormals2[(gx-1)+(gy-1)*cells]; - if(gx0) - acc += faceNormals2[gx+(gy-1)*cells]; - if(gx>0 && gyBR->TL - meshes[tex_id].AppendTri(tr, br, tl, n_tr, n_br, n_tl, - uv_rotations[tex_rot,1], uv_rotations[tex_rot,0], uv_rotations[tex_rot,2]); - // Tri2: BL->TL->BR - meshes[tex_id].AppendTri(bl, tl, br, n_bl, n_tl, n_br, - uv_rotations[tex_rot,4], uv_rotations[tex_rot,3], uv_rotations[tex_rot,5]); - } - } - } - - public void PrintWLD() - { - Console.WriteLine(hdr); - for(int i=0;i<4;i++) - { - Console.WriteLine(sec[i]); - } - } - } -} diff --git a/Assets/Scripts/RGFileImport/RGGFXImport/RGWLDFile.cs.meta b/Assets/Scripts/RGFileImport/RGGFXImport/RGWLDFile.cs.meta deleted file mode 100644 index f7e2855b..00000000 --- a/Assets/Scripts/RGFileImport/RGGFXImport/RGWLDFile.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: ecfc0394b12a90feba1cc384f6881e52 \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGINIStore.cs b/Assets/Scripts/RGFileImport/RGINIStore.cs deleted file mode 100644 index fba10ca5..00000000 --- a/Assets/Scripts/RGFileImport/RGINIStore.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; -using RGFileImport; -using Assets.Scripts.RGFileImport.RGGFXImport; - -public static class RGINIStore -{ - static RGINIFile iniItemData; - static RGINIFile iniWorldData; - static RGINIFile iniMenuData; - - static RGINIStore() - { - iniItemData = new RGINIFile(); - iniItemData.LoadFile($"{Game.pathManager.GetRootFolder()}/ITEM.INI"); - iniWorldData = new RGINIFile(); - iniWorldData.LoadFile($"{Game.pathManager.GetRootFolder()}/WORLD.INI"); - iniMenuData = new RGINIFile(); - iniMenuData.LoadFile($"{Game.pathManager.GetRootFolder()}/MENU.INI"); - } - - //ITEM.INI - public struct itemData - { - public string name; - public string description; - public Material material; - public Material material_selected; - public RGMeshStore.UnityData_3D inventoryModel; - } - static int stringToRTXId(string s) - { - byte[] chars = System.Text.Encoding.ASCII.GetBytes(s.ToLower()); - int o = BitConverter.ToInt32(chars); - return o; - } - - // We need the RTX, ROB and textures to be loaded at this point - public static Dictionary GetItemList() - { - // bitmap example input: SYSTEM\PICKUPS.GXA - // remove SYSTEM\ and .GXA: - string bitmap = iniItemData.itemData.bitmap_file.Substring(7).ToUpper(); - bitmap = bitmap.Substring(0, bitmap.IndexOf(".")); - - string bitmap_selected = iniItemData.itemData.bitmap_selected_file.Substring(7).ToUpper(); - bitmap_selected = bitmap_selected.Substring(0, bitmap_selected.IndexOf(".")); - - Dictionary o = new Dictionary(); - foreach(int key in iniItemData.itemData.items.Keys) - { - itemData i = new itemData(); - RGINIFile.INIItem item = iniItemData.itemData.items[key]; - - int nameId = stringToRTXId(item.name); - int descriptionId = stringToRTXId(item.description); - - i.name = RGSoundStore.GetRTX(nameId).subtitle; - i.description = RGSoundStore.GetRTX(descriptionId).subtitle; - - i.material = RGTexStore.GetMaterial_GXA(bitmap, item.bitmap); - i.material_selected = RGTexStore.GetMaterial_GXA(bitmap_selected, item.bitmap); - - // model example input: 3DART\Isword.3D - // remove 3DART\ and .GXA and make it uppercase - string modelname = item.inventory_object_file.Substring(6).ToUpper(); - modelname = modelname.Substring(0, modelname.IndexOf(".")); - i.inventoryModel = RGMeshStore.LoadMesh(RGMeshStore.mesh_type.mesh_3d, - modelname, - "ISLAND"); // TODO: which COL do we want here? - - o.Add(key, i); - } - return o; - } - - // WORLD.INI - public struct worldData - { - public string RGM; // doubles as name - public string WLD; - public string COL; - public string skyBoxGXA; - public string skyBoxBSI; - public string sunImage; - public string loadScreen; - } - public static Dictionary GetWorldList() - { - Dictionary o = new Dictionary(); - foreach(int key in iniWorldData.worldData.worlds.Keys) - { - worldData w = new worldData(); - RGINIFile.INIWorld world = iniWorldData.worldData.worlds[key]; - if(!string.IsNullOrEmpty(world.map)) - { - // example input: MAPS\start.rgm - // remove MAPS/ and .rgm: - w.RGM = world.map.Substring(5).ToUpper(); - w.RGM = w.RGM.Substring(0, w.RGM.IndexOf(".")); - - } - else - w.RGM = ""; - if(!string.IsNullOrEmpty(world.world)) - { - // example input: MAPS\hideout.WLD - // remove MAPS/ and .WLD: - w.WLD = world.world.Substring(5).ToUpper(); - w.WLD = w.WLD.Substring(0, w.WLD.IndexOf(".")); - } - else - w.WLD = ""; - if(!string.IsNullOrEmpty(world.palette)) - { - // example input: 3DART\sunset.COL - // remove 3DART/ and .COL: - w.COL = world.palette.Substring(6).ToUpper(); - w.COL = w.COL.Substring(0, w.COL.IndexOf(".")); - } - else - w.COL = ""; - if(!string.IsNullOrEmpty(world.sky)) - { - // example input: system\sunset.GXA - // remove system/ and .GXA: - w.skyBoxGXA = world.sky.Substring(7).ToUpper(); - w.skyBoxGXA = w.skyBoxGXA.Substring(0, w.skyBoxGXA.IndexOf(".")); - } - else - w.skyBoxGXA = ""; - if(!string.IsNullOrEmpty(world.skyfx)) - { - // example input: sky899.bsi - // remove .BSI: - w.skyBoxBSI = world.skyfx.Substring(0, world.skyfx.IndexOf(".")).ToUpper(); - } - else - w.skyBoxBSI = ""; - if(!string.IsNullOrEmpty(world.sunimg)) - { - // example input: sun001.bsi - // remove .BSI: - w.skyBoxBSI = world.sunimg.Substring(0, world.sunimg.IndexOf(".")).ToUpper(); - } - else - w.sunImage = ""; - if(!string.IsNullOrEmpty(world.flash_filename)) - { - // example input: system\island.gxa - // remove system/ and .gxa - w.loadScreen = world.flash_filename.Substring(7).ToUpper(); - w.loadScreen = w.loadScreen.Substring(0, w.loadScreen.IndexOf(".")); - } - else - w.loadScreen = ""; - - o.Add(key, w); - } - return o; - } - // MENU.INI - public enum textJustify - { - textjustify_left, - textjustify_right, - } - public struct menuItemData - { - public int textX; - public int textY; - public string text; - public textJustify justify; - - } - public struct menuData - { - public List itemData; - } - -} diff --git a/Assets/Scripts/RGFileImport/RGINIStore.cs.meta b/Assets/Scripts/RGFileImport/RGINIStore.cs.meta deleted file mode 100644 index 6e5c8ca4..00000000 --- a/Assets/Scripts/RGFileImport/RGINIStore.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 6720b6f9637496dc8a594178dd8f8e33 \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGMData/RGObjectStore.cs b/Assets/Scripts/RGFileImport/RGMData/RGObjectStore.cs index 7bde1ec3..0d30d121 100644 --- a/Assets/Scripts/RGFileImport/RGMData/RGObjectStore.cs +++ b/Assets/Scripts/RGFileImport/RGMData/RGObjectStore.cs @@ -277,7 +277,7 @@ public PlayerInventory(int itemCount=86) { activeItem = -1; items = new int[itemCount]; - // TODO: set starting items here, should come from RGINIStore + // TODO: set starting items from game data source } public int HaveItem(int id) { diff --git a/Assets/Scripts/RGFileImport/RGMData/RGScriptedObject.cs b/Assets/Scripts/RGFileImport/RGMData/RGScriptedObject.cs index 028bfa44..5ca9687f 100644 --- a/Assets/Scripts/RGFileImport/RGMData/RGScriptedObject.cs +++ b/Assets/Scripts/RGFileImport/RGMData/RGScriptedObject.cs @@ -32,7 +32,7 @@ public enum TaskType task_facing, task_animating, } - // TODO: this is duplicated from RGRGMStore + // TODO: this is duplicated from runtime transform helper const float RGM_MPOB_SCALE = 1/5120.0f; public string objectName; @@ -152,19 +152,27 @@ public void AddLight() public void Instanciate3DObject(RGFileImport.RGRGMFile.RGMMPOBItem MPOB, RGFileImport.RGRGMFile filergm, string name_col) { skinnedMeshRenderer = gameObject.AddComponent(); - - animations = new AnimData(MPOB.scriptName); + + try + { + animations = new AnimData(MPOB.scriptName); + } + catch + { + animations = null; + } currentMesh = 0; - if(animations.animationData.RAANItems.Count > 0) + if(animations != null && animations.animationData.RAANItems.Count > 0) { Debug.Log($"ANIMATED {scriptName}"); type = ScriptedObjectType.scriptedobject_animated; - RGMeshStore.UnityData_3D data_3D = RGMeshStore.LoadMesh(RGMeshStore.mesh_type.mesh_3d, - animations.animationData.RAANItems[0].modelFile, - name_col, - RAHDData.textureId); + if (!FFIModelLoader.TryGetMeshData(animations.animationData.RAANItems[0].modelFile, name_col, out Mesh firstMesh, out List firstMaterials, out int firstFrameCount)) + { + type = ScriptedObjectType.scriptedobject_static; + return; + } meshes = new Mesh[animations.animationData.RAANItems.Count]; meshFrameCount = new int[animations.animationData.RAANItems.Count]; @@ -173,29 +181,26 @@ public void Instanciate3DObject(RGFileImport.RGRGMFile.RGMMPOBItem MPOB, RGFileI for(int j=0;j materials, out _)) + { + skinnedMeshRenderer.sharedMesh = mesh; + skinnedMeshRenderer.SetMaterials(materials); + } } } @@ -206,13 +211,11 @@ public void InstanciateLightObject(RGFileImport.RGRGMFile.RGMMPOBItem MPOB, RGFi skinnedMeshRenderer = gameObject.AddComponent(); string modelname = MPOB.scriptName; - RGMeshStore.UnityData_3D data_3D = RGMeshStore.LoadMesh(RGMeshStore.mesh_type.mesh_3d, - modelname, - name_col, - RAHDData.textureId); - - skinnedMeshRenderer.sharedMesh = data_3D.mesh; - skinnedMeshRenderer.SetMaterials(data_3D.materials); + if (FFIModelLoader.TryGetMeshData(modelname, name_col, out Mesh mesh, out List materials, out _)) + { + skinnedMeshRenderer.sharedMesh = mesh; + skinnedMeshRenderer.SetMaterials(materials); + } AddLight(); light.type = LightType.Point; @@ -247,13 +250,16 @@ public void Instanciate(RGFileImport.RGRGMFile.RGMMPOBItem MPOB, RGFileImport.RG { scriptName = MPOB.scriptName; colName = name_col; - RAHDData = filergm.RAHD.dict[scriptName]; + if (!filergm.RAHD.dict.TryGetValue(scriptName, out RAHDData)) + { + RAHDData = new RGFileImport.RGRGMFile.RGMRAHDItem(); + } Vector3 position = Vector3.zero; position.x = (float)(MPOB.posX)*RGM_MPOB_SCALE; position.y = -(float)(MPOB.posY)*RGM_MPOB_SCALE; position.z = -(float)(0xFFFFFF-MPOB.posZ)*RGM_MPOB_SCALE; - Vector3 rotation = RGRGMStore.eulers_from_MPOB_data(MPOB); + Vector3 rotation = EulerFromMpobData(MPOB); transform.position = position; transform.Rotate(rotation); originalRotation = rotation; @@ -264,6 +270,8 @@ public void Instanciate(RGFileImport.RGRGMFile.RGMMPOBItem MPOB, RGFileImport.RG int RALC_offset = RAHDData.RALCOffset/12; for(int i=0;i= filergm.RALC.items.Count) + break; RGRGMFile.RGMRALCItem RALCData = filergm.RALC.items[RALC_offset+i]; Vector3 loc = position; loc.x += (float)(RALCData.offsetX)*RGM_MPOB_SCALE; @@ -302,10 +310,25 @@ public void Instanciate(RGFileImport.RGRGMFile.RGMMPOBItem MPOB, RGFileImport.RG break; } - script = new ScriptData(MPOB.scriptName, MPOB.id); + + try + { + script = new ScriptData(MPOB.scriptName, MPOB.id); + } + catch + { + script = null; + } attributes = new byte[256]; - Array.Copy(filergm.RAAT.attributes, RAHDData.index*256, attributes, 0, 256); + if (filergm.RAAT.attributes != null) + { + int attrOffset = RAHDData.index * 256; + if (attrOffset >= 0 && attrOffset + 256 <= filergm.RAAT.attributes.Length) + { + Array.Copy(filergm.RAAT.attributes, attrOffset, attributes, 0, 256); + } + } if(skinnedMeshRenderer != null) { @@ -318,12 +341,13 @@ public void Instanciate(RGFileImport.RGRGMFile.RGMMPOBItem MPOB, RGFileImport.RG objectName = null; objectId = MPOB.id; - if(RAHDData.RANMLength > 0) + if(RAHDData.RANMLength > 0 && filergm.RANM.data != null) { - MemoryReader RANMReader = new MemoryReader(filergm.RANM.data); - RANMReader.Seek((uint)RAHDData.RANMOffset, 0); - char[] curc = RANMReader.ReadChars(RAHDData.RANMLength-1); - objectName = new string(curc); + int maxLen = Math.Min(RAHDData.RANMLength, filergm.RANM.data.Length - RAHDData.RANMOffset); + if (RAHDData.RANMOffset >= 0 && maxLen > 1) + { + objectName = new string(filergm.RANM.data, RAHDData.RANMOffset, maxLen - 1); + } } // TODO: when to add audio source? @@ -415,6 +439,15 @@ public int SetAnim(int animId, int firstFrame) Debug.Log($"{scriptName}: tried to set animation but object type is not animated"); return 0; } + + private static Vector3 EulerFromMpobData(RGFileImport.RGRGMFile.RGMMPOBItem item) + { + const float da2dg = 180.0f / 1024.0f; + Vector3 eulers = new Vector3(item.anglex % 2048, item.angley % 2048, item.anglez % 2048); + eulers *= da2dg; + return Vector3.Scale(eulers, new Vector3(1f, 1f, 1f)); + } + int GlobalToLocalFrame(int globalFrame) { int cnt_tot = 0; @@ -488,7 +521,7 @@ void UpdateTasks() return; UpdateTask(mainTask); - if(mainTask.type == TaskType.task_idle) + if(mainTask.type == TaskType.task_idle && script != null) { try { script.tickScript(); @@ -718,6 +751,7 @@ void SetupFunctions() functions[93] = Sound; functions[94] = FlatSound; functions[95] = AmbientSound; + functions[96] = AmbientRtx; functions[99] = WaitOnDialog; functions[100] = HideMe; functions[101] = ShowMe; @@ -925,7 +959,7 @@ public int menuAddItem(uint caller, bool multitask, int[] i /*3*/) // i[1]: 1 grays out the item // i[2]: the item index (? not 100% sequential, might miss some in-between) - string displayText = RGSoundStore.GetRTX(i[0]).subtitle; + string displayText = FFISoundStore.GetRTX(i[0]).subtitle; Game.uiManager.AddDialogueOption(displayText, (i[1] == 1), i[2]); return 0; } @@ -943,21 +977,23 @@ public int RTX(uint caller, bool multitask, int[] i /*1*/) { // Play a sound from RTX and display subtitles // i[0]: index of the RTX data - RGSoundStore.RTXEntry rtx = RGSoundStore.GetRTX(i[0]); + FFISoundStore.RTXEntry rtx = FFISoundStore.GetRTX(i[0]); + float duration = 0f; if(rtx.audio) { audioSource.clip = rtx.audio; audioSource.Play(); + duration = audioSource.clip.length; } TaskData newTask = new TaskData(); newTask.type = TaskType.task_waiting; - newTask.duration = audioSource.clip.length; + newTask.duration = duration; newTask.timer = 0; string displayText = rtx.subtitle; - Game.uiManager.ShowSubtitleText(displayText, audioSource.clip.length); + Game.uiManager.ShowSubtitleText(displayText, duration); AddTask(multitask, newTask); @@ -974,26 +1010,31 @@ public int rtxAnim(uint caller, bool multitask, int[] i /*4*/) // i[2]: 2nd animation to play // i[3]: 3rd animation to play // TODO: this sometimes is a non-existing animation - RGSoundStore.RTXEntry rtx = RGSoundStore.GetRTX(i[0]); + FFISoundStore.RTXEntry rtx = FFISoundStore.GetRTX(i[0]); TaskData newTask = new TaskData(); + float duration = 0f; if(rtx.audio) { audioSource.clip = rtx.audio; audioSource.Play(); - newTask.duration = audioSource.clip.length; + duration = audioSource.clip.length; newTask.dialog = true; } + newTask.duration = duration; string displayText = rtx.subtitle; - Game.uiManager.ShowSubtitleText(displayText, audioSource.clip.length); + Game.uiManager.ShowSubtitleText(displayText, duration); newTask.type = TaskType.task_animating; newTask.timer = 0; newTask.animationFinished = true; newTask.animationQueue = new Queue<(int animId, int startFrame)>(); - animations.shouldExitFcn = newTask.animAlwaysExitFcn; - animations.doExitFcn= newTask.animSetFinishedFcn; + if (animations != null) + { + animations.shouldExitFcn = newTask.animAlwaysExitFcn; + animations.doExitFcn= newTask.animSetFinishedFcn; + } for(int j=1;j<4;j++) { @@ -1344,8 +1385,9 @@ public int Sound(uint caller, bool multitask, int[] i /*3*/) // i[1]: TODO: UNKNOWN, volume? // i[2]: TODO: UNKNOWN, falloff? - audioSource.clip = RGSoundStore.GetSFX(i[0]); - audioSource.Play(); + audioSource.clip = FFISoundStore.GetSFX(i[0]); + if (audioSource.clip != null) + audioSource.Play(); return 0; } /*function 94*/ @@ -1356,8 +1398,9 @@ public int FlatSound(uint caller, bool multitask, int[] i /*3*/) // i[1]: TODO: UNKNOWN, volume? // i[2]: TODO: UNKNOWN, falloff? - audioSource.clip = RGSoundStore.GetSFX(i[0]); - audioSource.Play(); + audioSource.clip = FFISoundStore.GetSFX(i[0]); + if (audioSource.clip != null) + audioSource.Play(); return 0; } @@ -1368,13 +1411,38 @@ public int AmbientSound(uint caller, bool multitask, int[] i /*3*/) // i[0]: sound id // i[1]: TODO: UNKNOWN, volume? // i[2]: TODO: UNKNOWN, falloff? - audioSource.clip = RGSoundStore.GetSFX(i[0]); - audioSource.Play(); + audioSource.clip = FFISoundStore.GetSFX(i[0]); + if (audioSource.clip != null) + audioSource.Play(); + + // wait until playback is done + TaskData newTask = new TaskData(); + newTask.type = TaskType.task_waiting; + newTask.duration = audioSource.clip != null ? audioSource.clip.length : 0f; + newTask.timer = 0; + + AddTask(multitask, newTask); + return 0; + } + /*multitask 96*/ + public int AmbientRtx(uint caller, bool multitask, int[] i /*3*/) + { + // plays RTX audio and displays subtitles as ambient dialogue + // i[0]: index of the RTX data + // i[1]: TODO: UNKNOWN, volume? + // i[2]: TODO: UNKNOWN, falloff? + FFISoundStore.RTXEntry rtx = FFISoundStore.GetRTX(i[0]); + + if (rtx.audio) + { + audioSource.clip = rtx.audio; + audioSource.Play(); + } // wait until playback is done TaskData newTask = new TaskData(); newTask.type = TaskType.task_waiting; - newTask.duration = audioSource.clip.length; + newTask.duration = rtx.audio != null ? rtx.audio.length : 0f; newTask.timer = 0; AddTask(multitask, newTask); @@ -1480,7 +1548,7 @@ public int ACTIVATE(uint caller, bool multitask, int[] i /*1*/) // i[0]: Stringid shown on screen when looking at the item // TODO: we only want a single activatable item to return 1 on this // testable by walking to the cave in the jungle and picking up all the potions in 1 go - string displayText = RGSoundStore.GetRTX(i[0]).subtitle; + string displayText = FFISoundStore.GetRTX(i[0]).subtitle; Game.uiManager.ShowInteractionText(displayText); if(Game.Input.activate) return 1; @@ -1501,8 +1569,11 @@ public int PushAnimation(uint caller, bool multitask, int[] i /*2*/) newTask.animationFinished = true; newTask.animationQueue = new Queue<(int animId, int startFrame)>(); - animations.shouldExitFcn = newTask.animAlwaysExitFcn; - animations.doExitFcn= newTask.animSetFinishedFcn; + if (animations != null) + { + animations.shouldExitFcn = newTask.animAlwaysExitFcn; + animations.doExitFcn= newTask.animSetFinishedFcn; + } (int animId, int startFrame) nextAnim = (i[0], i[1]); newTask.animationQueue.Enqueue(nextAnim); diff --git a/Assets/Scripts/RGFileImport/RGMeshStore.cs b/Assets/Scripts/RGFileImport/RGMeshStore.cs deleted file mode 100644 index 7f7e9854..00000000 --- a/Assets/Scripts/RGFileImport/RGMeshStore.cs +++ /dev/null @@ -1,315 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing.Printing; -using UnityEngine; -using UnityEngine.Rendering; -using Assets.Scripts.RGFileImport.RGGFXImport; -using Unity.Profiling; - -public static class RGMeshStore -{ - -static readonly ProfilerMarker s_load_mesh = new ProfilerMarker("LoadMesh"); -static readonly ProfilerMarker s_load_WLD2mesh = new ProfilerMarker("WLD2Mesh"); -static readonly ProfilerMarker s_load_FLAT2mesh = new ProfilerMarker("FLAT2Mesh"); -static readonly ProfilerMarker s_load_3D2mesh = new ProfilerMarker("f3D2Mesh"); -static readonly ProfilerMarker s_load_3DStore = new ProfilerMarker("LoadMesh_3DStore"); -static readonly ProfilerMarker s_load_WLD = new ProfilerMarker("LoadMesh_WLD"); - -static readonly ProfilerMarker pm_loadmesh_blendshapes = new ProfilerMarker("pm_loadmesh_blendshapes"); -static readonly ProfilerMarker pm_loadmesh_submesh = new ProfilerMarker("pm_loadmesh_submesh"); -static readonly ProfilerMarker pm_loadmesh_submesh_uvs = new ProfilerMarker("pm_loadmesh_submesh_uvs"); -static readonly ProfilerMarker pm_loadmesh_framedata = new ProfilerMarker("pm_loadmesh_framedata"); - - -//using (s_load_iFLAT.Auto()){ - - const int WLD_TEXID_CNT = 64; - public enum mesh_type - { - mesh_3d = 0, - mesh_flat, - } - public struct UnityData_WLD - { - public Mesh mesh; - public List materials; - } - public struct UnityData_3D - { - public Mesh mesh; - public List materials; - public int framecount; - public Vector3[] vertices; - public Vector3[] normals; - } - - static Dictionary Mesh3DDict; // key: - // models: 3D/meshname/colname/texoverride - // flats: FLAT/name/colname/texoverride - - static RGMeshStore() - { - Mesh3DDict = new Dictionary(); - } - public static void ClearCache() - { - Mesh3DDict.Clear(); - } - - public static void DumpDict() - { - Debug.Log($"MESHES:"); - List keys = new List(Mesh3DDict.Keys); - for(int i=0;i materials = new List(); - for(int i=0;i1 range, they should be scaled but differently from the rest? otherwise i end up with a bunch of 0,0 uvs - data.mesh.uv = mesh_i.uv.ToArray(); - - return data; - } - - public static UnityData_3D f3D2Mesh(string meshname, string palettename, int texture_override = 0) - { - using(s_load_3D2mesh.Auto()) - { - RG3DStore.Mesh3D_intermediate mesh_i = RG3DStore.LoadMeshIntermediate3DC(meshname); - return LoadMesh_3DStore(mesh_i, palettename, "DEFAULT", texture_override); - } - } - - public static UnityData_3D f3D2Mesh_3D(string meshname, string palettename, int texture_override = 0) - { - using(s_load_3D2mesh.Auto()) - { - RG3DStore.Mesh3D_intermediate mesh_i = RG3DStore.LoadMeshIntermediate3D(meshname); - return LoadMesh_3DStore(mesh_i, palettename, "DEFAULT", texture_override); - } - } - private static UnityData_3D LoadMesh_3DStore(RG3DStore.Mesh3D_intermediate mesh_i, string palettename, string shadername, int texture_override) - { - List materials = new List(); - Mesh mesh_3d = new Mesh(); - mesh_3d.subMeshCount = mesh_i.subMeshCount; - - Vector3[] baseVerts = mesh_i.vertices.ToArray(); - Vector3[] baseNorms = mesh_i.normals.ToArray(); - Vector3[] rebaseDeltaV = null; - Vector3[] rebaseDeltaN = null; - - // Rebase to frame 1 so default appearance (all weights 0) is not the T-pose - if(mesh_i.framecount > 1) - { - rebaseDeltaV = mesh_i.frameDeltaVertices[1].ToArray(); - rebaseDeltaN = mesh_i.frameDeltaNormals[1].ToArray(); - for(int j = 0; j < baseVerts.Length; j++) - { - baseVerts[j] += rebaseDeltaV[j]; - baseNorms[j] += rebaseDeltaN[j]; - } - } - - mesh_3d.vertices = baseVerts; - mesh_3d.normals = baseNorms; - - for(int j = 0; j < mesh_i.framecount; j++) - { - Vector3[] deltaV = mesh_i.frameDeltaVertices[j].ToArray(); - Vector3[] deltaN = mesh_i.frameDeltaNormals[j].ToArray(); - if(rebaseDeltaV != null) - { - for(int k = 0; k < deltaV.Length; k++) - { - deltaV[k] -= rebaseDeltaV[k]; - deltaN[k] -= rebaseDeltaN[k]; - } - } - mesh_3d.AddBlendShapeFrame($"FRAME_{j}", 100.0f, deltaV, deltaN, null); - } - - List nuvs = new List(mesh_i.uv); - int i = 0; - foreach(var submesh in mesh_i.submeshes) - { - string[] keys = submesh.Key.Split("/"); - int texbsi = Int32.Parse(keys[0]); - int img = Int32.Parse(keys[1]); - if(texture_override != 0 && texbsi >= 0) - texbsi = texture_override; - - Material mat = RGTexStore.GetMaterial(palettename, texbsi, img, shadername); - materials.Add(mat); - - float aspect = (float)mat.mainTexture.height/(float)mat.mainTexture.width; - float h = (float)mat.mainTexture.height; - float w = (float)mat.mainTexture.width; - - List tri_lst = submesh.Value; - for(int j=0;j vec_lst = new List(); - List norm_lst = new List(); - List uv_lst = new List(); - List tri_lst = new List(); - int tri_ofs = 0; - for(int i=0;i dict; + } + + public struct RGMRALCItem + { + public int offsetX; + public int offsetY; + public int offsetZ; + } + + public struct RGMRALCSection + { + public uint num_items; + public List items; + } + + public struct RGMRAANSection + { + public byte[] data; + } + + public struct RGMRAGRSection + { + public byte[] data; + } + + public struct RGMRASTSection + { + public char[] text; + } + + public struct RGMRASBSection + { + public int[] offsets; + } + + public struct RGMRAVASection + { + public int[] data; + } + + public struct RGMRASCSection + { + public byte[] scripts; + } + + public struct RGMRAATSection + { + public byte[] attributes; + } + + public struct RGMRANMSection + { + public char[] data; + } + + public struct RGMRAVCItem + { + public byte offsetX; + public byte offsetY; + public byte offsetZ; + public short vertex; + public int radius; + } + + public struct RGMRAVCSection + { + public uint num_items; + public List items; + } + + public struct RGMRAHKSection + { + public byte[] data; + } + + public struct RGMRAEXItem + { + public short grip0; + public short grip1; + public short scabbard0; + public short scabbard1; + public short unknown0; + public short textureId; + public short vVertex; + public short vSize; + public short tauntId; + public short unknown1; + public short unknown2; + public short unknown3; + public short rangeMin; + public short rangeIdeal; + public short rangeMax; + } + + public struct RGMRAEXSection + { + public uint num_items; + public List items; + } + + public struct RGMMPOBSection + { + public uint num_items; + public List items; + } + + public struct RGMMPSZItem + { + public byte unknown; + public int sizeX; + public int sizeY; + public int sizeZ; + public int posX; + public int posY; + public int posZ; + public int pSizeX; + public int pSizeY; + public int pSizeZ; + public int mSizeX; + public int mSizeY; + public int mSizeZ; + } + + public struct RGMMPSZSection + { + public uint num_items; + public List items; + } + + public RGMRAHDSection RAHD; + public RGMRASTSection RAST; + public RGMRASBSection RASB; + public RGMRAVASection RAVA; + public RGMRASCSection RASC; + public RGMRAHKSection RAHK; + public RGMRALCSection RALC; + public RGMRAEXSection RAEX; + public RGMRAATSection RAAT; + public RGMRAANSection RAAN; + public RGMRAGRSection RAGR; + public RGMRANMSection RANM; + public RGMRAVCSection RAVC; + public RGMMPOBSection MPOB; + public RGMMPSZSection MPSZ; + public long fileSize; + + public RGRGMFile() + { + RAHD.dict = new Dictionary(); + RAAT.attributes = new byte[0]; + RAST.text = new char[0]; + RASB.offsets = new int[0]; + RAVA.data = new int[0]; + RASC.scripts = new byte[0]; + RAHK.data = new byte[0]; + RAEX.items = new List(); + RAAN.data = new byte[0]; + RAGR.data = new byte[0]; + RANM.data = new char[0]; + RAVC.items = new List(); + RALC.items = new List(); + MPOB.items = new List(); + MPSZ.items = new List(); + } + } +} diff --git a/Assets/Scripts/RGFileImport/RGRGMFileTypes.cs.meta b/Assets/Scripts/RGFileImport/RGRGMFileTypes.cs.meta new file mode 100644 index 00000000..cb13f268 --- /dev/null +++ b/Assets/Scripts/RGFileImport/RGRGMFileTypes.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7e58cd1acdefe4c4ca8f1a8042a73404 \ No newline at end of file diff --git a/Assets/Scripts/RGFileImport/RGRGMStore.cs b/Assets/Scripts/RGFileImport/RGRGMStore.cs deleted file mode 100644 index 7ede8cfe..00000000 --- a/Assets/Scripts/RGFileImport/RGRGMStore.cs +++ /dev/null @@ -1,320 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; -using UnityEngine.Rendering; -using Assets.Scripts.RGFileImport.RGGFXImport; -using Unity.Profiling; - -public static class RGRGMStore -{ - -static readonly ProfilerMarker s_load_RGM = new ProfilerMarker("GetRGM"); -static readonly ProfilerMarker s_load_MPSF = new ProfilerMarker("LoadMPSF"); -static readonly ProfilerMarker s_load_MPSO = new ProfilerMarker("LoadMPSO"); -static readonly ProfilerMarker s_load_MPRP = new ProfilerMarker("LoadMPRP"); -static readonly ProfilerMarker s_load_MPOB = new ProfilerMarker("LoadMPOB"); -static readonly ProfilerMarker s_load_WDNM = new ProfilerMarker("LoadWDNM"); - - - - //const float RGM_MPOB_SCALE = 0.000195f; - const float RGM_MPOB_SCALE = 1/5120.0f; - const float RGM_X_OFS = 0.0f;//29.8f; - const float RGM_Y_OFS = 0.0f; //-22.8f; - const float RGM_Z_OFS = 0.0f; - - static Dictionary RGMDict; - - static RGRGMStore() - { - RGMDict = new Dictionary(); - } - public static void DumpDict() - { - Debug.Log($"RGMS:"); - List keys = new List(RGMDict.Keys); - for(int i=0;i LoadMPSF(string filename) - { -using(s_load_MPSF.Auto()){ - RGFileImport.RGRGMFile filergm = GetRGM(filename); - - List data_out = new List(); - for(int i=0;i LoadMPSO(string filename) - { -using(s_load_MPSO.Auto()){ - RGFileImport.RGRGMFile filergm = GetRGM(filename); - - List data_out = new List(); - for(int i=0;i LoadMPRP(string filename) - { -using(s_load_MPRP.Auto()){ - RGFileImport.RGRGMFile filergm = GetRGM(filename); - - List data_out = new List(); - for(int i=0;i0?filergm.MPRP.items[i].staticModel:null; - data_out.Add(new RGRGMRopeData(id, filergm.MPRP.items[i].ropeModel,staticModel, new Vector3(posx, posy, posz), filergm.MPRP.items[i].length)); - } - catch(Exception ex) - { - Debug.Log($"Error loading MPRP item from {filename}: {filergm.MPRP.items[i].ropeModel}: {ex}"); - } - } - return data_out; -} - } - - public static List LoadMPOB(string filename) - { -using(s_load_MPOB.Auto()){ - RGFileImport.RGRGMFile filergm = GetRGM(filename); - - List data_out = new List(); - for(int i=0;i LoadWDNM(string filename) - { -using(s_load_WDNM.Auto()){ - RGFileImport.RGRGMFile filergm = GetRGM(filename); - - List data_out = new List(); - uint ID_GEN = 0xFFFF0000; - for(int i=0;i LoadMPMK(string filename) - { - RGFileImport.RGRGMFile filergm = GetRGM(filename); - - List data_out = new List(); - for(int i=0;i SFXList; - public static Dictionary RTXDict; - - static RGSoundStore() - { - SFXList = new List(); - RTXDict = new Dictionary(); - } - public static RTXEntry GetRTX(int id) - { - // we should have already loaded RTX here - return RTXDict[id]; - } - public static AudioClip GetSFX(int id) - { - // we should have already loaded SFX here - return SFXList[id]; - } - - - public static void LoadRTX(string rtxname) - { - try - { - string path = new string(Game.pathManager.GetRootFolder() + rtxname + ".RTX"); - RGRTXFile rtxFile = new RGRTXFile(); - rtxFile.LoadFile(path); - - foreach(KeyValuePair entry in rtxFile.rtxItemDict) - { - - AudioClip audio = null; - if(entry.Value.hasAudioData()) - { - audio = SFXToAudio(entry.Value.audioData, entry.Key); - } - RTXEntry newRTX = new RTXEntry(entry.Value.subtitle, audio); - RTXDict.Add(entry.Key, newRTX); - } - } - catch(Exception ex) - { - Debug.Log($"Failed to read RTX file with error {ex.Message}"); - } - } - - public static void LoadSFX(string sfxname) - { - try - { - string path = new string(Game.pathManager.GetSoundFolder() + sfxname + ".SFX"); - RGSFXFile sfxFile = new RGSFXFile(); - sfxFile.LoadFile(path); - - for(int i=0;i MaterialDict; - static Dictionary PaletteDict; - static Dictionary BSIFDict; - static Dictionary ShaderDict; - - // keys like this: - // FILENAME/IMG# - // uppercase filename without extention; eg MIRROR0 - public static Dictionary GXADict; - - static RGTexStore() - { - MaterialDict = new Dictionary(); - PaletteDict = new Dictionary(); - BSIFDict = new Dictionary(); - ShaderDict = new Dictionary(); - - GXADict = new Dictionary(); - - ShaderDict.Add("DEFAULT", Shader.Find("Universal Render Pipeline/Simple Lit")); - ShaderDict.Add("TERRAIN", Shader.Find("Universal Render Pipeline/Simple Lit")); - ShaderDict.Add("FLATS", Shader.Find("Universal Render Pipeline/Unlit")); - - } - - public static void ClearCache() - { - MaterialDict.Clear(); - } - - public static void DumpDict() - { - Debug.Log($"MATS:"); - List keys = new List(MaterialDict.Keys); - for(int i=0;i(PaletteDict.Keys); - for(int i=0;i(BSIFDict.Keys); - for(int i=0;i(ShaderDict.Keys); - for(int i=0;i= 0) - { - RGTEXBSIFile bsif = LoadTEXBSI(texbsi); - RGCOLFile palette = LoadPalette(palname); - - List[] tex_lst_sorted = new List[bsif.images.Count]; - for(int i =0;i cur_tex = GraphicsConverter.RGBSIToTexture2D(bsif.images[i].imageData, palette); - - MaterialDict.Add(new_mat_key, new Material(ShaderDict[shadername])); - MaterialDict[new_mat_key].mainTexture = cur_tex[0]; - - // note that FRAME_0 does not exist, its called _MainTex or smt - for(int j=0;j cur_tex = GraphicsConverter.RGBSIToTexture2D(bsif, palette); - - MaterialDict.Add(mat_key, new Material(Shader.Find("Legacy Shaders/Diffuse Fast"))); - MaterialDict[mat_key].mainTexture = cur_tex[0]; - - // note that FRAME_0 does not exist, its called _MainTex or smt - for(int j=0;j cur_tex = GraphicsConverter.RGGXAToTexture2D(gxa); - - - for(int j=0;j