├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── core-cd.yaml │ ├── interface-cd.yaml │ └── interface-ci.yaml ├── .gitignore ├── Core ├── Conversion │ ├── FormatConversion.cs │ ├── FormatConversionException.cs │ ├── FormatConverter.cs │ ├── FormatConverters.cs │ └── Formats │ │ ├── AnimatedTextureConverter.cs │ │ ├── ArtObjectConverter.cs │ │ ├── EffectConverter.cs │ │ ├── LevelConverter.cs │ │ ├── MapTreeConverter.cs │ │ ├── NpcMetadataConverter.cs │ │ ├── SkyConverter.cs │ │ ├── SoundEffectConverter.cs │ │ ├── SpriteFontConverter.cs │ │ ├── TextStorageConverter.cs │ │ ├── TextureConverter.cs │ │ ├── TrackedSongConverter.cs │ │ └── TrileSetConverter.cs ├── Definitions │ ├── Game │ │ ├── ArtObject │ │ │ ├── ArtObject.cs │ │ │ └── VertexInstance.cs │ │ ├── Common │ │ │ ├── ActorType.cs │ │ │ ├── FaceOrientation.cs │ │ │ ├── LevelNodeType.cs │ │ │ └── NpcAction.cs │ │ ├── Graphics │ │ │ ├── AnimatedTexture.cs │ │ │ ├── FrameContent.cs │ │ │ └── IndexedPrimitives.cs │ │ ├── Level │ │ │ ├── AmbienceTrack.cs │ │ │ ├── ArtObjectActorSettings.cs │ │ │ ├── ArtObjectInstance.cs │ │ │ ├── BackgroundPlane.cs │ │ │ ├── CameraNodeData.cs │ │ │ ├── CodeInput.cs │ │ │ ├── DotDialogueLine.cs │ │ │ ├── Level.cs │ │ │ ├── LiquidType.cs │ │ │ ├── MovementPath.cs │ │ │ ├── NpcActionContent.cs │ │ │ ├── NpcInstance.cs │ │ │ ├── PathEndBehabiour.cs │ │ │ ├── PathSegment.cs │ │ │ ├── Scripting │ │ │ │ ├── ComparisonOperator.cs │ │ │ │ ├── Entity.cs │ │ │ │ ├── Script.cs │ │ │ │ ├── ScriptAction.cs │ │ │ │ ├── ScriptCondition.cs │ │ │ │ └── ScriptTrigger.cs │ │ │ ├── SpeechLine.cs │ │ │ ├── TrileEmplacement.cs │ │ │ ├── TrileFace.cs │ │ │ ├── TrileGroup.cs │ │ │ ├── TrileInstance.cs │ │ │ ├── TrileInstanceActorSettings.cs │ │ │ ├── VibrationMotor.cs │ │ │ ├── Viewpoint.cs │ │ │ ├── Volume.cs │ │ │ └── VolumeActorSettings.cs │ │ ├── MapTree │ │ │ ├── MapNode.cs │ │ │ ├── MapNodeConnection.cs │ │ │ ├── MapTree.cs │ │ │ └── WinConditions.cs │ │ ├── NpcMetadata │ │ │ └── NpcMetadata.cs │ │ ├── Sky │ │ │ ├── Sky.cs │ │ │ └── SkyLayer.cs │ │ ├── TrackedSong │ │ │ ├── AssembleChords.cs │ │ │ ├── Loop.cs │ │ │ ├── ShardNotes.cs │ │ │ └── TrackedSong.cs │ │ ├── TrileSet │ │ │ ├── CollisionType.cs │ │ │ ├── SurfaceType.cs │ │ │ ├── Trile.cs │ │ │ └── TrileSet.cs │ │ ├── XNA │ │ │ ├── Color.cs │ │ │ ├── Effect.cs │ │ │ ├── Matrix.cs │ │ │ ├── PrimitiveType.cs │ │ │ ├── Quaternion.cs │ │ │ ├── Rectangle.cs │ │ │ ├── SoundEffect.cs │ │ │ ├── SpriteFont.cs │ │ │ ├── SurfaceFormat.cs │ │ │ ├── Texture2D.cs │ │ │ ├── Vector2.cs │ │ │ ├── Vector3.cs │ │ │ └── Vector4.cs │ │ ├── XnbPropertyAttribute.cs │ │ ├── XnbReaderTypeAttribute.cs │ │ └── XnbTypeAttribute.cs │ └── Json │ │ ├── JsonModel.cs │ │ ├── LevelJsonModel.cs │ │ ├── MapNodeConnectionJsonModel.cs │ │ ├── MapNodeJsonModel.cs │ │ ├── MapTreeJsonModel.cs │ │ ├── SpriteFontPropertiesJsonModel.cs │ │ ├── TrileGroupJsonModel.cs │ │ └── TrileInstanceJsonModel.cs ├── FEZRepacker.Core.csproj ├── FileSystem │ ├── FileBundle.cs │ ├── PakFileRecord.cs │ ├── PakPackage.cs │ ├── PakReader.cs │ └── PakWriter.cs ├── Helpers │ ├── BinaryStreamExtensions.cs │ ├── DxtUtil.cs │ ├── ImageSharpUtils.cs │ ├── Json │ │ ├── ConfiguredJsonSerializer.cs │ │ └── CustomConverters │ │ │ ├── ColorJsonConverter.cs │ │ │ ├── QuaternionJsonConverter.cs │ │ │ ├── ScriptPropertiesJsonConverters.cs │ │ │ ├── TimeSpanJsonConverter.cs │ │ │ ├── TrileEmplacementJsonConverter.cs │ │ │ ├── TrileEmplacementListJsonConverter.cs │ │ │ └── VectorJsonConverters.cs │ ├── LzxDecoder.cs │ ├── TexturesUtil.cs │ ├── TrixelArtUtil.cs │ └── WavefrontObjUtil.cs └── XNB │ ├── ContentSerialization │ ├── GenericContentSerializer.cs │ ├── System │ │ ├── ArrayContentSerializer.cs │ │ ├── BooleanContentSerializer.cs │ │ ├── ByteArrayContentSerializer.cs │ │ ├── CharContentSerializer.cs │ │ ├── DictionaryContentSerializer.cs │ │ ├── EnumContentSerializer.cs │ │ ├── Int32ContentSerializer.cs │ │ ├── ListContentSerializer.cs │ │ ├── StringContentSerializer.cs │ │ ├── TimeSpanContentSerializer.cs │ │ └── UInt16ContentSerializer.cs │ └── XnbContentSerializer.cs │ ├── ContentTypes │ ├── AnimatedTextureContentIdentity.cs │ ├── ArtObjectContentIdentity.cs │ ├── EffectContentIdentity.cs │ ├── LevelContentIdentity.cs │ ├── MapContentIdentity.cs │ ├── NpcMetadataContentIdentity.cs │ ├── SkyContentIdentity.cs │ ├── SoundEffectContentIdentity.cs │ ├── SpriteFontContentIdentity.cs │ ├── TextStorageContentIdentity.cs │ ├── TextureContentIdentity.cs │ ├── TrackedSongContentIdentity.cs │ └── TrileSetContentIdentity.cs │ ├── XnbAssemblyQualifier.cs │ ├── XnbCompressor.cs │ ├── XnbContentReader.cs │ ├── XnbContentWriter.cs │ ├── XnbHeader.cs │ ├── XnbPrimaryContentIdentity.cs │ ├── XnbPrimaryContents.cs │ ├── XnbSerializationException.cs │ └── XnbSerializer.cs ├── FEZRepacker.sln ├── Interface ├── Actions │ ├── ConvertFromXnbAction.cs │ ├── ConvertToXnbAction.cs │ ├── HelpAction.cs │ ├── ListPackageContentAction.cs │ ├── PackAction.cs │ ├── UnpackAction.cs │ ├── UnpackConvertAction.cs │ ├── UnpackDecompressedAction.cs │ ├── UnpackGameAction.cs │ └── UnpackRawAction.cs ├── CommandLineAction.cs ├── CommandLineArgument.cs ├── CommandLineInterface.cs ├── FEZRepacker.Interface.csproj └── Program.cs ├── README.md └── Tests ├── .runsettings ├── FEZRepacker.Tests.csproj ├── TestPacking.cs └── TestUtils.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/core-cd.yaml: -------------------------------------------------------------------------------- 1 | name: Core CD 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | release-nuget: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: actions/setup-dotnet@v4 14 | with: 15 | dotnet-version: '9.x' 16 | 17 | - name: Fetch version 18 | run: echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV 19 | 20 | - name: Restore packages 21 | run: dotnet restore Core/FEZRepacker.Core.csproj 22 | 23 | - name: Build 24 | run: dotnet build Core/FEZRepacker.Core.csproj -c Release /p:Version=${VERSION} 25 | 26 | - name: Pack 27 | run: dotnet pack Core/FEZRepacker.Core.csproj -c Release /p:Version=${VERSION} --no-build --output . 28 | 29 | - name: Push 30 | run: dotnet nuget push FEZRepacker.Core.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} 31 | -------------------------------------------------------------------------------- /.github/workflows/interface-cd.yaml: -------------------------------------------------------------------------------- 1 | name: Interface CD 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | build-interface: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: actions/setup-dotnet@v4 14 | with: 15 | dotnet-version: '9.x' 16 | 17 | - name: Restore packages 18 | run: dotnet restore Interface/FEZRepacker.Interface.csproj 19 | 20 | - name: Build for Windows 21 | run: dotnet publish Interface/FEZRepacker.Interface.csproj -c Release -r win-x86 --self-contained 22 | 23 | - name: Build for Linux 24 | run: dotnet publish Interface/FEZRepacker.Interface.csproj -c Release -r linux-arm64 --self-contained 25 | 26 | - name: Upload Windows artifact 27 | uses: actions/upload-artifact@v4 28 | with: 29 | name: FEZRepacker.Interface.Windows 30 | path: Interface/bin/Release/net6.0/win-x86/publish/FEZRepacker.Interface.exe 31 | if-no-files-found: error 32 | 33 | - name: Upload Linux artifact 34 | uses: actions/upload-artifact@v4 35 | with: 36 | name: FEZRepacker.Interface.Linux 37 | path: Interface/bin/Release/net6.0/linux-arm64/publish/FEZRepacker.Interface 38 | if-no-files-found: error 39 | release: 40 | if: github.repository == 'FEZModding/FEZRepacker' 41 | needs: [build-interface] 42 | runs-on: ubuntu-latest 43 | steps: 44 | 45 | - name: Download Linux Build 46 | uses: actions/download-artifact@v4 47 | with: 48 | name: FEZRepacker.Interface.Linux 49 | - name: Rename Linux Build 50 | run: mv FEZRepacker.Interface FEZRepacker 51 | 52 | - name: Download Windows Build 53 | uses: actions/download-artifact@v4 54 | with: 55 | name: FEZRepacker.Interface.Windows 56 | - name: Rename Windows Build 57 | run: mv FEZRepacker.Interface.exe FEZRepacker.exe 58 | 59 | - name: Create Release 60 | uses: softprops/action-gh-release@v1 61 | with: 62 | body: | 63 | ## Usage 64 | 65 | Download one of the following: 66 | 67 | - `FEZRepacker.exe` - Windows standalone command-line interface 68 | - `FEZRepacker` - Linux standalone command-line interface 69 | 70 | ...and consult the README.md for usage details. 71 | 72 | Additionally, Core library can be accessed as a NuGet package: https://www.nuget.org/packages/FEZRepacker.Core 73 | 74 | ## Changelog 75 | 76 | TODO 77 | files: | 78 | FEZRepacker.exe 79 | FEZRepacker 80 | fail_on_unmatched_files: true 81 | -------------------------------------------------------------------------------- /.github/workflows/interface-ci.yaml: -------------------------------------------------------------------------------- 1 | name: Interface CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | tags-ignore: 8 | - '**' 9 | paths-ignore: 10 | - '.github/*' 11 | - '.github/workflows/**.yml' 12 | - '.gitattributes' 13 | - '.gitignore' 14 | - 'docs/**' 15 | - '**.md' 16 | - 'LICENSE' 17 | 18 | jobs: 19 | build: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: actions/setup-dotnet@v4 24 | with: 25 | dotnet-version: '9.x' 26 | 27 | - name: Restore packages 28 | run: dotnet restore Interface/FEZRepacker.Interface.csproj 29 | 30 | - name: Build for Windows 31 | run: dotnet publish Interface/FEZRepacker.Interface.csproj -c Release -r win-x86 --self-contained 32 | 33 | - name: Build for Linux 34 | run: dotnet publish Interface/FEZRepacker.Interface.csproj -c Release -r linux-arm64 --self-contained 35 | 36 | - name: Upload Windows artifact 37 | uses: actions/upload-artifact@v4 38 | with: 39 | name: FEZRepacker.Interface.Windows 40 | path: | 41 | Interface/bin/Release/net6.0/win-x86/publish/FEZRepacker.Interface.exe 42 | Interface/bin/Release/net6.0/win-x86/publish/FEZRepacker.Interface.pdb 43 | Interface/bin/Release/net6.0/win-x86/publish/FEZRepacker.Core.pdb 44 | if-no-files-found: error 45 | 46 | - name: Upload Linux artifact 47 | uses: actions/upload-artifact@v4 48 | with: 49 | name: FEZRepacker.Interface.Linux 50 | path: | 51 | Interface/bin/Release/net6.0/linux-arm64/publish/FEZRepacker.Interface 52 | Interface/bin/Release/net6.0/linux-arm64/publish/FEZRepacker.Interface.pdb 53 | Interface/bin/Release/net6.0/linux-arm64/publish/FEZRepacker.Core.pdb 54 | if-no-files-found: error 55 | -------------------------------------------------------------------------------- /Core/Conversion/FormatConversion.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.FileSystem; 2 | 3 | namespace FEZRepacker.Core.Conversion 4 | { 5 | /// 6 | /// Main format conversion handling logic. Contains methods for converting 7 | /// and deconverting objects to and from easily readable file formats. 8 | /// 9 | /// 10 | /// A specific to to given object type is used 11 | /// in order to convert and deconvert it. A list of format converters defining 12 | /// supported object types is stored in . 13 | /// 14 | public static class FormatConversion 15 | { 16 | /// 17 | /// Finds converter for a type of given object, then attempts to 18 | /// convert it and store in a . 19 | /// 20 | /// A reference to object to convert 21 | /// 22 | /// A containing file or files converted from given object. 23 | /// 24 | /// 25 | /// Thrown when null object was given 26 | /// 27 | /// 28 | /// Thrown when a type of given object is not supported by Repacker 29 | /// 30 | public static FileBundle Convert(object? data) 31 | { 32 | if(data == null) 33 | { 34 | throw new NullReferenceException(nameof(data)); 35 | } 36 | 37 | var converter = FormatConverters.FindForType(data.GetType()); 38 | 39 | if(converter == null) 40 | { 41 | throw new FormatConversionException($"Type {data.GetType()} is not supported for conversion."); 42 | } 43 | 44 | return converter.Convert(data); 45 | } 46 | 47 | /// 48 | /// Finds converter for main extension of given , 49 | /// then attempts to deconvert it back to an object with a type assigned to this converter. 50 | /// 51 | /// A containing files to convert. 52 | /// 53 | /// An object deconverted from files contained in given . 54 | /// 55 | /// 56 | /// Thrown when main extension of given is not supported by Repacker 57 | /// 58 | public static object? Deconvert(FileBundle bundle) 59 | { 60 | var converter = FormatConverters.FindForFileBundle(bundle); 61 | 62 | if (converter == null) 63 | { 64 | throw new FormatConversionException($"File bundle type {bundle.MainExtension} is not supported for conversion."); 65 | } 66 | 67 | return converter.Deconvert(bundle); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Core/Conversion/FormatConversionException.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Conversion 2 | { 3 | [Serializable] 4 | public class FormatConversionException : Exception 5 | { 6 | internal string? _message; 7 | public override string Message 8 | { 9 | get => _message ?? base.Message; 10 | } 11 | 12 | public FormatConversionException(string message) : base(message) 13 | { 14 | _message = message; 15 | } 16 | 17 | public FormatConversionException(string message, Exception innerException) : base(message, innerException) 18 | { 19 | _message = message; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Core/Conversion/FormatConverter.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.FileSystem; 2 | 3 | namespace FEZRepacker.Core.Conversion 4 | { 5 | /// 6 | /// Defines methods of convertion between specific object types 7 | /// and a , along with information about 8 | /// object types and file extension supported by this converter. 9 | /// 10 | public abstract class FormatConverter 11 | { 12 | public abstract string FileFormat { get; } 13 | public abstract Type FormatType { get; } 14 | 15 | public abstract FileBundle Convert(object? data); 16 | public abstract object? Deconvert(FileBundle bundle); 17 | 18 | public FileBundle Convert(T data) 19 | { 20 | return Convert((object?)data); 21 | } 22 | public T? Deconvert(FileBundle bundle) 23 | { 24 | return (T?)Deconvert(bundle); 25 | } 26 | } 27 | 28 | /// 29 | /// Generic type which automatically supplies format type property of 30 | /// 31 | /// A type to support for this converter 32 | public abstract class FormatConverter : FormatConverter 33 | { 34 | public override Type FormatType => typeof(ConvertedType); 35 | public abstract FileBundle ConvertTyped(ConvertedType data); 36 | public abstract ConvertedType DeconvertTyped(FileBundle bundle); 37 | 38 | public override FileBundle Convert(object? data) 39 | { 40 | return ConvertTyped((ConvertedType?)data!); 41 | } 42 | 43 | public override object? Deconvert(FileBundle bundle) 44 | { 45 | return DeconvertTyped(bundle); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Core/Conversion/FormatConverters.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Conversion.Formats; 2 | using FEZRepacker.Core.FileSystem; 3 | 4 | namespace FEZRepacker.Core.Conversion 5 | { 6 | /// 7 | /// Contains a statically declared list of all 8 | /// structures needed to convert all primary content types of FEZ into readable formats. 9 | /// 10 | public static class FormatConverters 11 | { 12 | public static readonly List List = new() 13 | { 14 | new AnimatedTextureConverter(), 15 | new ArtObjectConverter(), 16 | new EffectConverter(), 17 | new LevelConverter(), 18 | new MapTreeConverter(), 19 | new NpcMetadataConverter(), 20 | new SkyConverter(), 21 | new SoundEffectConverter(), 22 | new SpriteFontConverter(), 23 | new TextStorageConverter(), 24 | new TextureConverter(), 25 | new TrackedSongConverter(), 26 | new TrileSetConverter() 27 | }; 28 | 29 | public static FormatConverter? FindByExtension(string extension) 30 | { 31 | return List.FirstOrDefault(x => x.FileFormat == extension); 32 | } 33 | 34 | public static FormatConverter? FindForFileBundle(FileBundle bundle) 35 | { 36 | return FindByExtension(bundle.MainExtension); 37 | } 38 | 39 | public static FormatConverter? FindForType(Type type) 40 | { 41 | return List.FirstOrDefault(x => x.FormatType == type); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Core/Conversion/Formats/ArtObjectConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using FEZRepacker.Core.Definitions.Game.ArtObject; 4 | using FEZRepacker.Core.Definitions.Game.XNA; 5 | using FEZRepacker.Core.FileSystem; 6 | using FEZRepacker.Core.Helpers; 7 | using FEZRepacker.Core.Helpers.Json; 8 | 9 | using SixLabors.ImageSharp.Formats.Png; 10 | 11 | namespace FEZRepacker.Core.Conversion.Formats 12 | { 13 | internal class ArtObjectConverter : FormatConverter 14 | { 15 | public override string FileFormat => ".fezao"; 16 | 17 | public override FileBundle ConvertTyped(ArtObject data) 18 | { 19 | var bundle = ConfiguredJsonSerializer.SerializeToFileBundle(FileFormat, data); 20 | 21 | bundle.AddFile(GetTextureStream(data, TexturesUtil.CubemapPart.Albedo), ".png"); 22 | bundle.AddFile(GetTextureStream(data, TexturesUtil.CubemapPart.Emission), ".apng"); 23 | bundle.AddFile(GetModelStream(data), ".obj"); 24 | 25 | return bundle; 26 | } 27 | 28 | public override ArtObject DeconvertTyped(FileBundle bundle) 29 | { 30 | var artObject = ConfiguredJsonSerializer.DeserializeFromFileBundle(bundle); 31 | 32 | AppendGeometryStream(ref artObject, bundle.RequireData(".obj")); 33 | LoadCubemap(ref artObject, bundle.GetData(".png"), bundle.GetData(".apng")); 34 | 35 | 36 | return artObject; 37 | } 38 | 39 | private static Stream GetTextureStream(ArtObject data, TexturesUtil.CubemapPart part) 40 | { 41 | using var texture = TexturesUtil.ExtractCubemapPartFromTexture(data.Cubemap, part); 42 | return texture.SaveAsMemoryStream(new PngEncoder()); 43 | } 44 | 45 | private static Stream GetModelStream(ArtObject data) 46 | { 47 | return new MemoryStream(Encoding.UTF8.GetBytes(data.Geometry.ToWavefrontObj())); 48 | } 49 | 50 | private static void AppendGeometryStream(ref ArtObject data, Stream geometryStream) 51 | { 52 | var geometries = TrixelArtUtil.LoadGeometry(geometryStream); 53 | if (geometries.Count() > 0) 54 | { 55 | data.Geometry = geometries.First().Value; 56 | TrixelArtUtil.RecalculateCubemapTexCoords(data.Geometry, data.Size); 57 | } 58 | } 59 | 60 | private static void LoadCubemap(ref ArtObject data, Stream albedoStream, Stream emissionStream) 61 | { 62 | using var image = TexturesUtil.ConstructCubemap(albedoStream, emissionStream); 63 | data.Cubemap = TexturesUtil.ImageToTexture2D(image); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Core/Conversion/Formats/EffectConverter.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.XNA; 2 | using FEZRepacker.Core.FileSystem; 3 | 4 | namespace FEZRepacker.Core.Conversion.Formats 5 | { 6 | internal class EffectConverter : FormatConverter 7 | { 8 | public override string FileFormat => ".fxb"; 9 | 10 | public override FileBundle ConvertTyped(Effect data) 11 | { 12 | var outStream = new MemoryStream(); 13 | var outWriter = new BinaryWriter(outStream); 14 | 15 | outWriter.Write(data.Data.Length); 16 | outWriter.Write(data.Data); 17 | 18 | outStream.Seek(0, SeekOrigin.Begin); 19 | return FileBundle.Single(outStream, FileFormat); 20 | } 21 | 22 | public override Effect DeconvertTyped(FileBundle bundle) 23 | { 24 | var inReader = new BinaryReader(bundle.RequireData("")); 25 | return new Effect() 26 | { 27 | Data = inReader.ReadBytes(inReader.ReadInt32()) 28 | }; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Core/Conversion/Formats/LevelConverter.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.Level; 2 | using FEZRepacker.Core.Definitions.Json; 3 | using FEZRepacker.Core.FileSystem; 4 | using FEZRepacker.Core.Helpers.Json; 5 | 6 | namespace FEZRepacker.Core.Conversion.Formats 7 | { 8 | internal class LevelConverter : FormatConverter 9 | { 10 | public override string FileFormat => ".fezlvl"; 11 | 12 | public override FileBundle ConvertTyped(Level data) 13 | { 14 | var levelModel = new LevelJsonModel(data); 15 | return ConfiguredJsonSerializer.SerializeToFileBundle(FileFormat, levelModel); 16 | } 17 | 18 | public override Level DeconvertTyped(FileBundle bundle) 19 | { 20 | var levelModel = ConfiguredJsonSerializer.DeserializeFromFileBundle(bundle); 21 | return levelModel.Deserialize(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Core/Conversion/Formats/MapTreeConverter.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.MapTree; 2 | using FEZRepacker.Core.Definitions.Json; 3 | using FEZRepacker.Core.FileSystem; 4 | using FEZRepacker.Core.Helpers.Json; 5 | 6 | namespace FEZRepacker.Core.Conversion.Formats 7 | { 8 | internal class MapTreeConverter : FormatConverter 9 | { 10 | public override string FileFormat => ".fezmap"; 11 | 12 | public override FileBundle ConvertTyped(MapTree data) 13 | { 14 | var mapModel = new MapTreeJsonModel(); 15 | mapModel.SerializeFrom(data); 16 | return ConfiguredJsonSerializer.SerializeToFileBundle(FileFormat, mapModel); 17 | } 18 | 19 | public override MapTree DeconvertTyped(FileBundle bundle) 20 | { 21 | var mapModel = ConfiguredJsonSerializer.DeserializeFromFileBundle(bundle); 22 | return mapModel.Deserialize(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Core/Conversion/Formats/NpcMetadataConverter.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.NpcMetadata; 2 | using FEZRepacker.Core.FileSystem; 3 | using FEZRepacker.Core.Helpers.Json; 4 | 5 | namespace FEZRepacker.Core.Conversion.Formats 6 | { 7 | internal class NpcMetadataConverter : FormatConverter 8 | { 9 | public override string FileFormat => ".feznpc"; 10 | 11 | public override FileBundle ConvertTyped(NpcMetadata data) 12 | { 13 | return ConfiguredJsonSerializer.SerializeToFileBundle(FileFormat, data); 14 | } 15 | 16 | public override NpcMetadata DeconvertTyped(FileBundle bundle) 17 | { 18 | return ConfiguredJsonSerializer.DeserializeFromFileBundle(bundle); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Core/Conversion/Formats/SkyConverter.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.Sky; 2 | using FEZRepacker.Core.FileSystem; 3 | using FEZRepacker.Core.Helpers.Json; 4 | 5 | namespace FEZRepacker.Core.Conversion.Formats 6 | { 7 | internal class SkyConverter : FormatConverter 8 | { 9 | public override string FileFormat => ".fezsky"; 10 | 11 | public override FileBundle ConvertTyped(Sky data) 12 | { 13 | return ConfiguredJsonSerializer.SerializeToFileBundle(FileFormat, data); 14 | } 15 | 16 | public override Sky DeconvertTyped(FileBundle bundle) 17 | { 18 | return ConfiguredJsonSerializer.DeserializeFromFileBundle(bundle); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Core/Conversion/Formats/SpriteFontConverter.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.XNA; 2 | using FEZRepacker.Core.Definitions.Json; 3 | using FEZRepacker.Core.FileSystem; 4 | using FEZRepacker.Core.Helpers; 5 | using FEZRepacker.Core.Helpers.Json; 6 | 7 | using SixLabors.ImageSharp; 8 | using SixLabors.ImageSharp.Formats.Png; 9 | using SixLabors.ImageSharp.PixelFormats; 10 | 11 | namespace FEZRepacker.Core.Conversion.Formats 12 | { 13 | internal class SpriteFontConverter : FormatConverter 14 | { 15 | public override string FileFormat => ".fezfont"; 16 | 17 | public override FileBundle ConvertTyped(SpriteFont data) 18 | { 19 | var spriteFontModel = new SpriteFontPropertiesJsonModel(); 20 | spriteFontModel.SerializeFrom(data); 21 | var bundle = ConfiguredJsonSerializer.SerializeToFileBundle(FileFormat, spriteFontModel); 22 | 23 | using var fontAtlas = TexturesUtil.ImageFromTexture2D(data.Texture); 24 | bundle.AddFile(fontAtlas.SaveAsMemoryStream(new PngEncoder()), ".png"); 25 | 26 | return bundle; 27 | } 28 | 29 | public override SpriteFont DeconvertTyped(FileBundle bundle) 30 | { 31 | var spriteFontModel = ConfiguredJsonSerializer.DeserializeFromFileBundle(bundle); 32 | var spriteFont = spriteFontModel.Deserialize(); 33 | 34 | using var importedImage = Image.Load(bundle.RequireData(".png")); 35 | spriteFont.Texture = TexturesUtil.ImageToTexture2D(importedImage); 36 | 37 | return spriteFont; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Core/Conversion/Formats/TextStorageConverter.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.FileSystem; 2 | using FEZRepacker.Core.Helpers.Json; 3 | 4 | namespace FEZRepacker.Core.Conversion.Formats 5 | { 6 | using TextStorage = Dictionary>; 7 | internal class TextStorageConverter : FormatConverter 8 | { 9 | public override string FileFormat => ".feztxt"; 10 | 11 | public override FileBundle ConvertTyped(TextStorage data) 12 | { 13 | return ConfiguredJsonSerializer.SerializeToFileBundle(FileFormat, data); 14 | } 15 | 16 | public override TextStorage DeconvertTyped(FileBundle bundle) 17 | { 18 | return ConfiguredJsonSerializer.DeserializeFromFileBundle(bundle); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Core/Conversion/Formats/TextureConverter.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.XNA; 2 | using FEZRepacker.Core.FileSystem; 3 | using FEZRepacker.Core.Helpers; 4 | 5 | using SixLabors.ImageSharp; 6 | using SixLabors.ImageSharp.Formats.Png; 7 | using SixLabors.ImageSharp.PixelFormats; 8 | 9 | namespace FEZRepacker.Core.Conversion.Formats 10 | { 11 | internal class TextureConverter : FormatConverter 12 | { 13 | public override string FileFormat => ".png"; 14 | 15 | public override FileBundle ConvertTyped(Texture2D texture) 16 | { 17 | using var image = TexturesUtil.ImageFromTexture2D(texture); 18 | var outStream = image.SaveAsMemoryStream(new PngEncoder()); 19 | return FileBundle.Single(outStream, FileFormat); 20 | } 21 | 22 | public override Texture2D DeconvertTyped(FileBundle bundle) 23 | { 24 | using var importedImage = Image.Load(bundle.RequireData("")); 25 | return TexturesUtil.ImageToTexture2D(importedImage); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Core/Conversion/Formats/TrackedSongConverter.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.TrackedSong; 2 | using FEZRepacker.Core.FileSystem; 3 | using FEZRepacker.Core.Helpers.Json; 4 | 5 | namespace FEZRepacker.Core.Conversion.Formats 6 | { 7 | internal class TrackedSongConverter : FormatConverter 8 | { 9 | public override string FileFormat => ".fezsong"; 10 | 11 | public override FileBundle ConvertTyped(TrackedSong data) 12 | { 13 | return ConfiguredJsonSerializer.SerializeToFileBundle(FileFormat, data); 14 | } 15 | 16 | public override TrackedSong DeconvertTyped(FileBundle bundle) 17 | { 18 | return ConfiguredJsonSerializer.DeserializeFromFileBundle(bundle); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Core/Conversion/Formats/TrileSetConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using FEZRepacker.Core.Definitions.Game.ArtObject; 4 | using FEZRepacker.Core.Definitions.Game.Graphics; 5 | using FEZRepacker.Core.Definitions.Game.TrileSet; 6 | using FEZRepacker.Core.Definitions.Game.XNA; 7 | using FEZRepacker.Core.FileSystem; 8 | using FEZRepacker.Core.Helpers; 9 | using FEZRepacker.Core.Helpers.Json; 10 | 11 | using SixLabors.ImageSharp.Formats.Png; 12 | 13 | namespace FEZRepacker.Core.Conversion.Formats 14 | { 15 | internal class TrileSetConverter : FormatConverter 16 | { 17 | public override string FileFormat => ".fezts"; 18 | 19 | public override FileBundle ConvertTyped(TrileSet data) 20 | { 21 | var bundle = ConfiguredJsonSerializer.SerializeToFileBundle(FileFormat, data); 22 | 23 | bundle.AddFile(GetTextureStream(data, TexturesUtil.CubemapPart.Albedo), ".png"); 24 | bundle.AddFile(GetTextureStream(data, TexturesUtil.CubemapPart.Emission), ".apng"); 25 | bundle.AddFile(GetModelStream(data), ".obj"); 26 | 27 | return bundle; 28 | } 29 | 30 | public override TrileSet DeconvertTyped(FileBundle bundle) 31 | { 32 | var trileSet = ConfiguredJsonSerializer.DeserializeFromFileBundle(bundle); 33 | 34 | AppendGeometryStream(ref trileSet, bundle.RequireData(".obj")); 35 | LoadCubemap(ref trileSet, bundle.GetData(".png"), bundle.GetData(".apng")); 36 | 37 | return trileSet; 38 | } 39 | 40 | private static Stream GetTextureStream(TrileSet data, TexturesUtil.CubemapPart part) 41 | { 42 | using var texture = TexturesUtil.ExtractCubemapPartFromTexture(data.TextureAtlas, part); 43 | return texture.SaveAsMemoryStream(new PngEncoder()); 44 | } 45 | 46 | private static Stream GetModelStream(TrileSet data) 47 | { 48 | var geometryDict = new Dictionary>(); 49 | 50 | foreach (var trileRecord in data.Triles) 51 | { 52 | geometryDict[trileRecord.Key.ToString()] = trileRecord.Value.Geometry; 53 | } 54 | var objString = WavefrontObjUtil.ToWavefrontObj(geometryDict); 55 | return new MemoryStream(Encoding.UTF8.GetBytes(objString)); 56 | } 57 | 58 | 59 | private static void AppendGeometryStream(ref TrileSet data, Stream geometryStream) 60 | { 61 | var geometries = TrixelArtUtil.LoadGeometry(geometryStream); 62 | foreach (var objRecord in geometries) 63 | { 64 | var id = int.Parse(objRecord.Key); 65 | 66 | if (!data.Triles.ContainsKey(id)) 67 | { 68 | data.Triles[id] = new Trile(); 69 | } 70 | data.Triles[id].Geometry = objRecord.Value; 71 | } 72 | } 73 | 74 | private static void LoadCubemap(ref TrileSet data, Stream albedoStream, Stream emissionStream) 75 | { 76 | using var image = TexturesUtil.ConstructCubemap(albedoStream, emissionStream); 77 | data.TextureAtlas = TexturesUtil.ImageToTexture2D(image); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Core/Definitions/Game/ArtObject/ArtObject.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | using FEZRepacker.Core.Definitions.Game.Common; 4 | using FEZRepacker.Core.Definitions.Game.Graphics; 5 | using FEZRepacker.Core.Definitions.Game.XNA; 6 | 7 | 8 | namespace FEZRepacker.Core.Definitions.Game.ArtObject 9 | { 10 | [XnbType("FezEngine.Structure.ArtObject")] 11 | [XnbReaderType("FezEngine.Readers.ArtObjectReader")] 12 | public class ArtObject 13 | { 14 | [XnbProperty] 15 | public string Name { get; set; } = ""; 16 | 17 | [JsonIgnore] 18 | [XnbProperty(UseConverter = true)] 19 | public Texture2D Cubemap { get; set; } = new(); 20 | 21 | [XnbProperty] 22 | public Vector3 Size { get; set; } 23 | 24 | [JsonIgnore] 25 | [XnbProperty(UseConverter = true)] 26 | public IndexedPrimitives Geometry { get; set; } = new(); 27 | 28 | [XnbProperty(UseConverter = true)] 29 | public ActorType ActorType { get; set; } 30 | 31 | [XnbProperty] 32 | public bool NoSihouette { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Core/Definitions/Game/ArtObject/VertexInstance.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.XNA; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game.ArtObject 4 | { 5 | [XnbType("FezEngine.Structure.Geometry.VertexPositionNormalTextureInstance")] 6 | [XnbReaderType("FezEngine.Readers.VertexPositionNormalTextureInstanceReader")] 7 | // Original name in FezEngine: VertexPositionNormalTextureInstance 8 | public class VertexInstance 9 | { 10 | [XnbProperty] 11 | public Vector3 Position { get; set; } 12 | 13 | [XnbProperty] 14 | public byte NormalByte { get; set; } 15 | 16 | [XnbProperty] 17 | public Vector2 TextureCoordinate { get; set; } 18 | 19 | 20 | 21 | public Vector3 Normal 22 | { 23 | get 24 | { 25 | return ByteToNormal(NormalByte); 26 | } 27 | set 28 | { 29 | NormalByte = NormalToByte(value); 30 | } 31 | } 32 | 33 | public static Vector3 ByteToNormal(byte normalByte) 34 | { 35 | switch (normalByte % 6) 36 | { 37 | case 0: return new Vector3(-1f, 0f, 0f); // left 38 | case 1: return new Vector3(0f, -1f, 0f); // down 39 | case 2: return new Vector3(0f, 0f, -1f); // forward 40 | case 3: return new Vector3(1f, 0f, 0f); // right 41 | case 4: return new Vector3(0f, 1f, 0f); // up 42 | case 5: return new Vector3(0f, 0f, 1f); // backward 43 | } 44 | return new Vector3(); 45 | } 46 | 47 | public static byte NormalToByte(Vector3 normal) 48 | { 49 | byte normalByte = 0; 50 | float smallestDistance = 9f; 51 | for (byte i = 0; i < 6; i++) 52 | { 53 | float distance = (normal - ByteToNormal(i)).Length(); 54 | if (distance < smallestDistance) 55 | { 56 | smallestDistance = distance; 57 | normalByte = i; 58 | } 59 | } 60 | return normalByte; 61 | } 62 | 63 | public override bool Equals(object obj) 64 | { 65 | var other = obj as VertexInstance; 66 | return other != null 67 | && other.Position == this.Position 68 | && other.NormalByte == this.NormalByte 69 | && other.TextureCoordinate == this.TextureCoordinate; 70 | } 71 | 72 | public override int GetHashCode() 73 | { 74 | return Position.GetHashCode() ^ NormalByte.GetHashCode() ^ TextureCoordinate.GetHashCode(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Common/ActorType.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Common 2 | { 3 | [XnbType("FezEngine.Structure.ActorType")] 4 | public enum ActorType 5 | { 6 | None, 7 | Ladder, 8 | Bouncer, 9 | Sign, 10 | GoldenCube, 11 | PickUp, 12 | Bomb, 13 | Destructible, 14 | DestructiblePermanent, 15 | Vase, 16 | Door, 17 | Heart, 18 | Watcher, 19 | Crystal, 20 | BlackHole, 21 | Vine, 22 | BigBomb, 23 | TntBlock, 24 | TntPickup, 25 | MotorBlock, 26 | Hurt, 27 | Checkpoint, 28 | TreasureChest, 29 | CubeShard, 30 | BigHeart, 31 | SkeletonKey, 32 | ExploSwitch, 33 | PushSwitch, 34 | EightBitDoor, 35 | PushSwitchSticky, 36 | PushSwitchPermanent, 37 | SuckBlock, 38 | WarpGate, 39 | OneBitDoor, 40 | SpinBlock, 41 | PivotHandle, 42 | FourBitDoor, 43 | LightningPlatform, 44 | LightningGhost, 45 | Tombstone, 46 | SplitUpCube, 47 | UnlockedDoor, 48 | Hole, 49 | Couch, 50 | Valve, 51 | Rumbler, 52 | Waterfall, 53 | Trickle, 54 | Drips, 55 | Geyser, 56 | ConnectiveRail, 57 | BoltHandle, 58 | BoltNutBottom, 59 | BoltNutTop, 60 | CodeMachine, 61 | NumberCube, 62 | LetterCube, 63 | TriSkull, 64 | Tome, 65 | SecretCube, 66 | LesserGate, 67 | Crumbler, 68 | LaserEmitter, 69 | LaserBender, 70 | LaserReceiver, 71 | RebuildingHexahedron, 72 | TreasureMap, 73 | Timeswitch, 74 | TimeswitchMovingPart, 75 | Mail, 76 | Mailbox, 77 | Bookcase, 78 | TwoBitDoor, 79 | SixteenBitDoor, 80 | ThirtyTwoBitDoor, 81 | SixtyFourBitDoor, 82 | Owl, 83 | Bell, 84 | RotatingGroup, 85 | BigWaterfall, 86 | Telescope, 87 | SinkPickup, 88 | QrCode, 89 | FpsPost, 90 | PieceOfHeart, 91 | SecretPassage, 92 | Piston 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Common/FaceOrientation.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Common 2 | { 3 | [XnbType("FezEngine.FaceOrientation")] 4 | public enum FaceOrientation 5 | { 6 | Left, 7 | Down, 8 | Back, 9 | Right, 10 | Top, 11 | Front, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Common/LevelNodeType.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Common 2 | { 3 | [XnbType("FezEngine.LevelNodeType")] 4 | public enum LevelNodeType 5 | { 6 | Node, 7 | Hub, 8 | Lesser 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Common/NpcAction.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Common 2 | { 3 | [XnbType("FezEngine.Structure.NpcAction")] 4 | public enum NpcAction 5 | { 6 | None, 7 | Idle, 8 | Idle2, 9 | Idle3, 10 | Walk, 11 | Turn, 12 | Talk, 13 | Burrow, 14 | Hide, 15 | ComeOut, 16 | TakeOff, 17 | Fly, 18 | Land 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Graphics/AnimatedTexture.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Graphics 2 | { 3 | [XnbType("FezEngine.Structure.AnimatedTexture")] 4 | [XnbReaderType("FezEngine.Readers.AnimatedTextureReader")] 5 | public class AnimatedTexture 6 | { 7 | // Definition has been altered since the Texture property is 8 | // serialized in a different way than Texture reader does it 9 | 10 | [XnbProperty] 11 | public int AtlasWidth { get; set; } 12 | 13 | [XnbProperty] 14 | public int AtlasHeight { get; set; } 15 | 16 | [XnbProperty] 17 | public int FrameWidth { get; set; } 18 | 19 | [XnbProperty] 20 | public int FrameHeight { get; set; } 21 | 22 | [XnbProperty(UseConverter = true, SkipIdentifier = true)] 23 | public byte[] TextureData { get; set; } = { }; 24 | 25 | [XnbProperty(UseConverter = true)] 26 | public List Frames { get; set; } = new(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Graphics/FrameContent.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.Definitions.Game.XNA; 3 | 4 | namespace FEZRepacker.Core.Definitions.Game.Graphics 5 | { 6 | [XnbType("FezEngine.Content.FrameContent")] 7 | [XnbReaderType("FezEngine.Readers.FrameReader")] 8 | public class FrameContent 9 | { 10 | [XnbProperty(UseConverter = true)] 11 | public TimeSpan Duration { get; set; } 12 | 13 | [XnbProperty(UseConverter = true)] 14 | public Rectangle Rectangle { get; set; } = new(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Graphics/IndexedPrimitives.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.XNA; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game.Graphics 4 | { 5 | [XnbType("FezEngine.Structure.Geometry.ShaderInstancedIndexedPrimitives")] 6 | [XnbReaderType("FezEngine.Readers.ShaderInstancedIndexedPrimitivesReader")] 7 | // Original name in FezEngine: ShaderInstancedIndexedPrimitives 8 | public class IndexedPrimitives 9 | { 10 | [XnbProperty(UseConverter = true)] 11 | public PrimitiveType PrimitiveType { get; set; } 12 | 13 | [XnbProperty(UseConverter = true)] 14 | public TemplateType[] Vertices { get; set; } 15 | 16 | [XnbProperty(UseConverter = true)] 17 | public ushort[] Indices { get; set; } 18 | 19 | public IndexedPrimitives() 20 | { 21 | Vertices = new TemplateType[0]; 22 | Indices = new ushort[0]; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/AmbienceTrack.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level 2 | { 3 | [XnbType("FezEngine.Structure.AmbienceTrack")] 4 | [XnbReaderType("FezEngine.Readers.AmbienceTrackReader")] 5 | public class AmbienceTrack 6 | { 7 | [XnbProperty(UseConverter = true)] 8 | public string Name { get; set; } = ""; 9 | 10 | [XnbProperty] 11 | public bool Day { get; set; } 12 | 13 | [XnbProperty] 14 | public bool Dusk { get; set; } 15 | 16 | [XnbProperty] 17 | public bool Night { get; set; } 18 | 19 | [XnbProperty] 20 | public bool Dawn { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/ArtObjectActorSettings.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.Common; 2 | using FEZRepacker.Core.Definitions.Game.XNA; 3 | 4 | namespace FEZRepacker.Core.Definitions.Game.Level 5 | { 6 | [XnbType("FezEngine.Structure.ArtObjectActorSettings")] 7 | [XnbReaderType("FezEngine.Readers.ArtObjectActorSettingsReader")] 8 | public class ArtObjectActorSettings 9 | { 10 | [XnbProperty] 11 | public bool Inactive { get; set; } 12 | 13 | [XnbProperty(UseConverter = true)] 14 | public ActorType ContainedTrile { get; set; } 15 | 16 | [XnbProperty(Optional = true)] 17 | public int? AttachedGroup { get; set; } 18 | 19 | [XnbProperty(UseConverter = true)] 20 | public Viewpoint SpinView { get; set; } 21 | 22 | [XnbProperty] 23 | public float SpinEvery { get; set; } 24 | 25 | [XnbProperty] 26 | public float SpinOffset { get; set; } 27 | 28 | [XnbProperty] 29 | public bool OffCenter { get; set; } 30 | 31 | [XnbProperty] 32 | public Vector3 RotationCenter { get; set; } 33 | 34 | [XnbProperty(UseConverter = true)] 35 | public VibrationMotor[] VibrationPattern { get; set; } = { }; 36 | 37 | [XnbProperty(UseConverter = true)] 38 | public CodeInput[] CodePattern { get; set; } = { }; 39 | 40 | [XnbProperty(UseConverter = true)] 41 | public PathSegment Segment { get; set; } = new(); 42 | 43 | [XnbProperty(Optional = true)] 44 | public int? NextNode { get; set; } 45 | 46 | [XnbProperty(UseConverter = true)] 47 | public string DestinationLevel { get; set; } = ""; 48 | 49 | [XnbProperty(UseConverter = true)] 50 | public string TreasureMapName { get; set; } = ""; 51 | 52 | [XnbProperty(UseConverter = true)] 53 | public FaceOrientation[] InvisibleSides { get; set; } = { }; 54 | 55 | [XnbProperty] 56 | public float TimeswitchWindBackSpeed { get; set; } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/ArtObjectInstance.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.XNA; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game.Level 4 | { 5 | [XnbType("FezEngine.Structure.ArtObjectInstance")] 6 | [XnbReaderType("FezEngine.Readers.ArtObjectInstanceReader")] 7 | public class ArtObjectInstance 8 | { 9 | [XnbProperty] 10 | public string Name { get; set; } = ""; 11 | 12 | [XnbProperty] 13 | public Vector3 Position { get; set; } 14 | 15 | [XnbProperty] 16 | public Quaternion Rotation { get; set; } 17 | 18 | [XnbProperty] 19 | public Vector3 Scale { get; set; } 20 | 21 | [XnbProperty(UseConverter = true)] 22 | public ArtObjectActorSettings ActorSettings { get; set; } = new(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/BackgroundPlane.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.Common; 2 | using FEZRepacker.Core.Definitions.Game.XNA; 3 | 4 | namespace FEZRepacker.Core.Definitions.Game.Level 5 | { 6 | [XnbType("FezEngine.Structure.BackgroundPlane")] 7 | [XnbReaderType("FezEngine.Readers.BackgroundPlaneReader")] 8 | public class BackgroundPlane 9 | { 10 | [XnbProperty] 11 | public Vector3 Position { get; set; } 12 | 13 | [XnbProperty] 14 | public Quaternion Rotation { get; set; } = Quaternion.Identity; 15 | 16 | [XnbProperty] 17 | public Vector3 Scale { get; set; } = new Vector3(1.0f, 1.0f, 1.0f); 18 | 19 | [XnbProperty] 20 | public Vector3 Size { get; set; } 21 | 22 | [XnbProperty] 23 | public string TextureName { get; set; } = ""; 24 | 25 | [XnbProperty] 26 | public bool LightMap { get; set; } 27 | 28 | [XnbProperty] 29 | public bool AllowOverbrightness { get; set; } 30 | 31 | [XnbProperty] 32 | public Color Filter { get; set; } = new Color(255, 255, 255, 255); 33 | 34 | [XnbProperty] 35 | public bool Animated { get; set; } 36 | 37 | [XnbProperty] 38 | public bool Doublesided { get; set; } 39 | 40 | [XnbProperty] 41 | public float Opacity { get; set; } = 1.0f; 42 | 43 | [XnbProperty(Optional = true)] 44 | public int? AttachedGroup { get; set; } 45 | 46 | [XnbProperty] 47 | public bool Billboard { get; set; } 48 | 49 | [XnbProperty] 50 | public bool SyncWithSamples { get; set; } 51 | 52 | [XnbProperty] 53 | public bool Crosshatch { get; set; } 54 | 55 | // no idea what it's for. It's in the original reader, so I'm leaving it here 56 | // perhaps a deleted flag? 57 | [XnbProperty] 58 | public bool UnusedFlag { get; set; } 59 | 60 | [XnbProperty] 61 | public bool AlwaysOnTop { get; set; } 62 | 63 | [XnbProperty] 64 | public bool Fullbright { get; set; } 65 | 66 | [XnbProperty] 67 | public bool PixelatedLightmap { get; set; } 68 | 69 | [XnbProperty] 70 | public bool XTextureRepeat { get; set; } 71 | 72 | [XnbProperty] 73 | public bool YTextureRepeat { get; set; } 74 | 75 | [XnbProperty] 76 | public bool ClampTexture { get; set; } 77 | 78 | [XnbProperty(UseConverter = true)] 79 | public ActorType ActorType { get; set; } 80 | 81 | [XnbProperty(Optional = true)] 82 | public int? AttachedPlane { get; set; } 83 | 84 | [XnbProperty] 85 | public float ParallaxFactor { get; set; } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/CameraNodeData.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level 2 | { 3 | [XnbType("FezEngine.Structure.CameraNodeData")] 4 | [XnbReaderType("FezEngine.Readers.CameraNodeDataReader")] 5 | public class CameraNodeData 6 | { 7 | [XnbProperty] 8 | public bool Perspective { get; set; } 9 | 10 | [XnbProperty] 11 | public int PixelsPerTrixel { get; set; } 12 | 13 | [XnbProperty(UseConverter = true)] 14 | public string SoundName { get; set; } = ""; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/CodeInput.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level 2 | { 3 | [XnbType("FezEngine.Structure.Input.CodeInput")] 4 | [Flags] 5 | public enum CodeInput 6 | { 7 | None = 0, 8 | Up = 1, 9 | Down = 2, 10 | Left = 4, 11 | Right = 8, 12 | SpinLeft = 16, 13 | SpinRight = 32, 14 | Jump = 64, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/DotDialogueLine.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level 2 | { 3 | [XnbType("FezEngine.Structure.DotDialogueLine")] 4 | [XnbReaderType("FezEngine.Readers.DotDialogueLineReader")] 5 | public class DotDialogueLine 6 | { 7 | [XnbProperty(UseConverter = true)] 8 | public string ResourceText { get; set; } = ""; 9 | 10 | [XnbProperty] 11 | public bool Grouped { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/Level.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.Common; 2 | using FEZRepacker.Core.Definitions.Game.Level.Scripting; 3 | using FEZRepacker.Core.Definitions.Game.XNA; 4 | 5 | namespace FEZRepacker.Core.Definitions.Game.Level 6 | { 7 | [XnbType("FezEngine.Structure.Level")] 8 | [XnbReaderType("FezEngine.Readers.LevelReader")] 9 | public class Level 10 | { 11 | [XnbProperty(UseConverter = true)] 12 | public string Name { get; set; } = ""; 13 | 14 | [XnbProperty] 15 | public Vector3 Size { get; set; } 16 | 17 | [XnbProperty(UseConverter = true)] 18 | public TrileFace StartingFace { get; set; } = new(); 19 | 20 | [XnbProperty(UseConverter = true)] 21 | public string SequenceSamplesPath { get; set; } = ""; 22 | 23 | [XnbProperty] 24 | public bool Flat { get; set; } 25 | 26 | [XnbProperty] 27 | public bool SkipPostProcess { get; set; } 28 | 29 | [XnbProperty] 30 | public float BaseDiffuse { get; set; } = 1.0f; 31 | 32 | [XnbProperty] 33 | public float BaseAmbient { get; set; } = 0.35f; 34 | 35 | [XnbProperty(UseConverter = true)] 36 | public string GomezHaloName { get; set; } = ""; 37 | 38 | [XnbProperty] 39 | public bool HaloFiltering { get; set; } 40 | 41 | [XnbProperty] 42 | public bool BlinkingAlpha { get; set; } 43 | 44 | [XnbProperty] 45 | public bool Loops { get; set; } 46 | 47 | [XnbProperty(UseConverter = true)] 48 | public LiquidType WaterType { get; set; } 49 | 50 | [XnbProperty] 51 | public float WaterHeight { get; set; } 52 | 53 | [XnbProperty] 54 | public string SkyName { get; set; } = ""; 55 | 56 | [XnbProperty(UseConverter = true)] 57 | public string TrileSetName { get; set; } = ""; 58 | 59 | [XnbProperty(UseConverter = true)] 60 | public Dictionary Volumes { get; set; } = new(); 61 | 62 | [XnbProperty(UseConverter = true)] 63 | public Dictionary Scripts { get; set; } = new(); 64 | 65 | [XnbProperty(UseConverter = true)] 66 | public string SongName { get; set; } = ""; 67 | 68 | [XnbProperty] 69 | public int FAPFadeOutStart { get; set; } 70 | 71 | [XnbProperty] 72 | public int FAPFadeOutLength { get; set; } 73 | 74 | [XnbProperty(UseConverter = true)] 75 | public Dictionary Triles { get; set; } = new(); 76 | 77 | [XnbProperty(UseConverter = true)] 78 | public Dictionary ArtObjects { get; set; } = new(); 79 | 80 | [XnbProperty(UseConverter = true)] 81 | public Dictionary BackgroundPlanes { get; set; } = new(); 82 | 83 | [XnbProperty(UseConverter = true)] 84 | public Dictionary Groups { get; set; } = new(); 85 | 86 | [XnbProperty(UseConverter = true)] 87 | public Dictionary NonPlayerCharacters { get; set; } = new(); 88 | 89 | [XnbProperty(UseConverter = true)] 90 | public Dictionary Paths { get; set; } = new(); 91 | 92 | [XnbProperty] 93 | public bool Descending { get; set; } 94 | 95 | [XnbProperty] 96 | public bool Rainy { get; set; } 97 | 98 | [XnbProperty] 99 | public bool LowPass { get; set; } 100 | 101 | [XnbProperty(UseConverter = true)] 102 | public List MutedLoops { get; set; } = new(); 103 | 104 | [XnbProperty(UseConverter = true)] 105 | public List AmbienceTracks { get; set; } = new(); 106 | 107 | [XnbProperty(UseConverter = true)] 108 | public LevelNodeType NodeType { get; set; } 109 | 110 | [XnbProperty] 111 | public bool Quantum { get; set; } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/LiquidType.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level 2 | { 3 | [XnbType("FezEngine.Structure.LiquidType")] 4 | public enum LiquidType 5 | { 6 | None, 7 | Water, 8 | Blood, 9 | Lava, 10 | Sewer, 11 | Purple, 12 | Green, 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/MovementPath.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level 2 | { 3 | [XnbType("FezEngine.Structure.MovementPath")] 4 | [XnbReaderType("FezEngine.Readers.MovementPathReader")] 5 | public class MovementPath 6 | { 7 | [XnbProperty(UseConverter = true)] 8 | public List Segments { get; set; } = new(); 9 | 10 | [XnbProperty] 11 | public bool NeedsTrigger { get; set; } 12 | 13 | [XnbProperty(UseConverter = true)] 14 | public PathEndBehavior EndBehavior { get; set; } 15 | 16 | [XnbProperty(UseConverter = true)] 17 | public string SoundName { get; set; } = ""; 18 | 19 | [XnbProperty] 20 | public bool IsSpline { get; set; } 21 | 22 | [XnbProperty] 23 | public float OffsetSeconds { get; set; } 24 | 25 | [XnbProperty] 26 | public bool SaveTrigger { get; set; } 27 | } 28 | } -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/NpcActionContent.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level 2 | { 3 | [XnbType("FezEngine.Structure.NpcActionContent")] 4 | [XnbReaderType("FezEngine.Readers.NpcActionContentReader")] 5 | public class NpcActionContent 6 | { 7 | [XnbProperty(UseConverter = true)] 8 | public string AnimationName { get; set; } = ""; 9 | 10 | [XnbProperty(UseConverter = true)] 11 | public string SoundName { get; set; } = ""; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/NpcInstance.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.Common; 2 | using FEZRepacker.Core.Definitions.Game.XNA; 3 | 4 | namespace FEZRepacker.Core.Definitions.Game.Level 5 | { 6 | [XnbType("FezEngine.Structure.NpcInstance")] 7 | [XnbReaderType("FezEngine.Readers.NpcInstanceReader")] 8 | public class NpcInstance 9 | { 10 | [XnbProperty] 11 | public string Name { get; set; } = ""; 12 | 13 | [XnbProperty] 14 | public Vector3 Position { get; set; } 15 | 16 | [XnbProperty] 17 | public Vector3 DestinationOffset { get; set; } 18 | 19 | [XnbProperty] 20 | public float WalkSpeed { get; set; } 21 | 22 | [XnbProperty] 23 | public bool RandomizeSpeech { get; set; } 24 | 25 | [XnbProperty] 26 | public bool SayFirstSpeechLineOnce { get; set; } 27 | 28 | [XnbProperty] 29 | public bool AvoidsGomez { get; set; } 30 | 31 | [XnbProperty(UseConverter = true)] 32 | public ActorType ActorType { get; set; } 33 | 34 | [XnbProperty(UseConverter = true)] 35 | public List Speech { get; set; } = new(); 36 | 37 | [XnbProperty(UseConverter = true)] 38 | public Dictionary Actions { get; set; } = new(); 39 | } 40 | } -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/PathEndBehabiour.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level 2 | { 3 | [XnbType("FezEngine.Structure.PathEndBehavior")] 4 | public enum PathEndBehavior 5 | { 6 | Bounce, 7 | Loop, 8 | Stop 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/PathSegment.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.XNA; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game.Level 4 | { 5 | [XnbType("FezEngine.Structure.PathSegment")] 6 | [XnbReaderType("FezEngine.Readers.PathSegmentReader")] 7 | public class PathSegment 8 | { 9 | [XnbProperty] 10 | public Vector3 Destination { get; set; } 11 | 12 | [XnbProperty(UseConverter = true)] 13 | public TimeSpan Duration { get; set; } = TimeSpan.FromSeconds(1.0f); 14 | 15 | [XnbProperty(UseConverter = true)] 16 | public TimeSpan WaitTimeOnStart { get; set; } 17 | 18 | [XnbProperty(UseConverter = true)] 19 | public TimeSpan WaitTimeOnFinish { get; set; } 20 | 21 | [XnbProperty] 22 | public float Acceleration { get; set; } 23 | 24 | [XnbProperty] 25 | public float Deceleration { get; set; } 26 | 27 | [XnbProperty] 28 | public float JitterFactor { get; set; } 29 | 30 | [XnbProperty] 31 | public Quaternion Orientation { get; set; } = Quaternion.Identity; 32 | 33 | [XnbProperty(UseConverter = true, Optional = true)] 34 | public CameraNodeData? CustomData { get; set; } = null; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/Scripting/ComparisonOperator.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level.Scripting 2 | { 3 | [XnbType("FezEngine.Structure.Scripting.ComparisonOperator")] 4 | public enum ComparisonOperator 5 | { 6 | None = -1, 7 | Equal = 0, 8 | Greater = 1, 9 | GreaterEqual = 2, 10 | Less = 3, 11 | LessEqual = 4, 12 | NotEqual = 5, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/Scripting/Entity.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level.Scripting 2 | { 3 | [XnbType("FezEngine.Structure.Scripting.Entity")] 4 | [XnbReaderType("FezEngine.Readers.EntityReader")] 5 | public class Entity 6 | { 7 | [XnbProperty] 8 | public string Type { get; set; } = ""; 9 | 10 | [XnbProperty(Optional = true)] 11 | public int? Identifier { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/Scripting/Script.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level.Scripting 2 | { 3 | [XnbType("FezEngine.Structure.Scripting.Script")] 4 | [XnbReaderType("FezEngine.Readers.ScriptReader")] 5 | public class Script 6 | { 7 | [XnbProperty] 8 | public string Name { get; set; } = "Untitled"; 9 | 10 | [XnbProperty(Optional = true)] 11 | public TimeSpan? Timeout { get; set; } 12 | 13 | [XnbProperty(UseConverter = true)] 14 | public List Triggers { get; set; } = new(); 15 | 16 | [XnbProperty(UseConverter = true)] 17 | public List Conditions { get; set; } = new(); 18 | 19 | [XnbProperty(UseConverter = true)] 20 | public List Actions { get; set; } = new(); 21 | 22 | [XnbProperty] 23 | public bool OneTime { get; set; } 24 | 25 | [XnbProperty] 26 | public bool Triggerless { get; set; } 27 | 28 | [XnbProperty] 29 | public bool IgnoreEndTriggers { get; set; } 30 | 31 | [XnbProperty] 32 | public bool LevelWideOneTime { get; set; } 33 | 34 | [XnbProperty] 35 | public bool Disabled { get; set; } 36 | 37 | [XnbProperty] 38 | public bool IsWinCondition { get; set; } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/Scripting/ScriptAction.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level.Scripting 2 | { 3 | [XnbType("FezEngine.Structure.Scripting.ScriptAction")] 4 | [XnbReaderType("FezEngine.Readers.ScriptActionReader")] 5 | public class ScriptAction 6 | { 7 | [XnbProperty(UseConverter = true)] 8 | public Entity Object { get; set; } = new(); 9 | 10 | [XnbProperty] 11 | public string Operation { get; set; } = ""; 12 | 13 | [XnbProperty(UseConverter = true)] 14 | public string[] Arguments { get; set; } = { }; 15 | 16 | [XnbProperty] 17 | public bool Killswitch { get; set; } // whether action should kill this script 18 | 19 | [XnbProperty] 20 | public bool Blocking { get; set; } // should action be blocked from execution if another one is executing already 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/Scripting/ScriptCondition.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level.Scripting 2 | { 3 | [XnbType("FezEngine.Structure.Scripting.ScriptCondition")] 4 | [XnbReaderType("FezEngine.Readers.ScriptConditionReader")] 5 | public class ScriptCondition 6 | { 7 | [XnbProperty(UseConverter = true)] 8 | public Entity Object { get; set; } = new(); 9 | 10 | [XnbProperty(UseConverter = true)] 11 | public ComparisonOperator Operator { get; set; } = ComparisonOperator.None; 12 | 13 | [XnbProperty] 14 | public string Property { get; set; } = ""; 15 | 16 | [XnbProperty] 17 | public string Value { get; set; } = ""; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/Scripting/ScriptTrigger.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level.Scripting 2 | { 3 | [XnbType("FezEngine.Structure.Scripting.ScriptTrigger")] 4 | [XnbReaderType("FezEngine.Readers.ScriptTriggerReader")] 5 | public class ScriptTrigger 6 | { 7 | [XnbProperty(UseConverter = true)] 8 | public Entity Object { get; set; } = new(); 9 | 10 | [XnbProperty] 11 | public string Event { get; set; } = ""; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/SpeechLine.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level 2 | { 3 | [XnbType("FezEngine.Structure.SpeechLine")] 4 | [XnbReaderType("FezEngine.Readers.SpeechLineReader")] 5 | public class SpeechLine 6 | { 7 | [XnbProperty(UseConverter = true)] 8 | public string Text { get; set; } = ""; 9 | 10 | [XnbProperty(UseConverter = true)] 11 | public NpcActionContent OverrideContent { get; set; } = new(); 12 | } 13 | } -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/TrileEmplacement.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.XNA; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game.Level 4 | { 5 | [XnbType("FezEngine.Structure.TrileEmplacement")] 6 | [XnbReaderType("FezEngine.Readers.TrileEmplacementReader")] 7 | public class TrileEmplacement : IEquatable, IComparable 8 | { 9 | [XnbProperty] 10 | public int X { get; set; } 11 | 12 | [XnbProperty] 13 | public int Y { get; set; } 14 | 15 | [XnbProperty] 16 | public int Z { get; set; } 17 | 18 | public TrileEmplacement() { } 19 | 20 | public TrileEmplacement(int x, int y, int z) 21 | { 22 | X = x; 23 | Y = y; 24 | Z = z; 25 | } 26 | 27 | public TrileEmplacement(Vector3 vec) 28 | { 29 | X = EmplacementPositionRound(vec.X); 30 | Y = EmplacementPositionRound(vec.Y); 31 | Z = EmplacementPositionRound(vec.Z); 32 | } 33 | 34 | public override bool Equals(object? obj) 35 | { 36 | if ((obj == null) || !this.GetType().Equals(obj.GetType())) 37 | { 38 | return false; 39 | } 40 | else 41 | { 42 | TrileEmplacement p = (TrileEmplacement)obj; 43 | return (X == p.X) && (Y == p.Y) && (Z == p.Z); 44 | } 45 | } 46 | 47 | public override int GetHashCode() 48 | { 49 | return X ^ (Y << 10) ^ (Z << 20); 50 | } 51 | 52 | public bool Equals(TrileEmplacement? other) 53 | { 54 | if (other == null) return false; 55 | return (X == other.X) && (Y == other.Y) && (Z == other.Z); 56 | } 57 | 58 | public int CompareTo(TrileEmplacement? other) 59 | { 60 | if (other == null) return -1; 61 | int num = X.CompareTo(other.X); 62 | if (num == 0) 63 | { 64 | num = Y.CompareTo(other.Y); 65 | if (num == 0) 66 | { 67 | num = Z.CompareTo(other.Z); 68 | } 69 | } 70 | return num; 71 | } 72 | 73 | // Making sure numbers are rounded EXACTLY like in the game, 74 | // otheriwse the result might differ in some edge cases. 75 | private static int EmplacementPositionRound(double value) 76 | { 77 | if (value < 0.0) 78 | { 79 | return (int)(value - 0.5); 80 | } 81 | return (int)(value + 0.5); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/TrileFace.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.Common; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game.Level 4 | { 5 | [XnbType("FezEngine.Structure.TrileFace")] 6 | [XnbReaderType("FezEngine.Readers.TrileFaceReader")] 7 | public class TrileFace 8 | { 9 | [XnbProperty(UseConverter = true)] 10 | public TrileEmplacement Id { get; set; } = new(); 11 | 12 | [XnbProperty(UseConverter = true)] 13 | public FaceOrientation Face { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/TrileGroup.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.Common; 2 | using FEZRepacker.Core.Definitions.Game.XNA; 3 | 4 | namespace FEZRepacker.Core.Definitions.Game.Level 5 | { 6 | [XnbType("FezEngine.Structure.TrileGroup")] 7 | [XnbReaderType("FezEngine.Readers.TrileGroupReader")] 8 | public class TrileGroup 9 | { 10 | [XnbProperty(UseConverter = true)] 11 | public List Triles { get; set; } = new(); 12 | 13 | [XnbProperty(UseConverter = true, Optional = true, SkipIdentifier = true)] 14 | public MovementPath? Path { get; set; } = null; 15 | 16 | [XnbProperty] 17 | public bool Heavy { get; set; } 18 | 19 | [XnbProperty(UseConverter = true)] 20 | public ActorType ActorType { get; set; } 21 | 22 | [XnbProperty] 23 | public float GeyserOffset { get; set; } 24 | 25 | [XnbProperty] 26 | public float GeyserPauseFor { get; set; } 27 | 28 | [XnbProperty] 29 | public float GeyserLiftFor { get; set; } 30 | 31 | [XnbProperty] 32 | public float GeyserApexHeight { get; set; } 33 | 34 | [XnbProperty] 35 | public Vector3 SpinCenter { get; set; } 36 | 37 | [XnbProperty] 38 | public bool SpinClockwise { get; set; } 39 | 40 | [XnbProperty] 41 | public float SpinFrequency { get; set; } 42 | 43 | [XnbProperty] 44 | public bool SpinNeedsTriggering { get; set; } 45 | 46 | [XnbProperty] 47 | public bool Spin180Degrees { get; set; } 48 | 49 | [XnbProperty] 50 | public bool FallOnRotate { get; set; } 51 | 52 | [XnbProperty] 53 | public float SpinOffset { get; set; } 54 | 55 | [XnbProperty(UseConverter = true)] 56 | public string AssociatedSound { get; set; } = ""; 57 | } 58 | } -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/TrileInstance.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.XNA; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game.Level 4 | { 5 | [XnbType("FezEngine.Structure.TrileInstance")] 6 | [XnbReaderType("FezEngine.Readers.TrileInstanceReader")] 7 | public class TrileInstance 8 | { 9 | [XnbProperty] 10 | public Vector3 Position { get; set; } 11 | 12 | [XnbProperty] 13 | public int TrileId { get; set; } 14 | 15 | [XnbProperty] 16 | public byte PhiLight { get; set; } 17 | 18 | [XnbProperty(UseConverter = true, Optional = true)] 19 | public TrileInstanceActorSettings? ActorSettings { get; set; } = null; 20 | 21 | [XnbProperty(UseConverter = true)] 22 | public List OverlappedTriles { get; set; } = new(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/TrileInstanceActorSettings.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level 2 | { 3 | [XnbType("FezEngine.Structure.InstanceActorSettings")] 4 | [XnbReaderType("FezEngine.Readers.InstanceActorSettingsReader")] 5 | // Original name in FezEngine: InstanceActorSettings 6 | public class TrileInstanceActorSettings 7 | { 8 | [XnbProperty(Optional = true)] 9 | public int? ContainedTrile { get; set; } 10 | 11 | [XnbProperty(UseConverter = true)] 12 | public string SignText { get; set; } = ""; 13 | 14 | [XnbProperty(UseConverter = true)] 15 | public bool[] Sequence { get; set; } = { }; 16 | 17 | [XnbProperty(UseConverter = true)] 18 | public string SequenceSampleName { get; set; } = ""; 19 | 20 | [XnbProperty(UseConverter = true)] 21 | public string SequenceAlternateSampleName { get; set; } = ""; 22 | 23 | [XnbProperty(Optional = true)] 24 | public int? HostVolume { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/VibrationMotor.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level 2 | { 3 | [XnbType("FezEngine.Structure.Input.VibrationMotor")] 4 | public enum VibrationMotor 5 | { 6 | None, 7 | LeftLow, 8 | RightHigh 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/Viewpoint.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Level 2 | { 3 | [XnbType("FezEngine.Viewpoint")] 4 | public enum Viewpoint 5 | { 6 | None, 7 | Front, 8 | Right, 9 | Back, 10 | Left, 11 | Up, 12 | Down, 13 | Perspective 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/Volume.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.Common; 2 | using FEZRepacker.Core.Definitions.Game.XNA; 3 | 4 | namespace FEZRepacker.Core.Definitions.Game.Level 5 | { 6 | [XnbType("FezEngine.Structure.Volume")] 7 | [XnbReaderType("FezEngine.Readers.VolumeReader")] 8 | public class Volume 9 | { 10 | [XnbProperty(UseConverter = true)] 11 | public FaceOrientation[] Orientations { get; set; } = { }; 12 | 13 | [XnbProperty] 14 | public Vector3 From { get; set; } 15 | 16 | [XnbProperty] 17 | public Vector3 To { get; set; } 18 | 19 | [XnbProperty(UseConverter = true)] 20 | public VolumeActorSettings ActorSettings { get; set; } = new(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Level/VolumeActorSettings.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.XNA; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game.Level 4 | { 5 | [XnbType("FezEngine.Structure.VolumeActorSettings")] 6 | [XnbReaderType("FezEngine.Readers.VolumeActorSettingsReader")] 7 | public class VolumeActorSettings 8 | { 9 | [XnbProperty] 10 | public Vector2 FarawayPlaneOffset { get; set; } 11 | 12 | [XnbProperty] 13 | public bool IsPointOfInterest { get; set; } 14 | 15 | [XnbProperty(UseConverter = true)] 16 | public List DotDialogue { get; set; } = new(); 17 | 18 | [XnbProperty] 19 | public bool WaterLocked { get; set; } 20 | 21 | [XnbProperty(UseConverter = true)] 22 | public CodeInput[] CodePattern { get; set; } = { }; 23 | 24 | [XnbProperty] 25 | public bool IsBlackHole { get; set; } 26 | 27 | [XnbProperty] 28 | public bool NeedsTrigger { get; set; } 29 | 30 | [XnbProperty] 31 | public bool IsSecretPassage { get; set; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Core/Definitions/Game/MapTree/MapNode.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.Common; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game.MapTree 4 | { 5 | [XnbType("FezEngine.Structure.MapNode")] 6 | [XnbReaderType("FezEngine.Readers.MapNodeReader")] 7 | public class MapNode 8 | { 9 | [XnbProperty] 10 | public string LevelName { get; set; } = ""; 11 | 12 | [XnbProperty(UseConverter = true)] 13 | public List Connections { get; set; } = new(); 14 | 15 | [XnbProperty(UseConverter = true)] 16 | public LevelNodeType NodeType { get; set; } 17 | 18 | [XnbProperty(UseConverter = true)] 19 | public WinConditions Conditions { get; set; } = new(); 20 | 21 | [XnbProperty] 22 | public bool HasLesserGate { get; set; } 23 | 24 | [XnbProperty] 25 | public bool HasWarpGate { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Core/Definitions/Game/MapTree/MapNodeConnection.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.Common; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game.MapTree 4 | { 5 | [XnbType("FezEngine.Structure.MapNode+Connection")] 6 | [XnbReaderType("FezEngine.Readers.MapNodeConnectionReader")] 7 | public class MapNodeConnection 8 | { 9 | [XnbProperty(UseConverter = true)] 10 | public FaceOrientation Face { get; set; } 11 | 12 | [XnbProperty(UseConverter = true)] 13 | public MapNode Node { get; set; } = new(); 14 | 15 | [XnbProperty] 16 | public float BranchOversize { get;set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Core/Definitions/Game/MapTree/MapTree.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.MapTree 2 | { 3 | [XnbType("FezEngine.Structure.MapTree")] 4 | [XnbReaderType("FezEngine.Readers.MapTreeReader")] 5 | public class MapTree 6 | { 7 | [XnbProperty(UseConverter = true)] 8 | public MapNode Root { get; set; } = new(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Core/Definitions/Game/MapTree/WinConditions.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.MapTree 2 | { 3 | [XnbType("FezEngine.Structure.WinConditions")] 4 | [XnbReaderType("FezEngine.Readers.WinConditionsReader")] 5 | public class WinConditions 6 | { 7 | [XnbProperty] 8 | public int ChestCount { get; set; } 9 | 10 | [XnbProperty] 11 | public int LockedDoorCount { get; set; } 12 | 13 | [XnbProperty] 14 | public int UnlockedDoorCount { get; set; } 15 | 16 | [XnbProperty(UseConverter = true)] 17 | public List ScriptIds { get; set; } = new(); 18 | 19 | [XnbProperty] 20 | public int CubeShardCount { get; set; } 21 | 22 | [XnbProperty] 23 | public int OtherCollectibleCount { get; set; } 24 | 25 | [XnbProperty] 26 | public int SplitUpCount { get; set; } 27 | 28 | [XnbProperty] 29 | public int SecretCount { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Core/Definitions/Game/NpcMetadata/NpcMetadata.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.Common; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game.NpcMetadata 4 | { 5 | [XnbType("FezEngine.Structure.NpcMetadata")] 6 | [XnbReaderType("FezEngine.Readers.NpcMetadataReader")] 7 | public class NpcMetadata 8 | { 9 | [XnbProperty] 10 | public float WalkSpeed { get; set; } 11 | 12 | [XnbProperty] 13 | public bool AvoidsGomez { get;set; } 14 | 15 | [XnbProperty(UseConverter = true)] 16 | public string SoundPath { get; set; } = ""; 17 | 18 | [XnbProperty(UseConverter = true)] 19 | public List SoundActions { get; set; } = new(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Sky/Sky.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Sky 2 | { 3 | [XnbType("FezEngine.Structure.Sky")] 4 | [XnbReaderType("FezEngine.Readers.SkyReader")] 5 | public class Sky 6 | { 7 | [XnbProperty] 8 | public string Name { get; set; } = ""; 9 | 10 | [XnbProperty] 11 | public string Background { get; set; } = ""; 12 | 13 | [XnbProperty] 14 | public float WindSpeed { get; set; } 15 | 16 | [XnbProperty] 17 | public float Density { get; set; } 18 | 19 | [XnbProperty] 20 | public float FogDensity { get; set; } 21 | 22 | [XnbProperty(UseConverter = true)] 23 | public List Layers { get; set; } = new(); 24 | 25 | [XnbProperty(UseConverter = true)] 26 | public List Clouds { get; set; } = new(); 27 | 28 | [XnbProperty(UseConverter = true)] 29 | public string Shadows { get; set; } = ""; 30 | 31 | [XnbProperty(UseConverter = true)] 32 | public string Stars { get; set; } = ""; 33 | 34 | [XnbProperty(UseConverter = true)] 35 | public string CloudTint { get; set; } = ""; 36 | 37 | [XnbProperty] 38 | public bool VerticalTiling { get; set; } 39 | 40 | [XnbProperty] 41 | public bool HorizontalScrolling { get; set; } 42 | 43 | [XnbProperty] 44 | public float LayerBaseHeight { get; set; } 45 | 46 | [XnbProperty] 47 | public float InterLayerVerticalDistance { get; set; } 48 | 49 | [XnbProperty] 50 | public float InterLayerHorizontalDistance { get; set; } 51 | 52 | [XnbProperty] 53 | public float HorizontalDistance { get; set; } 54 | 55 | [XnbProperty] 56 | public float VerticalDistance { get; set; } 57 | 58 | [XnbProperty] 59 | public float LayerBaseSpacing { get; set; } 60 | 61 | [XnbProperty] 62 | public float WindParallax { get; set; } 63 | 64 | [XnbProperty] 65 | public float WindDistance { get; set; } 66 | 67 | [XnbProperty] 68 | public float CloudsParallax { get; set; } 69 | 70 | [XnbProperty] 71 | public float ShadowOpacity { get; set; } 72 | 73 | [XnbProperty] 74 | public bool FoliageShadows { get; set; } 75 | 76 | [XnbProperty] 77 | public bool NoPerFaceLayerXOffset { get; set; } 78 | 79 | [XnbProperty] 80 | public float LayerBaseXOffset { get; set; } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Core/Definitions/Game/Sky/SkyLayer.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.Sky 2 | { 3 | [XnbType("FezEngine.Structure.SkyLayer")] 4 | [XnbReaderType("FezEngine.Readers.SkyLayerReader")] 5 | public class SkyLayer 6 | { 7 | [XnbProperty] 8 | public string Name { get; set; } = ""; 9 | 10 | [XnbProperty] 11 | public bool InFront { get; set; } 12 | 13 | [XnbProperty] 14 | public float Opacity { get; set; } 15 | 16 | [XnbProperty] 17 | public float FogTint { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Core/Definitions/Game/TrackedSong/AssembleChords.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.TrackedSong 2 | { 3 | [XnbType("FezEngine.Structure.AssembleChords")] 4 | public enum AssembleChords 5 | { 6 | C_maj, 7 | Csharp_maj, 8 | D_maj, 9 | Dsharp_maj, 10 | E_maj, 11 | F_maj, 12 | Fsharp_maj, 13 | G_maj, 14 | Gsharp_maj, 15 | A_maj, 16 | Asharp_maj, 17 | B_maj 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Core/Definitions/Game/TrackedSong/Loop.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.TrackedSong 2 | { 3 | [XnbType("FezEngine.Structure.Loop")] 4 | [XnbReaderType("FezEngine.Readers.LoopReader")] 5 | public class Loop 6 | { 7 | [XnbProperty] 8 | public int Duration { get; set; } 9 | 10 | [XnbProperty] 11 | public int LoopTimesFrom { get; set; } 12 | 13 | [XnbProperty] 14 | public int LoopTimesTo { get; set; } 15 | 16 | [XnbProperty] 17 | public string Name { get; set; } = ""; 18 | 19 | [XnbProperty] 20 | public int TriggerFrom { get; set; } 21 | 22 | [XnbProperty] 23 | public int TriggerTo { get; set; } 24 | 25 | [XnbProperty] 26 | public int Delay { get; set; } 27 | 28 | [XnbProperty] 29 | public bool Night { get; set; } 30 | 31 | [XnbProperty] 32 | public bool Day { get; set; } 33 | 34 | [XnbProperty] 35 | public bool Dusk { get; set; } 36 | 37 | [XnbProperty] 38 | public bool Dawn { get; set; } 39 | 40 | [XnbProperty] 41 | public bool FractionalTime { get; set; } 42 | 43 | [XnbProperty] 44 | public bool OneAtATime { get; set; } 45 | 46 | [XnbProperty] 47 | public bool CutOffTail { get; set; } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Core/Definitions/Game/TrackedSong/ShardNotes.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.TrackedSong 2 | { 3 | [XnbType("FezEngine.Structure.ShardNotes")] 4 | public enum ShardNotes 5 | { 6 | C2, 7 | Csharp2, 8 | D2, 9 | Dsharp2, 10 | E2, 11 | F2, 12 | Fsharp2, 13 | G2, 14 | Gsharp2, 15 | A2, 16 | Asharp2, 17 | B2, 18 | C3, 19 | Csharp3, 20 | D3, 21 | Dsharp3, 22 | E3, 23 | F3, 24 | Fsharp3, 25 | G3, 26 | Gsharp3, 27 | A3, 28 | Asharp3, 29 | B3, 30 | C4 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Core/Definitions/Game/TrackedSong/TrackedSong.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.TrackedSong 2 | { 3 | [XnbType("FezEngine.Structure.TrackedSong")] 4 | [XnbReaderType("FezEngine.Readers.TrackedSongReader")] 5 | public class TrackedSong 6 | { 7 | [XnbProperty(UseConverter = true)] 8 | public List Loops { get; set; } = new(); 9 | 10 | [XnbProperty] 11 | public string Name { get; set; } = ""; 12 | 13 | [XnbProperty] 14 | public int Tempo { get; set; } 15 | 16 | [XnbProperty] 17 | public int TimeSignature { get; set; } 18 | 19 | [XnbProperty(UseConverter = true)] 20 | public ShardNotes[] Notes { get; set; } = { }; 21 | 22 | [XnbProperty(UseConverter = true)] 23 | public AssembleChords AssembleChord { get; set; } 24 | 25 | [XnbProperty] 26 | public bool RandomOrdering { get; set; } 27 | 28 | [XnbProperty(UseConverter = true)] 29 | public int[] CustomOrdering { get; set; } = {}; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Core/Definitions/Game/TrileSet/CollisionType.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.TrileSet 2 | { 3 | [XnbType("FezEngine.CollisionType")] 4 | public enum CollisionType 5 | { 6 | AllSides, 7 | TopOnly, 8 | None, 9 | Immaterial, 10 | TopNoStraightLedge 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Core/Definitions/Game/TrileSet/SurfaceType.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.TrileSet 2 | { 3 | [XnbType("FezEngine.Structure.SurfaceType")] 4 | public enum SurfaceType 5 | { 6 | Grass, 7 | Metal, 8 | Stone, 9 | Wood 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Core/Definitions/Game/TrileSet/Trile.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | using FEZRepacker.Core.Definitions.Game.ArtObject; 4 | using FEZRepacker.Core.Definitions.Game.Common; 5 | using FEZRepacker.Core.Definitions.Game.Graphics; 6 | using FEZRepacker.Core.Definitions.Game.XNA; 7 | 8 | namespace FEZRepacker.Core.Definitions.Game.TrileSet 9 | { 10 | [XnbType("FezEngine.Structure.Trile")] 11 | [XnbReaderType("FezEngine.Readers.TrileReader")] 12 | public class Trile 13 | { 14 | [XnbProperty] 15 | public string Name { get; set; } = ""; 16 | 17 | [XnbProperty] 18 | public string CubemapPath { get; set; } = ""; 19 | 20 | [XnbProperty] 21 | public Vector3 Size { get; set; } 22 | 23 | [XnbProperty] 24 | public Vector3 Offset { get; set; } 25 | 26 | [XnbProperty] 27 | public bool Immaterial { get; set; } 28 | 29 | [XnbProperty] 30 | public bool SeeThrough { get; set; } 31 | 32 | [XnbProperty] 33 | public bool Thin { get; set; } 34 | 35 | [XnbProperty] 36 | public bool ForceHugging { get; set; } 37 | 38 | [XnbProperty(UseConverter = true)] 39 | public Dictionary Faces { get; set; } = new(); 40 | 41 | [JsonIgnore] 42 | [XnbProperty(UseConverter = true)] 43 | public IndexedPrimitives Geometry { get; set; } = new(); 44 | 45 | [XnbProperty(UseConverter = true)] 46 | public ActorType Type { get; set; } 47 | 48 | [XnbProperty(UseConverter = true)] 49 | public FaceOrientation Face { get; set; } 50 | 51 | [XnbProperty(UseConverter = true)] 52 | public SurfaceType SurfaceType { get; set; } 53 | 54 | [XnbProperty] 55 | public Vector2 AtlasOffset { get; set; } 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Core/Definitions/Game/TrileSet/TrileSet.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | using FEZRepacker.Core.Definitions.Game.XNA; 4 | 5 | 6 | namespace FEZRepacker.Core.Definitions.Game.TrileSet 7 | { 8 | [XnbType("FezEngine.Structure.TrileSet")] 9 | [XnbReaderType("FezEngine.Readers.TrileSetReader")] 10 | public class TrileSet 11 | { 12 | [XnbProperty] 13 | public string Name { get; set; } = ""; 14 | 15 | [XnbProperty(UseConverter = true)] 16 | public Dictionary Triles { get; set; } = new(); 17 | 18 | [JsonIgnore] 19 | [XnbProperty(UseConverter = true)] 20 | public Texture2D TextureAtlas { get; set; } = new(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XNA/Color.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.XNA 2 | { 3 | [XnbType("Microsoft.Xna.Framework.Color")] 4 | [XnbReaderType("Microsoft.Xna.Framework.Content.ColorReader")] 5 | public class Color 6 | { 7 | [XnbProperty] 8 | public byte R { get; set; } 9 | 10 | [XnbProperty] 11 | public byte G { get; set; } 12 | 13 | [XnbProperty] 14 | public byte B { get; set; } 15 | 16 | [XnbProperty] 17 | public byte A { get; set; } 18 | 19 | public Color() { } 20 | 21 | public Color(byte r, byte g, byte b, byte a) 22 | { 23 | R = r; 24 | G = g; 25 | B = b; 26 | A = a; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XNA/Effect.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.XNA 2 | { 3 | [XnbType("Microsoft.Xna.Framework.Graphics.Effect")] 4 | [XnbReaderType("Microsoft.Xna.Framework.Content.EffectReader")] 5 | public class Effect 6 | { 7 | // Definition of this class is nothing alike what's actually in the game. 8 | // Stored data is simply a raw FXB file. 9 | 10 | [XnbProperty(UseConverter = true, SkipIdentifier = true)] 11 | public byte[] Data { get; set; } = { }; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XNA/Matrix.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.XNA 2 | { 3 | [XnbType("Microsoft.Xna.Framework.Matrix")] 4 | [XnbReaderType("Microsoft.Xna.Framework.Content.MatrixReader")] 5 | public class Matrix 6 | { 7 | [XnbProperty] 8 | public float M11 { get; set; } 9 | 10 | [XnbProperty] 11 | public float M12 { get; set; } 12 | 13 | [XnbProperty] 14 | public float M13 { get; set; } 15 | 16 | [XnbProperty] 17 | public float M14 { get; set; } 18 | 19 | [XnbProperty] 20 | public float M21 { get; set; } 21 | 22 | [XnbProperty] 23 | public float M22 { get; set; } 24 | 25 | [XnbProperty] 26 | public float M23 { get; set; } 27 | 28 | [XnbProperty] 29 | public float M24 { get; set; } 30 | 31 | [XnbProperty] 32 | public float M31 { get; set; } 33 | 34 | [XnbProperty] 35 | public float M32 { get; set; } 36 | 37 | [XnbProperty] 38 | public float M33 { get; set; } 39 | 40 | [XnbProperty] 41 | public float M34 { get; set; } 42 | 43 | [XnbProperty] 44 | public float M41 { get; set; } 45 | 46 | [XnbProperty] 47 | public float M42 { get; set; } 48 | 49 | [XnbProperty] 50 | public float M43 { get; set; } 51 | 52 | [XnbProperty] 53 | public float M44 { get; set; } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XNA/PrimitiveType.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.XNA 2 | { 3 | [XnbType("Microsoft.Xna.Framework.Graphics.PrimitiveType")] 4 | public enum PrimitiveType 5 | { 6 | TriangleList, 7 | TriangleStrip, 8 | LineList, 9 | LineStrip, 10 | PointListEXT 11 | } 12 | } -------------------------------------------------------------------------------- /Core/Definitions/Game/XNA/Quaternion.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.XNA 2 | { 3 | [XnbType("Microsoft.Xna.Framework.Quaternion")] 4 | [XnbReaderType("Microsoft.Xna.Framework.Content.QuaternionReader")] 5 | public struct Quaternion : IEquatable 6 | { 7 | [XnbProperty] 8 | public float X { get; set; } 9 | 10 | [XnbProperty] 11 | public float Y { get; set; } 12 | 13 | [XnbProperty] 14 | public float Z { get; set; } 15 | 16 | [XnbProperty] 17 | public float W { get; set; } 18 | 19 | public static Quaternion Identity => new Quaternion(0, 0, 0, 1); 20 | 21 | public Quaternion() { 22 | X = 0; 23 | Y = 0; 24 | Z = 0; 25 | W = 1; 26 | } 27 | 28 | public Quaternion(float x, float y, float z, float w) 29 | { 30 | X = x; 31 | Y = y; 32 | Z = z; 33 | W = w; 34 | } 35 | 36 | public bool Equals(Quaternion other) => 37 | this.X == other.X && this.Y == other.Y && this.Z == other.Z && this.W == other.W; 38 | public static bool operator ==(Quaternion left, Quaternion right) => left.Equals(right); 39 | public static bool operator !=(Quaternion left, Quaternion right) => !left.Equals(right); 40 | 41 | public override int GetHashCode() 42 | { 43 | return this.X.GetHashCode() + this.Y.GetHashCode() + this.Z.GetHashCode() + this.W.GetHashCode(); 44 | } 45 | 46 | public override bool Equals(object obj) 47 | { 48 | if (!(obj is Quaternion)) return false; 49 | return Equals((Quaternion)obj); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XNA/Rectangle.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.XNA 2 | { 3 | [XnbType("Microsoft.Xna.Framework.Rectangle")] 4 | [XnbReaderType("Microsoft.Xna.Framework.Content.RectangleReader")] 5 | public class Rectangle 6 | { 7 | [XnbProperty] 8 | public int X { get; set; } 9 | 10 | [XnbProperty] 11 | public int Y { get; set; } 12 | 13 | [XnbProperty] 14 | public int Width { get; set; } 15 | 16 | [XnbProperty] 17 | public int Height { get; set; } 18 | 19 | public Rectangle() 20 | { 21 | X = 0; 22 | Y = 0; 23 | Width = 0; 24 | Height = 0; 25 | } 26 | public Rectangle(int x, int y, int width, int height) 27 | { 28 | X = x; 29 | Y = y; 30 | Width = width; 31 | Height = height; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XNA/SoundEffect.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.XNA 2 | { 3 | [XnbType("Microsoft.Xna.Framework.Audio.SoundEffect")] 4 | [XnbReaderType("Microsoft.Xna.Framework.Content.SoundEffectReader")] 5 | public class SoundEffect 6 | { 7 | // Definition of this class is nothing alike what's actually in the game. 8 | // Stored data is extremely similar to WAV format and its chunks, 9 | // so I'm just storing raw byte arrays of chunks in most cases. 10 | 11 | [XnbProperty] 12 | public int FormatChunkSize { get; set; } 13 | 14 | [XnbProperty] 15 | public short FormatType { get; set; } 16 | 17 | [XnbProperty] 18 | public short ChannelCount { get; set; } 19 | 20 | [XnbProperty] 21 | public int SampleFrequency { get; set; } 22 | 23 | [XnbProperty] 24 | public int BytesPerSecond { get; set; } 25 | 26 | [XnbProperty] 27 | public short BlockAlignment { get; set; } 28 | 29 | [XnbProperty] 30 | public short BitsPerSample { get; set; } 31 | 32 | // normally this does not occur in PCM, but XNA's reader expects it. 33 | [XnbProperty] 34 | public short ExtraParameter { get; set; } 35 | 36 | [XnbProperty(UseConverter = true, SkipIdentifier = true)] 37 | public byte[] DataChunk { get; set; } = { }; 38 | 39 | [XnbProperty] 40 | public int LoopStart { get; set; } 41 | 42 | [XnbProperty] 43 | public int LoopLength { get; set; } 44 | 45 | [XnbProperty] 46 | public int UnknownValue { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XNA/SpriteFont.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace FEZRepacker.Core.Definitions.Game.XNA 3 | { 4 | [XnbType("Microsoft.Xna.Framework.Graphics.SpriteFont")] 5 | [XnbReaderType("Microsoft.Xna.Framework.Content.SpriteFontReader")] 6 | public class SpriteFont 7 | { 8 | [XnbProperty(UseConverter = true)] 9 | public Texture2D Texture { get; set; } = new(); 10 | 11 | [XnbProperty(UseConverter = true)] 12 | public List GlyphBounds { get; set; } = new(); 13 | 14 | [XnbProperty(UseConverter = true)] 15 | public List Cropping { get; set; } = new(); 16 | 17 | [XnbProperty(UseConverter = true)] 18 | public List Characters { get; set; } = new(); 19 | 20 | [XnbProperty] 21 | public int LineSpacing { get; set; } 22 | 23 | [XnbProperty] 24 | public float Spacing { get; set; } 25 | 26 | [XnbProperty(UseConverter = true)] 27 | public List KerningData { get; set; } = new(); 28 | 29 | [XnbProperty(UseConverter = true, Optional = true)] 30 | public char DefaultCharacter { get; set; } = '\u0000'; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XNA/SurfaceFormat.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.XNA 2 | { 3 | [XnbType("Microsoft.Xna.Framework.Graphics.SurfaceFormat")] 4 | public enum SurfaceFormat 5 | { 6 | Color = 0, 7 | Bgr565 = 1, 8 | Bgra5551 = 2, 9 | Bgra4444 = 3, 10 | Dxt1 = 4, 11 | Dxt3 = 5, 12 | Dxt5 = 6, 13 | NormalizedByte2 = 7, 14 | NormalizedByte4 = 8, 15 | Rgba1010102 = 9, 16 | Rg32 = 10, 17 | Rgba64 = 11, 18 | Alpha8 = 12, 19 | Single = 13, 20 | Vector2 = 14, 21 | Vector4 = 15, 22 | HalfSingle = 16, 23 | HalfVector2 = 17, 24 | HalfVector4 = 18, 25 | HdrBlendable = 19, 26 | RgbPvrtc2Bpp = 50, 27 | RgbPvrtc4Bpp = 51, 28 | RgbaPvrtc2Bpp = 52, 29 | RgbaPvrtc4Bpp = 53, 30 | RgbEtc1 = 60, 31 | } 32 | } -------------------------------------------------------------------------------- /Core/Definitions/Game/XNA/Texture2D.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.XNA 2 | { 3 | [XnbType("Microsoft.Xna.Framework.Graphics.Texture2D")] 4 | [XnbReaderType("Microsoft.Xna.Framework.Content.Texture2DReader")] 5 | public class Texture2D 6 | { 7 | [XnbProperty(UseConverter = true, SkipIdentifier = true)] 8 | public SurfaceFormat Format { get; set; } = SurfaceFormat.Color; 9 | 10 | [XnbProperty] 11 | public int Width { get; set; } 12 | 13 | [XnbProperty] 14 | public int Height { get; set; } 15 | 16 | [XnbProperty] 17 | public int MipmapLevels { get; set; } = 1; 18 | 19 | // the format technically supports multiple mipmaps levels, which would alter how 20 | // this data is stored, but none of FEZ's original assets are using additional 21 | // mipmap levels, so I'm dropping support for it completely. 22 | [XnbProperty(UseConverter = true, SkipIdentifier = true)] 23 | public byte[] TextureData { get; set; } = { }; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XNA/Vector2.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.XNA 2 | { 3 | [XnbType("Microsoft.Xna.Framework.Vector2")] 4 | [XnbReaderType("Microsoft.Xna.Framework.Content.Vector2Reader")] 5 | public struct Vector2 : IEquatable 6 | { 7 | [XnbProperty] 8 | public float X { get; set; } 9 | 10 | [XnbProperty] 11 | public float Y { get; set; } 12 | 13 | public Vector2() { } 14 | 15 | public Vector2(float x, float y) 16 | { 17 | X = x; 18 | Y = y; 19 | } 20 | 21 | public bool Equals(Vector2 other) => 22 | this.X == other.X && this.Y == other.Y; 23 | public static bool operator ==(Vector2 left, Vector2 right) => left.Equals(right); 24 | public static bool operator !=(Vector2 left, Vector2 right) => !left.Equals(right); 25 | 26 | public override int GetHashCode() 27 | { 28 | return this.X.GetHashCode() ^ this.Y.GetHashCode() << 2; 29 | } 30 | 31 | public override bool Equals(object obj) 32 | { 33 | if (!(obj is Vector2)) return false; 34 | return Equals((Vector2)obj); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XNA/Vector3.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.XNA 2 | { 3 | [XnbType("Microsoft.Xna.Framework.Vector3")] 4 | [XnbReaderType("Microsoft.Xna.Framework.Content.Vector3Reader")] 5 | public struct Vector3 : IEquatable 6 | { 7 | [XnbProperty] 8 | public float X { get; set; } 9 | 10 | [XnbProperty] 11 | public float Y { get; set; } 12 | 13 | [XnbProperty] 14 | public float Z { get; set; } 15 | 16 | public static Vector3 Zero => new Vector3(0.0f, 0.0f, 0.0f); 17 | public static Vector3 One => new Vector3(1.0f, 1.0f, 1.0f); 18 | 19 | public Vector3() { } 20 | 21 | public Vector3(float x, float y, float z) 22 | { 23 | X = x; 24 | Y = y; 25 | Z = z; 26 | } 27 | 28 | public float Length() => (float)Math.Sqrt(Dot(this,this)); 29 | public static float Dot(Vector3 v, Vector3 w) => v.X * w.X + v.Y * w.Y + v.Z * w.Z; 30 | 31 | public static Vector3 operator +(Vector3 v) => v; 32 | public static Vector3 operator -(Vector3 v) => new Vector3(-v.X, -v.Y, -v.Z); 33 | 34 | public static Vector3 operator *(Vector3 v, float f) => new Vector3(v.X * f, v.Y * f, v.Z * f); 35 | public static Vector3 operator /(Vector3 v, float f) => new Vector3(v.X / f, v.Y / f, v.Z / f); 36 | 37 | public static Vector3 operator +(Vector3 v, Vector3 w) => new Vector3(v.X + w.X, v.Y + w.Y, v.Z + w.Y); 38 | public static Vector3 operator -(Vector3 v, Vector3 w) => new Vector3(v.X - w.X, v.Y - w.Y, v.Z - w.Z); 39 | public static Vector3 operator *(Vector3 v, Vector3 w) => new Vector3(v.X * w.X, v.Y * w.Y, v.Z * w.Y); 40 | public static Vector3 operator /(Vector3 v, Vector3 w) => new Vector3(v.X / w.X, v.Y / w.Y, v.Z / w.Z); 41 | 42 | public bool Equals(Vector3 other) => 43 | this.X == other.X && this.Y == other.Y && this.Z == other.Z; 44 | public static bool operator ==(Vector3 left, Vector3 right) => left.Equals(right); 45 | public static bool operator !=(Vector3 left, Vector3 right) => !left.Equals(right); 46 | 47 | public override int GetHashCode() 48 | { 49 | return this.X.GetHashCode() ^ this.Y.GetHashCode() << 2 ^ this.Z.GetHashCode() >> 2; 50 | } 51 | 52 | public override bool Equals(object obj) 53 | { 54 | if (!(obj is Vector3)) return false; 55 | return Equals((Vector3)obj); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XNA/Vector4.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Game.XNA 2 | { 3 | [XnbType("Microsoft.Xna.Framework.Vector4")] 4 | [XnbReaderType("Microsoft.Xna.Framework.Content.Vector4Reader")] 5 | public struct Vector4 : IEquatable 6 | { 7 | [XnbProperty] 8 | public float X { get; set; } 9 | 10 | [XnbProperty] 11 | public float Y { get; set; } 12 | 13 | [XnbProperty] 14 | public float Z { get; set; } 15 | 16 | [XnbProperty] 17 | public float W { get; set; } 18 | 19 | public Vector4() { } 20 | 21 | public Vector4(float w, float x, float y, float z) 22 | { 23 | W = w; 24 | X = x; 25 | Y = y; 26 | Z = z; 27 | } 28 | 29 | public bool Equals(Vector4 other) => 30 | this.X == other.X && this.Y == other.Y && this.Z == other.Z && this.W == other.W; 31 | public static bool operator ==(Vector4 left, Vector4 right) => left.Equals(right); 32 | public static bool operator !=(Vector4 left, Vector4 right) => !left.Equals(right); 33 | 34 | public override int GetHashCode() 35 | { 36 | return this.X.GetHashCode() ^ this.Y.GetHashCode() << 2 ^ this.Z.GetHashCode() >> 2 ^ this.Z.GetHashCode() << 4; 37 | } 38 | 39 | public override bool Equals(object obj) 40 | { 41 | if (!(obj is Vector4)) return false; 42 | return Equals((Vector4)obj); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XnbPropertyAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game 4 | { 5 | /// 6 | /// Used to assign conversion method for content type property. 7 | /// For more details, see GenericContentType implementation. 8 | /// 9 | [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] 10 | internal sealed class XnbPropertyAttribute : Attribute 11 | { 12 | public int Order { get; private set; } 13 | public bool UseConverter { get; set; } 14 | public bool Optional { get; set; } 15 | public bool SkipIdentifier { get; set; } 16 | 17 | public XnbPropertyAttribute( 18 | [CallerLineNumber] int _order = 0 19 | ) 20 | { 21 | Order = _order; 22 | } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XnbReaderTypeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game 4 | { 5 | /// 6 | /// Used to store information about type readers declared in XNB files. 7 | /// XNB files use reader type assembly qualifers to identify types which 8 | /// aren't handled by generic readers (like enums). 9 | /// 10 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] 11 | internal sealed class XnbReaderTypeAttribute : Attribute 12 | { 13 | public XnbAssemblyQualifier Qualifier { get; set; } 14 | 15 | public XnbReaderTypeAttribute(string _qualifier) 16 | { 17 | Qualifier = _qualifier; 18 | } 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Core/Definitions/Game/XnbTypeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace FEZRepacker.Core.Definitions.Game 4 | { 5 | /// 6 | /// Used to declare custom properties for a content type conversion. 7 | /// Instead of matching namespaces and specification, each content type has an assembly 8 | /// qualifier structure containing name actually used within the XNB file. 9 | /// 10 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] 11 | internal sealed class XnbTypeAttribute : Attribute 12 | { 13 | public XnbAssemblyQualifier Qualifier { get; set; } 14 | 15 | public XnbTypeAttribute(string _qualifier) 16 | { 17 | Qualifier = _qualifier; 18 | } 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Core/Definitions/Json/JsonModel.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.Definitions.Json 2 | { 3 | public interface JsonModel 4 | { 5 | public void SerializeFrom(T data); 6 | public T Deserialize(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Core/Definitions/Json/MapNodeConnectionJsonModel.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.Definitions.Game.Common; 3 | using FEZRepacker.Core.Definitions.Game.MapTree; 4 | 5 | namespace FEZRepacker.Core.Definitions.Json 6 | { 7 | public class MapNodeConnectionJsonModel : JsonModel 8 | { 9 | public FaceOrientation Face { get; set; } 10 | public int Node { get; set; } 11 | public float BranchOversize { get; set; } 12 | 13 | public MapNodeConnectionJsonModel() 14 | { 15 | 16 | } 17 | 18 | public MapNodeConnectionJsonModel(MapNodeConnection data) : this() 19 | { 20 | SerializeFrom(data); 21 | } 22 | 23 | public MapNodeConnection Deserialize() 24 | { 25 | return new() 26 | { 27 | Face = Face, 28 | BranchOversize = BranchOversize 29 | }; 30 | } 31 | 32 | public void SerializeFrom(MapNodeConnection data) 33 | { 34 | Face = data.Face; 35 | BranchOversize = data.BranchOversize; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Core/Definitions/Json/MapNodeJsonModel.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.Definitions.Game.Common; 3 | using FEZRepacker.Core.Definitions.Game.MapTree; 4 | 5 | namespace FEZRepacker.Core.Definitions.Json 6 | { 7 | public class MapNodeJsonModel : JsonModel 8 | { 9 | public string LevelName { get; set; } 10 | public LevelNodeType NodeType { get; set; } 11 | public WinConditions Conditions { get; set; } 12 | public bool HasLesserGate { get; set; } 13 | public bool HasWarpGate { get; set; } 14 | public List Connections { get; set; } 15 | 16 | public MapNodeJsonModel() 17 | { 18 | LevelName = ""; 19 | Conditions = new(); 20 | Connections = new(); 21 | } 22 | 23 | public MapNodeJsonModel(MapNode data) : this() 24 | { 25 | SerializeFrom(data); 26 | } 27 | 28 | public MapNode Deserialize() 29 | { 30 | return new() 31 | { 32 | LevelName = LevelName, 33 | NodeType = NodeType, 34 | Conditions = Conditions, 35 | HasLesserGate = HasLesserGate, 36 | HasWarpGate = HasWarpGate 37 | }; 38 | } 39 | 40 | public void SerializeFrom(MapNode data) 41 | { 42 | LevelName = data.LevelName; 43 | NodeType = data.NodeType; 44 | Conditions = data.Conditions; 45 | HasLesserGate = data.HasLesserGate; 46 | HasWarpGate = data.HasWarpGate; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Core/Definitions/Json/MapTreeJsonModel.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.Definitions.Game.MapTree; 3 | 4 | namespace FEZRepacker.Core.Definitions.Json 5 | { 6 | public class MapTreeJsonModel : Dictionary, JsonModel 7 | { 8 | public MapTree Deserialize() 9 | { 10 | var mapTree = new MapTree(); 11 | 12 | var nodesToConvert = new Dictionary() 13 | { 14 | { 0, mapTree.Root } 15 | }; 16 | 17 | var convertedNodes = new List() { 0 }; 18 | 19 | while (nodesToConvert.Count > 0) 20 | { 21 | var newNodesToConvert = new Dictionary(); 22 | 23 | foreach (var nodeRecord in nodesToConvert) 24 | { 25 | var modNode = this[nodeRecord.Key]; 26 | nodeRecord.Value.LevelName = modNode.LevelName; 27 | nodeRecord.Value.NodeType = modNode.NodeType; 28 | nodeRecord.Value.Conditions = modNode.Conditions; 29 | nodeRecord.Value.HasLesserGate = modNode.HasLesserGate; 30 | nodeRecord.Value.HasWarpGate = modNode.HasWarpGate; 31 | 32 | foreach (var modConnection in modNode.Connections) 33 | { 34 | if (convertedNodes.Contains(modConnection.Node)) continue; 35 | convertedNodes.Add(nodeRecord.Key); 36 | 37 | var connection = new MapNodeConnection() 38 | { 39 | Face = modConnection.Face, 40 | BranchOversize = modConnection.BranchOversize, 41 | Node = new MapNode() 42 | }; 43 | 44 | newNodesToConvert[modConnection.Node] = connection.Node; 45 | nodeRecord.Value.Connections.Add(connection); 46 | } 47 | } 48 | 49 | nodesToConvert = newNodesToConvert; 50 | } 51 | 52 | return mapTree; 53 | } 54 | 55 | public void SerializeFrom(MapTree data) 56 | { 57 | var nodesToConvert = UnpackMapNodes(data.Root); 58 | 59 | var convertedIndexedNodes = nodesToConvert.Select(node => new MapNodeJsonModel(node) 60 | { 61 | Connections = node.Connections.Select(conn => new MapNodeConnectionJsonModel(conn) 62 | { 63 | Node = nodesToConvert.FindIndex(node => node == conn.Node) 64 | }).ToList() 65 | }).Select((node, index) => (Node: node, Index: index)); 66 | 67 | foreach ((var node, var index) in convertedIndexedNodes) this[index] = node; 68 | } 69 | 70 | private List UnpackMapNodes(MapNode rootNode) 71 | { 72 | var nodesToConvert = new List(); 73 | var nodesToUnpack = new List() { rootNode }; 74 | 75 | while (nodesToUnpack.Count > 0) 76 | { 77 | nodesToConvert.AddRange(nodesToUnpack); 78 | 79 | nodesToUnpack = nodesToUnpack 80 | .SelectMany(node => node.Connections) 81 | .Select(conn => conn.Node).ToList(); 82 | } 83 | 84 | return nodesToConvert; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Core/Definitions/Json/SpriteFontPropertiesJsonModel.cs: -------------------------------------------------------------------------------- 1 |  2 | 3 | using FEZRepacker.Core.Definitions.Game.XNA; 4 | 5 | namespace FEZRepacker.Core.Definitions.Json 6 | { 7 | public class SpriteFontPropertiesJsonModel : JsonModel 8 | { 9 | public class CharacterData 10 | { 11 | public char Character; 12 | public Rectangle GlyphBounds = new(); 13 | public Rectangle Cropping = new(); 14 | public Vector3 KerningData; 15 | } 16 | 17 | public int LineSpacing { get; set; } 18 | public float Spacing { get; set; } 19 | public char DefaultCharacter { get; set; } 20 | public List Characters { get; set; } 21 | 22 | public SpriteFontPropertiesJsonModel() 23 | { 24 | Characters = new(); 25 | } 26 | 27 | public SpriteFontPropertiesJsonModel(SpriteFont spriteFont) : this() 28 | { 29 | SerializeFrom(spriteFont); 30 | } 31 | 32 | public SpriteFont Deserialize() 33 | { 34 | var font = new SpriteFont(); 35 | 36 | font.LineSpacing = this.LineSpacing; 37 | font.Spacing = this.Spacing; 38 | font.DefaultCharacter = this.DefaultCharacter; 39 | 40 | foreach (var character in this.Characters) 41 | { 42 | font.Characters.Add(character.Character); 43 | font.GlyphBounds.Add(character.GlyphBounds); 44 | font.Cropping.Add(character.Cropping); 45 | font.KerningData.Add(character.KerningData); 46 | } 47 | 48 | return font; 49 | } 50 | 51 | public void SerializeFrom(SpriteFont font) 52 | { 53 | LineSpacing = font.LineSpacing; 54 | Spacing = font.Spacing; 55 | DefaultCharacter = font.DefaultCharacter; 56 | 57 | for (int i = 0; i < font.Characters.Count; i++) 58 | { 59 | var character = new CharacterData(); 60 | character.Character = font.Characters[i]; 61 | character.GlyphBounds = font.GlyphBounds[i]; 62 | character.Cropping = font.Cropping[i]; 63 | character.KerningData = font.KerningData[i]; 64 | 65 | Characters.Add(character); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Core/Definitions/Json/TrileGroupJsonModel.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.Definitions.Game.Common; 3 | using FEZRepacker.Core.Definitions.Game.Level; 4 | using FEZRepacker.Core.Definitions.Game.XNA; 5 | 6 | namespace FEZRepacker.Core.Definitions.Json 7 | { 8 | // storing TrileEmplacements instead of TrileInstances since the game is using 9 | // just that to identify TrileInstance from Level's Triles dictionary anyway. 10 | public class TrileGroupJsonModel : JsonModel 11 | { 12 | public List Triles { get; set; } 13 | public MovementPath Path { get; set; } 14 | public bool Heavy { get; set; } 15 | public ActorType ActorType { get; set; } 16 | public float GeyserOffset { get; set; } 17 | public float GeyserPauseFor { get; set; } 18 | public float GeyserLiftFor { get; set; } 19 | public float GeyserApexHeight { get; set; } 20 | public Vector3 SpinCenter { get; set; } 21 | public bool SpinClockwise { get; set; } 22 | public float SpinFrequency { get; set; } 23 | public bool SpinNeedsTriggering { get; set; } 24 | public bool Spin180Degrees { get; set; } 25 | public bool FallOnRotate { get; set; } 26 | public float SpinOffset { get; set; } 27 | public string AssociatedSound { get; set; } 28 | 29 | public TrileGroupJsonModel() 30 | { 31 | Triles = new(); 32 | Path = new(); 33 | AssociatedSound = ""; 34 | } 35 | public TrileGroupJsonModel(TrileGroup data) : this() 36 | { 37 | SerializeFrom(data); 38 | } 39 | 40 | public TrileGroup Deserialize() 41 | { 42 | var trileGroup = new TrileGroup() 43 | { 44 | Path = Path, 45 | Heavy = Heavy, 46 | ActorType = ActorType, 47 | GeyserOffset = GeyserOffset, 48 | GeyserPauseFor = GeyserPauseFor, 49 | GeyserLiftFor = GeyserLiftFor, 50 | GeyserApexHeight = GeyserApexHeight, 51 | SpinCenter = SpinCenter, 52 | SpinClockwise = SpinClockwise, 53 | SpinFrequency = SpinFrequency, 54 | SpinNeedsTriggering = SpinNeedsTriggering, 55 | Spin180Degrees = Spin180Degrees, 56 | FallOnRotate = FallOnRotate, 57 | SpinOffset = SpinOffset, 58 | AssociatedSound = AssociatedSound, 59 | }; 60 | 61 | trileGroup.Triles = Triles 62 | .Select(x => new TrileInstance() { Position = new(x.X, x.Y, x.Z) }) 63 | .ToList(); 64 | 65 | return trileGroup; 66 | } 67 | 68 | private void CopyBasicPropertiesOverFrom(TrileGroup trileGroup) 69 | { 70 | Path = trileGroup.Path!; 71 | Heavy = trileGroup.Heavy; 72 | ActorType = trileGroup.ActorType; 73 | GeyserOffset = trileGroup.GeyserOffset; 74 | GeyserPauseFor = trileGroup.GeyserPauseFor; 75 | GeyserLiftFor = trileGroup.GeyserLiftFor; 76 | GeyserApexHeight = trileGroup.GeyserApexHeight; 77 | SpinCenter = trileGroup.SpinCenter; 78 | SpinClockwise = trileGroup.SpinClockwise; 79 | SpinFrequency = trileGroup.SpinFrequency; 80 | SpinNeedsTriggering = trileGroup.SpinNeedsTriggering; 81 | Spin180Degrees = trileGroup.Spin180Degrees; 82 | FallOnRotate = trileGroup.FallOnRotate; 83 | SpinOffset = trileGroup.SpinOffset; 84 | AssociatedSound = trileGroup.AssociatedSound; 85 | } 86 | 87 | public void SerializeFrom(TrileGroup trileGroup) 88 | { 89 | CopyBasicPropertiesOverFrom(trileGroup); 90 | 91 | Triles = trileGroup.Triles.Select(x => new TrileEmplacement(x.Position)).ToList(); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Core/Definitions/Json/TrileInstanceJsonModel.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.Definitions.Game.Level; 3 | using FEZRepacker.Core.Definitions.Game.XNA; 4 | 5 | namespace FEZRepacker.Core.Definitions.Json 6 | { 7 | public class TrileInstanceJsonModel : JsonModel 8 | { 9 | public TrileEmplacement Emplacement { get; set; } 10 | public Vector3 Position { get; set; } 11 | public byte Phi { get; set; } 12 | public int Id { get; set; } 13 | public TrileInstanceActorSettings? ActorSettings { get; set; } 14 | 15 | public TrileInstanceJsonModel() 16 | { 17 | Emplacement = new(); 18 | } 19 | 20 | public TrileInstanceJsonModel(TrileEmplacement emplacement) : this() 21 | { 22 | Emplacement = emplacement; 23 | } 24 | 25 | public TrileInstanceJsonModel(TrileEmplacement emplacement, TrileInstance instance) : this(emplacement) 26 | { 27 | SerializeFrom(instance); 28 | } 29 | 30 | public TrileInstance Deserialize() 31 | { 32 | return new() 33 | { 34 | Position = Position, 35 | PhiLight = Phi, 36 | TrileId = Id, 37 | ActorSettings = ActorSettings 38 | }; 39 | } 40 | 41 | public void SerializeFrom(TrileInstance data) 42 | { 43 | Position = data.Position; 44 | Phi = data.PhiLight; 45 | Id = data.TrileId; 46 | ActorSettings = data.ActorSettings; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Core/FEZRepacker.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | netstandard2.0 6 | latest 7 | 8 | enable 9 | enable 10 | 11 | true 12 | 13 | Debug;Release 14 | 15 | 1.1.2 16 | 17 | True 18 | 19 | FEZRepacker.Core 20 | FEZRepacker.Core 21 | Krzyhau 22 | FEZModding 23 | Conversion pipeline for FEZ game assets. 24 | https://github.com/FEZModding/FEZRepacker 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Core/FileSystem/PakFileRecord.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.FileSystem 2 | { 3 | public class PakFileRecord 4 | { 5 | private readonly string path; 6 | private byte[] payload; 7 | private bool containsData; 8 | 9 | public string Path => path; 10 | public int Length => payload.Length; 11 | public byte[] Payload => payload; 12 | 13 | public PakFileRecord(string path) 14 | { 15 | this.path = path; 16 | this.payload = []; 17 | this.containsData = false; 18 | } 19 | 20 | public PakFileRecord(string path, byte[] payload) 21 | { 22 | this.path = path; 23 | this.payload = payload; 24 | this.containsData = true; 25 | } 26 | 27 | public Stream Open() 28 | { 29 | if (!containsData) 30 | { 31 | return new RecordWriterStream(this); 32 | } 33 | else 34 | { 35 | return new MemoryStream(payload); 36 | } 37 | } 38 | 39 | public string FindExtension() 40 | { 41 | return FindExtensionFromPayload(payload); 42 | } 43 | 44 | public static string FindExtensionFromPayload(byte[] payload) 45 | { 46 | var extension = ""; 47 | 48 | if (payload.Length >= 4) 49 | { 50 | if (payload[0] == 'X' && payload[1] == 'N' && payload[2] == 'B') 51 | { 52 | extension = ".xnb"; 53 | } 54 | else if (payload[0] == 'O' && payload[1] == 'g' && payload[2] == 'g' && payload[3] == 'S') 55 | { 56 | extension = ".ogg"; 57 | } 58 | else if (payload[0] == 0x01 && payload[1] == 0x09 && payload[2] == 0xFF && payload[3] == 0xFE) 59 | { 60 | extension = ".fxc"; 61 | } 62 | } 63 | 64 | return extension; 65 | } 66 | 67 | private class RecordWriterStream : MemoryStream 68 | { 69 | private readonly PakFileRecord fileRecord; 70 | public RecordWriterStream(PakFileRecord record) : base() 71 | { 72 | fileRecord = record; 73 | } 74 | public override void Close() 75 | { 76 | fileRecord.payload = ToArray(); 77 | fileRecord.containsData = true; 78 | base.Close(); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Core/FileSystem/PakPackage.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace FEZRepacker.Core.FileSystem 4 | { 5 | /// 6 | /// A class representing a FEZ PAK package. 7 | /// Use this for convenient file I/O with PAK package. Can be memory inefficient. 8 | /// 9 | public class PakPackage 10 | { 11 | private readonly List entries; 12 | public List Entries => entries; 13 | 14 | public PakPackage() 15 | { 16 | entries = new(); 17 | } 18 | 19 | public PakPackage(Stream stream) : this() 20 | { 21 | LoadEntriesFrom(stream); 22 | } 23 | 24 | private void LoadEntriesFrom(Stream stream) 25 | { 26 | using var reader = new BinaryReader(stream, Encoding.UTF8, true); 27 | 28 | entries.Clear(); 29 | var entriesCount = reader.ReadUInt32(); 30 | 31 | for (var i = 0; i < entriesCount; i++) 32 | { 33 | string path = reader.ReadString(); 34 | int fileSize = (int)reader.ReadUInt32(); 35 | var data = reader.ReadBytes(fileSize); 36 | 37 | var record = CreateEntry(path); 38 | using var recordStream = record.Open(); 39 | recordStream.Write(data, 0, fileSize); 40 | } 41 | } 42 | 43 | /// 44 | /// Creates a new PAK file record and appends it to the end of this package. 45 | /// 46 | /// Path to give to created file record 47 | public PakFileRecord CreateEntry(string localPath) 48 | { 49 | var record = new PakFileRecord(localPath); 50 | entries.Add(record); 51 | return record; 52 | } 53 | 54 | /// 55 | /// Encodes entire PAK package and its entries into a given stream 56 | /// 57 | public void WriteTo(Stream stream) 58 | { 59 | using var writer = new BinaryWriter(stream, Encoding.UTF8, true); 60 | 61 | writer.Write(entries.Count); 62 | foreach(var entry in entries) 63 | { 64 | writer.Write(entry.Path); 65 | writer.Write((uint)entry.Length); 66 | writer.Write(entry.Payload); 67 | } 68 | } 69 | 70 | /// 71 | /// Encodes entire PAK package and its entries into a file with given file path 72 | /// 73 | public void SaveTo(string filePath) 74 | { 75 | using var stream = File.OpenWrite(filePath); 76 | WriteTo(stream); 77 | } 78 | 79 | public static PakPackage ReadFromFile(string filePath) 80 | { 81 | using var stream = File.OpenRead(filePath); 82 | return new(stream); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Core/FileSystem/PakReader.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace FEZRepacker.Core.FileSystem 4 | { 5 | /// 6 | /// Allows accessing individual files in a FEZ PAK package contained in the given stream. 7 | /// Use this for a memory-efficient access to files in a PAK package. 8 | /// 9 | public class PakReader : IDisposable 10 | { 11 | private readonly BinaryReader reader; 12 | public uint FileCount { get; private set; } 13 | 14 | /// 15 | /// Initializes PAK package reader. 16 | /// 17 | /// A stream to read a package from. 18 | public PakReader(Stream stream) 19 | { 20 | reader = new BinaryReader(stream, Encoding.UTF8, false); 21 | 22 | FileCount = reader.ReadUInt32(); 23 | } 24 | 25 | /// 26 | /// Enumerates files from the package. 27 | /// 28 | /// Enumeration copies the content of the file into a buffer. 29 | /// A list of files contained within the package. 30 | public IEnumerable ReadFiles() 31 | { 32 | for (var i = 0; i < FileCount; i++) 33 | { 34 | string name = reader.ReadString(); 35 | uint fileSize = reader.ReadUInt32(); 36 | var data = reader.ReadBytes((int)fileSize); 37 | 38 | yield return new PakFileRecord(name, data); 39 | } 40 | } 41 | 42 | /// 43 | /// Loads a file from path and uses it to create PAK archive reader. 44 | /// 45 | /// A path of a file to load a stream from. 46 | /// PAK archive reader assigned to a stream of a loaded file. 47 | /// Thrown when given file path does not end with .pak extension 48 | public static PakReader FromFile(string filepath) 49 | { 50 | if (Path.GetExtension(filepath) != ".pak") 51 | { 52 | throw new FormatException("PAK package path must be a .PAK file."); 53 | } 54 | 55 | var fileStream = File.Open(filepath, FileMode.Open); 56 | return new PakReader(fileStream); 57 | } 58 | 59 | public void Dispose() 60 | { 61 | reader.Dispose(); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Core/FileSystem/PakWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace FEZRepacker.Core.FileSystem 4 | { 5 | /// 6 | /// Allows creation of a FEZ PAK package. 7 | /// Use this for a memory-efficient creation process of PAK package. 8 | /// 9 | public class PakWriter : IDisposable 10 | { 11 | 12 | private readonly BinaryWriter writer; 13 | private readonly HashSet writtenFilesRecords; 14 | 15 | private uint? lastWrittenFileCount = null; 16 | private uint currentFileCount = 0; 17 | 18 | public uint FileCount => currentFileCount; 19 | 20 | /// A stream to write PAK package to. 21 | public PakWriter(Stream stream) 22 | { 23 | writer = new BinaryWriter(stream, Encoding.UTF8, false); 24 | writtenFilesRecords = new HashSet(); 25 | 26 | WriteFileCount(); 27 | } 28 | 29 | private void WriteFileCount() 30 | { 31 | if (lastWrittenFileCount == currentFileCount) return; 32 | 33 | var currentPosition = (int)writer.BaseStream.Position; 34 | writer.Seek(0, SeekOrigin.Begin); 35 | writer.Write(currentFileCount); 36 | lastWrittenFileCount = currentFileCount; 37 | 38 | if (currentPosition != 0) 39 | { 40 | writer.Seek(currentPosition, SeekOrigin.Begin); 41 | } 42 | } 43 | 44 | private bool AssureNoDuplicate(string fileRecord) 45 | { 46 | if (writtenFilesRecords.Contains(fileRecord)) 47 | { 48 | return false; 49 | } 50 | writtenFilesRecords.Add(fileRecord); 51 | return true; 52 | } 53 | 54 | private void AppendNewFile(string name, Stream data) 55 | { 56 | writer.Write(name); 57 | writer.Write((uint)data.Length); 58 | data.CopyTo(writer.BaseStream); 59 | currentFileCount++; 60 | } 61 | 62 | /// 63 | /// Appends a file data at the end of PAK package. 64 | /// 65 | /// Path and name to identify the file data with in the package. 66 | /// File data to store in the package. 67 | /// Extension of the file, used to filter out duplicates. 68 | /// True if the file was written successfully, false otherwise. 69 | public bool WriteFile(string name, Stream data, string filterExtension = "") 70 | { 71 | if (!AssureNoDuplicate(name + filterExtension)) 72 | { 73 | return false; 74 | } 75 | AppendNewFile(name, data); 76 | return true; 77 | } 78 | 79 | public void Dispose() 80 | { 81 | WriteFileCount(); 82 | 83 | writer.Dispose(); 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /Core/Helpers/BinaryStreamExtensions.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.XNA; 2 | 3 | namespace FEZRepacker.Core.Helpers 4 | { 5 | internal static class BinaryStreamExtensions 6 | { 7 | public static Vector3 ReadVector3(this BinaryReader reader) 8 | { 9 | return new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); 10 | } 11 | 12 | public static void Write(this BinaryWriter writer, Vector3 vector) 13 | { 14 | writer.Write(vector.X); 15 | writer.Write(vector.Y); 16 | writer.Write(vector.Z); 17 | } 18 | 19 | public static Vector2 ReadVector2(this BinaryReader reader) 20 | { 21 | return new Vector2(reader.ReadSingle(), reader.ReadSingle()); 22 | } 23 | 24 | public static void Write(this BinaryWriter writer, Vector2 vector) 25 | { 26 | writer.Write(vector.X); 27 | writer.Write(vector.Y); 28 | } 29 | 30 | public static Quaternion ReadQuaternion(this BinaryReader reader) 31 | { 32 | return new Quaternion(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); 33 | } 34 | public static void Write(this BinaryWriter writer, Quaternion quaternion) 35 | { 36 | writer.Write(quaternion.X); 37 | writer.Write(quaternion.Y); 38 | writer.Write(quaternion.Z); 39 | writer.Write(quaternion.W); 40 | } 41 | 42 | public static Color ReadColor(this BinaryReader reader) 43 | { 44 | byte r = reader.ReadByte(); 45 | byte g = reader.ReadByte(); 46 | byte b = reader.ReadByte(); 47 | byte a = reader.ReadByte(); 48 | return new Color(r, g, b, a); 49 | } 50 | 51 | public static void Write(this BinaryWriter writer, Color color) 52 | { 53 | writer.Write(color.R); 54 | writer.Write(color.G); 55 | writer.Write(color.B); 56 | writer.Write(color.A); 57 | } 58 | 59 | // Source: hhttps://source.dot.net/#Microsoft.Build.Framework/BinaryReaderExtensions.cs,20 60 | public static int Read7BitEncodedInt(this BinaryReader reader) 61 | { 62 | // Read out an Int32 7 bits at a time. The high bit 63 | // of the byte when on means to continue reading more bytes. 64 | int count = 0; 65 | int shift = 0; 66 | byte b; 67 | do 68 | { 69 | // Check for a corrupted stream. Read a max of 5 bytes. 70 | // In a future version, add a DataFormatException. 71 | if (shift == 5 * 7) // 5 bytes max per Int32, shift += 7 72 | { 73 | throw new FormatException(); 74 | } 75 | 76 | // ReadByte handles end of stream cases for us. 77 | b = reader.ReadByte(); 78 | count |= (b & 0x7F) << shift; 79 | shift += 7; 80 | } while ((b & 0x80) != 0); 81 | return count; 82 | } 83 | 84 | // Source: https://source.dot.net/#Microsoft.Build.Framework/BinaryWriterExtensions.cs,35 85 | public static void Write7BitEncodedInt(this BinaryWriter writer, int value) 86 | { 87 | // Write out an int 7 bits at a time. The high bit of the byte, 88 | // when on, tells reader to continue reading more bytes. 89 | uint v = (uint)value; // support negative numbers 90 | while (v >= 0x80) 91 | { 92 | writer.Write((byte)(v | 0x80)); 93 | v >>= 7; 94 | } 95 | 96 | writer.Write((byte)v); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Core/Helpers/ImageSharpUtils.cs: -------------------------------------------------------------------------------- 1 | using SixLabors.ImageSharp; 2 | using SixLabors.ImageSharp.Formats; 3 | 4 | namespace FEZRepacker.Core.Helpers 5 | { 6 | internal static class ImageSharpUtils 7 | { 8 | public static MemoryStream SaveAsMemoryStream(this Image image, IImageEncoder encoder) 9 | { 10 | var outStream = new MemoryStream(); 11 | image.Save(outStream, encoder); 12 | outStream.Seek(0, SeekOrigin.Begin); 13 | return outStream; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Core/Helpers/Json/ConfiguredJsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Text.Json; 3 | using System.Text.Json.Serialization; 4 | 5 | using FEZRepacker.Core.FileSystem; 6 | using FEZRepacker.Core.Helpers.Json.CustomConverters; 7 | 8 | namespace FEZRepacker.Core.Helpers.Json 9 | { 10 | /// 11 | /// Wrapper for that passes custom options 12 | /// tailored for format conversion. Additionally, supplies some helper 13 | /// methods for interacting directly with 14 | /// 15 | internal static class ConfiguredJsonSerializer 16 | { 17 | static JsonSerializerOptions? _serializerOptions; 18 | 19 | public static string Serialize(T data) 20 | { 21 | return JsonSerializer.Serialize(data, GetOptions()); 22 | } 23 | 24 | public static T Deserialize(string json) 25 | { 26 | return JsonSerializer.Deserialize(json, GetOptions())!; 27 | } 28 | 29 | public static FileBundle SerializeToFileBundle(string secondaryFileFormat, T data) 30 | { 31 | var json = Serialize(data); 32 | var outStream = new MemoryStream(Encoding.UTF8.GetBytes(json)); 33 | return FileBundle.Single(outStream, secondaryFileFormat, ".json"); 34 | } 35 | 36 | public static T DeserializeFromFileBundle(FileBundle bundle) 37 | { 38 | using var inReader = new BinaryReader(bundle.RequireData(".json", ""), Encoding.UTF8, true); 39 | var json = new string(inReader.ReadChars((int)inReader.BaseStream.Length)); 40 | 41 | try 42 | { 43 | return Deserialize(json); 44 | } 45 | catch (JsonException ex) 46 | { 47 | throw new InvalidDataException($"No valid JSON structure in a file bundle: {ex.Message}"); 48 | } 49 | } 50 | 51 | private static JsonSerializerOptions GetOptions() 52 | { 53 | if (_serializerOptions == null) _serializerOptions = new() 54 | { 55 | IncludeFields = true, 56 | WriteIndented = true, 57 | Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, 58 | Converters = { 59 | new JsonStringEnumConverter(), 60 | new Vector2JsonConverter(), 61 | new Vector3JsonConverter(), 62 | new QuaternionJsonConverter(), 63 | new ColorJsonConverter(), 64 | new TimeSpanJsonConverter(), 65 | new TrileEmplacementJsonConverter(), 66 | new TrileEmplacementListJsonConverter(), 67 | new ScriptTriggerJsonConverter(), 68 | new ScriptConditionJsonConverter(), 69 | new ScriptActionJsonConverter(), 70 | } 71 | }; 72 | 73 | return _serializerOptions; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Core/Helpers/Json/CustomConverters/ColorJsonConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Text.Json; 3 | using System.Text.Json.Serialization; 4 | 5 | using FEZRepacker.Core.Definitions.Game.XNA; 6 | 7 | namespace FEZRepacker.Core.Helpers.Json.CustomConverters 8 | { 9 | internal class ColorJsonConverter : JsonConverter 10 | { 11 | public override Color Read( 12 | ref Utf8JsonReader reader, 13 | Type typeToConvert, 14 | JsonSerializerOptions options) 15 | { 16 | if (reader.TokenType != JsonTokenType.String) throw new JsonException(); 17 | 18 | string colorStr = JsonDocument.ParseValue(ref reader).Deserialize() ?? "#00000000"; 19 | return ColorFromHTML(colorStr); 20 | } 21 | 22 | public override void Write( 23 | Utf8JsonWriter writer, 24 | Color color, 25 | JsonSerializerOptions options) 26 | { 27 | string rHex = color.R.ToString("X2"); 28 | string gHex = color.G.ToString("X2"); 29 | string bHex = color.B.ToString("X2"); 30 | string aHex = color.A.ToString("X2"); 31 | 32 | writer.WriteRawValue($"\"#{rHex}{gHex}{bHex}{aHex}\""); 33 | } 34 | 35 | private static Color ColorFromHTML(string htmlCode) 36 | { 37 | string hexCode = htmlCode.Replace("#", ""); 38 | 39 | byte[] values = { 0, 0, 0, 255 }; 40 | int valueSize = hexCode.Length < 6 ? 1 : 2; 41 | if (valueSize == 2 && hexCode.Length % 2 == 1) hexCode += "0"; 42 | int valuesCount = hexCode.Length / valueSize; 43 | for (int i = 0; i < valuesCount; i++) 44 | { 45 | string hexValue = hexCode.Substring(i * valueSize, valueSize); 46 | values[i] = byte.Parse(hexValue, NumberStyles.HexNumber); 47 | } 48 | 49 | return new Color(values[0], values[1], values[2], values[3]); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Core/Helpers/Json/CustomConverters/QuaternionJsonConverter.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Text.Json; 3 | using System.Text.Json.Serialization; 4 | 5 | using FEZRepacker.Core.Definitions.Game.XNA; 6 | 7 | namespace FEZRepacker.Core.Helpers.Json.CustomConverters 8 | { 9 | internal class QuaternionJsonConverter : JsonConverter 10 | { 11 | public override Quaternion Read( 12 | ref Utf8JsonReader reader, 13 | Type typeToConvert, 14 | JsonSerializerOptions options) 15 | { 16 | if (reader.TokenType != JsonTokenType.StartArray) throw new JsonException(); 17 | 18 | reader.Read(); 19 | float x = reader.GetSingle(); 20 | reader.Read(); 21 | float y = reader.GetSingle(); 22 | reader.Read(); 23 | float z = reader.GetSingle(); 24 | reader.Read(); 25 | float w = reader.GetSingle(); 26 | Quaternion q = new Quaternion(x, y, z, w); 27 | reader.Read(); 28 | 29 | if (reader.TokenType != JsonTokenType.EndArray) throw new JsonException(); 30 | 31 | return q; 32 | } 33 | 34 | public override void Write( 35 | Utf8JsonWriter writer, 36 | Quaternion quaternion, 37 | JsonSerializerOptions options) 38 | { 39 | writer.WriteRawValue($"[{quaternion.X}, {quaternion.Y}, {quaternion.Z}, {quaternion.W}]"); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Core/Helpers/Json/CustomConverters/TimeSpanJsonConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace FEZRepacker.Core.Helpers.Json.CustomConverters 5 | { 6 | internal class TimeSpanJsonConverter : JsonConverter 7 | { 8 | public override TimeSpan Read( 9 | ref Utf8JsonReader reader, 10 | Type typeToConvert, 11 | JsonSerializerOptions options) 12 | { 13 | return TimeSpan.FromSeconds(reader.GetDouble()); 14 | } 15 | 16 | public override void Write( 17 | Utf8JsonWriter writer, 18 | TimeSpan timespan, 19 | JsonSerializerOptions options) 20 | { 21 | writer.WriteRawValue($"{timespan.TotalSeconds}"); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Core/Helpers/Json/CustomConverters/TrileEmplacementJsonConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Serialization; 3 | 4 | using FEZRepacker.Core.Definitions.Game.Level; 5 | 6 | namespace FEZRepacker.Core.Helpers.Json.CustomConverters 7 | { 8 | internal class TrileEmplacementJsonConverter : JsonConverter 9 | { 10 | public override TrileEmplacement Read( 11 | ref Utf8JsonReader reader, 12 | Type typeToConvert, 13 | JsonSerializerOptions options) 14 | { 15 | if (reader.TokenType != JsonTokenType.StartArray) throw new JsonException(); 16 | 17 | reader.Read(); 18 | int x = reader.GetInt32(); 19 | reader.Read(); 20 | int y = reader.GetInt32(); 21 | reader.Read(); 22 | int z = reader.GetInt32(); 23 | TrileEmplacement trile = new TrileEmplacement(x, y, z); 24 | reader.Read(); 25 | 26 | if (reader.TokenType != JsonTokenType.EndArray) throw new JsonException(); 27 | 28 | return trile; 29 | } 30 | 31 | public override void Write( 32 | Utf8JsonWriter writer, 33 | TrileEmplacement trilePos, 34 | JsonSerializerOptions options) 35 | { 36 | writer.WriteRawValue($"[{trilePos.X}, {trilePos.Y}, {trilePos.Z}]"); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Core/Helpers/Json/CustomConverters/TrileEmplacementListJsonConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Serialization; 3 | 4 | using FEZRepacker.Core.Definitions.Game.Level; 5 | 6 | 7 | namespace FEZRepacker.Core.Helpers.Json.CustomConverters 8 | { 9 | internal class TrileEmplacementListJsonConverter : JsonConverter> 10 | { 11 | 12 | public override List Read( 13 | ref Utf8JsonReader reader, 14 | Type typeToConvert, 15 | JsonSerializerOptions options) 16 | { 17 | if (reader.TokenType != JsonTokenType.StartArray) throw new JsonException(); 18 | 19 | List trilePoses = new(); 20 | 21 | while (reader.Read()) 22 | { 23 | if (reader.TokenType == JsonTokenType.EndArray) return trilePoses; 24 | 25 | if (reader.TokenType != JsonTokenType.StartArray) throw new JsonException(); 26 | 27 | reader.Read(); 28 | int x = reader.GetInt32(); 29 | reader.Read(); 30 | int y = reader.GetInt32(); 31 | reader.Read(); 32 | int z = reader.GetInt32(); 33 | TrileEmplacement trile = new TrileEmplacement(x, y, z); 34 | 35 | reader.Read(); 36 | if (reader.TokenType != JsonTokenType.EndArray) throw new JsonException(); 37 | 38 | trilePoses.Add(trile); 39 | } 40 | 41 | throw new JsonException(); 42 | } 43 | 44 | public override void Write( 45 | Utf8JsonWriter writer, 46 | List triles, 47 | JsonSerializerOptions options) 48 | { 49 | 50 | writer.WriteStartArray(); 51 | 52 | foreach (var pos in triles) 53 | { 54 | if (options.WriteIndented) 55 | { 56 | writer.WriteRawValue("\n" + new string(' ', writer.CurrentDepth * 2) + $"[{pos.X}, {pos.Y}, {pos.Z}]"); 57 | } 58 | } 59 | 60 | writer.WriteEndArray(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Core/Helpers/Json/CustomConverters/VectorJsonConverters.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Serialization; 3 | 4 | using FEZRepacker.Core.Definitions.Game.XNA; 5 | 6 | namespace FEZRepacker.Core.Helpers.Json.CustomConverters 7 | { 8 | internal class Vector3JsonConverter : JsonConverter 9 | { 10 | public override Vector3 Read( 11 | ref Utf8JsonReader reader, 12 | Type typeToConvert, 13 | JsonSerializerOptions options) 14 | { 15 | if (reader.TokenType != JsonTokenType.StartArray) throw new JsonException(); 16 | 17 | reader.Read(); 18 | float x = reader.GetSingle(); 19 | reader.Read(); 20 | float y = reader.GetSingle(); 21 | reader.Read(); 22 | float z = reader.GetSingle(); 23 | Vector3 v = new Vector3(x, y, z); 24 | reader.Read(); 25 | 26 | if (reader.TokenType != JsonTokenType.EndArray) throw new JsonException(); 27 | 28 | return v; 29 | } 30 | 31 | public override void Write( 32 | Utf8JsonWriter writer, 33 | Vector3 vector, 34 | JsonSerializerOptions options) 35 | { 36 | writer.WriteRawValue($"[{vector.X}, {vector.Y}, {vector.Z}]"); 37 | } 38 | } 39 | 40 | internal class Vector2JsonConverter : JsonConverter 41 | { 42 | public override Vector2 Read( 43 | ref Utf8JsonReader reader, 44 | Type typeToConvert, 45 | JsonSerializerOptions options) 46 | { 47 | if (reader.TokenType != JsonTokenType.StartArray) throw new JsonException(); 48 | 49 | reader.Read(); 50 | float x = reader.GetSingle(); 51 | reader.Read(); 52 | float y = reader.GetSingle(); 53 | Vector2 v = new Vector2(x, y); 54 | reader.Read(); 55 | 56 | if (reader.TokenType != JsonTokenType.EndArray) throw new JsonException(); 57 | 58 | return v; 59 | } 60 | 61 | public override void Write( 62 | Utf8JsonWriter writer, 63 | Vector2 vector, 64 | JsonSerializerOptions options) 65 | { 66 | writer.WriteRawValue($"[{vector.X}, {vector.Y}]"); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Core/Helpers/TrixelArtUtil.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using FEZRepacker.Core.Definitions.Game.ArtObject; 4 | using FEZRepacker.Core.Definitions.Game.Graphics; 5 | using FEZRepacker.Core.Definitions.Game.XNA; 6 | 7 | namespace FEZRepacker.Core.Helpers 8 | { 9 | internal static class TrixelArtUtil 10 | { 11 | public static Dictionary> LoadGeometry(Stream geometryStream) 12 | { 13 | using var geometryReader = new BinaryReader(geometryStream, Encoding.UTF8, true); 14 | string geometryString = new string(geometryReader.ReadChars((int)geometryStream.Length)); 15 | return WavefrontObjUtil.FromWavefrontObj(geometryString); 16 | } 17 | 18 | /// 19 | /// Recalculates texture coordinates to match what game does with them during loading process. 20 | /// 21 | /// Type of modified geometry 22 | /// Geometry to modify 23 | /// Bounds of the geometry to calculate texture coordinates with. 24 | public static void RecalculateCubemapTexCoords(IndexedPrimitives geometry, Vector3 geometryBounds) { 25 | foreach (var vertex in geometry.Vertices) 26 | { 27 | (int textureOffset, Vector3 xAxis, Vector3 yAxis) = vertex.NormalByte switch 28 | { 29 | 5 => (0, new Vector3(1, 0, 0), new Vector3(0, -1, 0)), // front 30 | 3 => (1, new Vector3(0, 0, -1), new Vector3(0, -1, 0)), // right 31 | 2 => (2, new Vector3(-1, 0, 0), new Vector3(0, -1, 0)), // back 32 | 0 => (3, new Vector3(0, 0, 1), new Vector3(0, -1, 0)), // left 33 | 4 => (4, new Vector3(1, 0, 0), new Vector3(0, 0, 1)), // top 34 | 1 => (5, new Vector3(1, 0, 0), new Vector3(0, 0, -1)), // bottom 35 | _ => (0, new Vector3(), new Vector3()) 36 | }; 37 | 38 | var texturePlanePosition = vertex.Position / geometryBounds; 39 | vertex.TextureCoordinate = new Vector2( 40 | (Vector3.Dot(texturePlanePosition, xAxis) + 0.5f + textureOffset) / 6.0f, 41 | Vector3.Dot(texturePlanePosition, yAxis) + 0.5f 42 | ); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Core/XNB/ContentSerialization/System/ArrayContentSerializer.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace FEZRepacker.Core.XNB.ContentSerialization.System 3 | { 4 | internal class ArrayContentSerializer : XnbContentSerializer where T : notnull 5 | { 6 | private XnbAssemblyQualifier _name; 7 | private readonly bool _skipElementType; 8 | public ArrayContentSerializer(bool skipElementType = true) : base() 9 | { 10 | // creating type assembly qualifier name 11 | _name = typeof(ArrayContentSerializer).FullName ?? ""; 12 | _name.Namespace = "Microsoft.Xna.Framework.Content"; 13 | _name.Name = "ArrayReader"; 14 | 15 | var genericQualifier = XnbAssemblyQualifier.GetFromXnbType(typeof(T)); 16 | if (genericQualifier.HasValue) _name.Templates[0] = genericQualifier.Value; 17 | 18 | // some arrays have element type prefixes, some dont. 19 | // i have no idea what's the rule here, im just making it a variable 20 | _skipElementType = skipElementType; 21 | } 22 | 23 | public override XnbAssemblyQualifier Name => _name; 24 | 25 | public override object Deserialize(XnbContentReader reader) 26 | { 27 | int dataCount = reader.ReadInt32(); 28 | T[] data = new T[dataCount]; 29 | for (int i = 0; i < dataCount; i++) 30 | { 31 | T? value = reader.ReadContent(_skipElementType); 32 | if (value != null) data[i] = value; 33 | } 34 | return data; 35 | } 36 | 37 | public override void Serialize(object data, XnbContentWriter writer) 38 | { 39 | T[] array = (T[])data; 40 | 41 | writer.Write(array.Length); 42 | foreach (T value in array) 43 | { 44 | writer.WriteContent(value, _skipElementType); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Core/XNB/ContentSerialization/System/BooleanContentSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.XNB.ContentSerialization.System 2 | { 3 | internal class BooleanContentSerializer : XnbContentSerializer 4 | { 5 | public BooleanContentSerializer() : base() { } 6 | public override XnbAssemblyQualifier Name => "Microsoft.Xna.Framework.Content.BooleanReader"; 7 | 8 | public override object Deserialize(XnbContentReader reader) 9 | { 10 | return (object)reader.ReadBoolean(); 11 | } 12 | 13 | public override void Serialize(object data, XnbContentWriter writer) 14 | { 15 | writer.Write((bool)data); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Core/XNB/ContentSerialization/System/ByteArrayContentSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.XNB.ContentSerialization.System 2 | { 3 | internal class ByteArrayContentSerializer : XnbContentSerializer 4 | { 5 | private XnbAssemblyQualifier _name; 6 | public ByteArrayContentSerializer() : base() 7 | { 8 | _name = typeof(ArrayContentSerializer).FullName ?? ""; 9 | _name.Namespace = "Microsoft.Xna.Framework.Content"; 10 | _name.Name = "ArrayReader"; 11 | IsPrivate = true; 12 | } 13 | 14 | public override XnbAssemblyQualifier Name => _name; 15 | 16 | public override object Deserialize(XnbContentReader reader) 17 | { 18 | int dataCount = reader.ReadInt32(); 19 | byte[] data = reader.ReadBytes(dataCount); 20 | return data; 21 | } 22 | 23 | public override void Serialize(object data, XnbContentWriter writer) 24 | { 25 | byte[] array = (byte[])data; 26 | 27 | writer.Write(array.Length); 28 | writer.Write(array); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Core/XNB/ContentSerialization/System/CharContentSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.XNB.ContentSerialization.System 2 | { 3 | internal class CharContentSerializer : XnbContentSerializer 4 | { 5 | public CharContentSerializer() : base() { } 6 | public override XnbAssemblyQualifier Name => "Microsoft.Xna.Framework.Content.CharReader"; 7 | 8 | public override object Deserialize(XnbContentReader reader) 9 | { 10 | return (object)reader.ReadChar(); 11 | } 12 | 13 | public override void Serialize(object data, XnbContentWriter writer) 14 | { 15 | writer.Write((char)data); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Core/XNB/ContentSerialization/System/DictionaryContentSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.XNB.ContentSerialization.System 2 | { 3 | internal class DictionaryContentSerializer : XnbContentSerializer> where K : notnull 4 | { 5 | private XnbAssemblyQualifier _name; 6 | private bool skipKeyIdentifier; 7 | private bool skipValueIdentifier; 8 | 9 | public DictionaryContentSerializer( 10 | bool skipKeyIdentifier = false, 11 | bool skipValueIdentifier = false 12 | ) : base() 13 | { 14 | // creating type assembly qualifier name, since we're using own types 15 | _name = ContentType.FullName ?? ""; 16 | _name.Namespace = "Microsoft.Xna.Framework.Content"; 17 | _name.Name = "DictionaryReader"; 18 | 19 | var genericKeyQualifier = XnbAssemblyQualifier.GetFromXnbType(typeof(K)); 20 | if (genericKeyQualifier.HasValue) _name.Templates[0] = genericKeyQualifier.Value; 21 | 22 | var genericValueQualifier = XnbAssemblyQualifier.GetFromXnbType(typeof(V)); 23 | if (genericValueQualifier.HasValue) _name.Templates[1] = genericValueQualifier.Value; 24 | 25 | this.skipKeyIdentifier = skipKeyIdentifier; 26 | this.skipValueIdentifier = skipValueIdentifier; 27 | } 28 | 29 | public override XnbAssemblyQualifier Name => _name; 30 | 31 | public override object Deserialize(XnbContentReader reader) 32 | { 33 | Dictionary data = new Dictionary(); 34 | int dataCount = reader.ReadInt32(); 35 | for (int i = 0; i < dataCount; i++) 36 | { 37 | K? key = reader.ReadContent(skipKeyIdentifier); 38 | V? value = reader.ReadContent(skipValueIdentifier); 39 | if (key != null && value != null) data[key] = value; 40 | } 41 | return data; 42 | } 43 | 44 | public override void Serialize(object data, XnbContentWriter writer) 45 | { 46 | Dictionary dict = (Dictionary)data; 47 | 48 | writer.Write(dict.Count); 49 | foreach (var record in dict) 50 | { 51 | writer.WriteContent(record.Key, skipKeyIdentifier); 52 | writer.WriteContent(record.Value, skipValueIdentifier); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Core/XNB/ContentSerialization/System/EnumContentSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.XNB.ContentSerialization.System 2 | { 3 | internal class EnumContentSerializer : XnbContentSerializer where T : Enum 4 | { 5 | private XnbAssemblyQualifier _name; 6 | 7 | public EnumContentSerializer() : base() 8 | { 9 | // creating type assembly qualifier name 10 | _name = typeof(EnumContentSerializer).FullName ?? ""; 11 | _name.Namespace = "Microsoft.Xna.Framework.Content"; 12 | _name.Name = "EnumReader"; 13 | 14 | var enumQualifier = XnbAssemblyQualifier.GetFromXnbType(typeof(T)); 15 | if (enumQualifier.HasValue) _name.Templates[0] = enumQualifier.Value; 16 | } 17 | 18 | public override XnbAssemblyQualifier Name => _name; 19 | 20 | public override object Deserialize(XnbContentReader reader) 21 | { 22 | return reader.ReadInt32(); 23 | } 24 | 25 | public override void Serialize(object data, XnbContentWriter writer) 26 | { 27 | int value = (int)data; 28 | writer.Write(value); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Core/XNB/ContentSerialization/System/Int32ContentSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.XNB.ContentSerialization.System 2 | { 3 | internal class Int32ContentSerializer : XnbContentSerializer 4 | { 5 | public Int32ContentSerializer() : base() { } 6 | public override XnbAssemblyQualifier Name => "Microsoft.Xna.Framework.Content.Int32Reader"; 7 | 8 | public override object Deserialize(XnbContentReader reader) 9 | { 10 | return (object)reader.ReadInt32(); 11 | } 12 | 13 | public override void Serialize(object data, XnbContentWriter writer) 14 | { 15 | writer.Write((int)data); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Core/XNB/ContentSerialization/System/ListContentSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.XNB.ContentSerialization.System 2 | { 3 | internal class ListContentSerializer : XnbContentSerializer> where T : notnull 4 | { 5 | private XnbAssemblyQualifier _name; 6 | private bool _skipElementType; 7 | public ListContentSerializer(bool skipElementType = false) : base() 8 | { 9 | // creating type assembly qualifier name 10 | _name = typeof(ArrayContentSerializer).FullName ?? ""; 11 | _name.Namespace = "Microsoft.Xna.Framework.Content"; 12 | _name.Name = "ListReader"; 13 | 14 | var genericQualifier = XnbAssemblyQualifier.GetFromXnbType(typeof(T)); 15 | if (genericQualifier.HasValue) _name.Templates[0] = genericQualifier.Value; 16 | 17 | // similarly to arrays, elements can have type identifier prefix 18 | // but unlike arrays, this is much less common 19 | // again, barely any idea what's the rule here. 20 | _skipElementType = skipElementType; 21 | } 22 | 23 | public override XnbAssemblyQualifier Name => _name; 24 | 25 | public override object Deserialize(XnbContentReader reader) 26 | { 27 | List data = new List(); 28 | int dataCount = reader.ReadInt32(); 29 | for (int i = 0; i < dataCount; i++) 30 | { 31 | T? value = reader.ReadContent(_skipElementType); 32 | if (value != null) data.Add(value); 33 | } 34 | return data; 35 | } 36 | 37 | public override void Serialize(object data, XnbContentWriter writer) 38 | { 39 | List list = (List)data; 40 | 41 | writer.Write(list.Count); 42 | foreach (T value in list) 43 | { 44 | writer.WriteContent(value, _skipElementType); 45 | } 46 | } 47 | 48 | public override bool IsEmpty(object data) 49 | { 50 | return ((List)data).Count == 0; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Core/XNB/ContentSerialization/System/StringContentSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.XNB.ContentSerialization.System 2 | { 3 | internal class StringContentSerializer : XnbContentSerializer 4 | { 5 | private bool isNullable; 6 | 7 | public StringContentSerializer(bool nullable = true) : base() { 8 | isNullable = nullable; 9 | } 10 | public override XnbAssemblyQualifier Name => "Microsoft.Xna.Framework.Content.StringReader"; 11 | 12 | 13 | public override object Deserialize(XnbContentReader reader) 14 | { 15 | return reader.ReadString(); 16 | } 17 | 18 | public override void Serialize(object data, XnbContentWriter writer) 19 | { 20 | writer.Write((string)data); 21 | } 22 | 23 | public override bool IsEmpty(object data) 24 | { 25 | return isNullable && ((string)data).Length == 0; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Core/XNB/ContentSerialization/System/TimeSpanContentSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.XNB.ContentSerialization.System 2 | { 3 | internal class TimeSpanContentSerializer : XnbContentSerializer 4 | { 5 | public TimeSpanContentSerializer() : base() { } 6 | public override XnbAssemblyQualifier Name => "Microsoft.Xna.Framework.Content.TimeSpanReader"; 7 | 8 | public override object Deserialize(XnbContentReader reader) 9 | { 10 | return new TimeSpan(reader.ReadInt64()); 11 | } 12 | 13 | public override void Serialize(object data, XnbContentWriter writer) 14 | { 15 | writer.Write(((TimeSpan)data).Ticks); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Core/XNB/ContentSerialization/System/UInt16ContentSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.XNB.ContentSerialization.System 2 | { 3 | internal class UInt16ContentSerializer : XnbContentSerializer 4 | { 5 | public UInt16ContentSerializer() : base() { } 6 | public override XnbAssemblyQualifier Name => "Microsoft.Xna.Framework.Content.UInt16Reader"; 7 | 8 | public override object Deserialize(XnbContentReader reader) 9 | { 10 | return (object)reader.ReadUInt16(); 11 | } 12 | 13 | public override void Serialize(object data, XnbContentWriter writer) 14 | { 15 | writer.Write((ushort)data); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Core/XNB/ContentSerialization/XnbContentSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.XNB.ContentSerialization 2 | { 3 | /// 4 | /// Gives information and read/write methods for specific content types in XNB files. 5 | /// 6 | internal abstract class XnbContentSerializer 7 | { 8 | public abstract XnbAssemblyQualifier Name { get; } 9 | public abstract Type ContentType { get; } 10 | public bool IsPrivate { get; protected set; } 11 | 12 | public XnbContentSerializer() 13 | { 14 | IsPrivate = false; 15 | } 16 | /// 17 | /// Uses given binary reader to read an object of implemented content type. 18 | /// 19 | /// Binary reader to read an object from. 20 | /// Object of type defined in this content type structure. 21 | public abstract object Deserialize(XnbContentReader reader); 22 | 23 | /// 24 | /// Writes given object into a given binary writer. 25 | /// 26 | /// Object to write, preferably of the type defined in this content type structure. 27 | /// Binary writer to write an object to. 28 | public abstract void Serialize(object data, XnbContentWriter writer); 29 | 30 | /// 31 | /// Used to determine whether an object of this content type is empty. 32 | /// Used by a converter to read/write nullable types. 33 | /// 34 | /// 35 | /// 36 | public virtual bool IsEmpty(object data) 37 | { 38 | return false; 39 | } 40 | } 41 | 42 | /// 43 | /// Helper class for XnbContentType to automatically assign BasicType based on given template. 44 | /// 45 | /// Type to use for BasicType field. 46 | internal abstract class XnbContentSerializer : XnbContentSerializer 47 | { 48 | public override Type ContentType => typeof(T); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Core/XNB/ContentTypes/AnimatedTextureContentIdentity.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.XNB.ContentSerialization; 2 | using FEZRepacker.Core.XNB.ContentSerialization.System; 3 | 4 | using FEZRepacker.Core.Definitions.Game.Graphics; 5 | using FEZRepacker.Core.Definitions.Game.XNA; 6 | 7 | namespace FEZRepacker.Core.XNB.ContentTypes 8 | { 9 | internal class AnimatedTextureContentIdentity : XnbPrimaryContentIdentity 10 | { 11 | protected override List ContentTypesFactory => new() 12 | { 13 | new GenericContentSerializer(), 14 | new ByteArrayContentSerializer(), 15 | new ListContentSerializer(), 16 | new GenericContentSerializer(), 17 | new TimeSpanContentSerializer(), 18 | new GenericContentSerializer() 19 | }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Core/XNB/ContentTypes/ArtObjectContentIdentity.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.Definitions.Game.ArtObject; 3 | using FEZRepacker.Core.Definitions.Game.Common; 4 | using FEZRepacker.Core.Definitions.Game.Graphics; 5 | using FEZRepacker.Core.Definitions.Game.XNA; 6 | using FEZRepacker.Core.XNB.ContentSerialization; 7 | using FEZRepacker.Core.XNB.ContentSerialization.System; 8 | 9 | namespace FEZRepacker.Core.XNB.ContentTypes 10 | { 11 | internal class ArtObjectContentIdentity : XnbPrimaryContentIdentity 12 | { 13 | protected override List ContentTypesFactory => new() 14 | { 15 | new GenericContentSerializer(), 16 | new GenericContentSerializer(), 17 | new EnumContentSerializer(), 18 | new ByteArrayContentSerializer(), 19 | new GenericContentSerializer>(), 20 | new GenericContentSerializer(), 21 | new GenericContentSerializer(), 22 | new EnumContentSerializer(), 23 | new Int32ContentSerializer(), 24 | new ArrayContentSerializer(), 25 | new ArrayContentSerializer(), 26 | new UInt16ContentSerializer(), 27 | new EnumContentSerializer() 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Core/XNB/ContentTypes/EffectContentIdentity.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.Definitions.Game.XNA; 3 | using FEZRepacker.Core.XNB.ContentSerialization; 4 | using FEZRepacker.Core.XNB.ContentSerialization.System; 5 | 6 | namespace FEZRepacker.Core.XNB.ContentTypes 7 | { 8 | internal class EffectContentIdentity : XnbPrimaryContentIdentity 9 | { 10 | protected override List ContentTypesFactory => new() 11 | { 12 | new GenericContentSerializer(), 13 | new ByteArrayContentSerializer() 14 | }; 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Core/XNB/ContentTypes/MapContentIdentity.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.Definitions.Game.Common; 2 | using FEZRepacker.Core.Definitions.Game.MapTree; 3 | using FEZRepacker.Core.XNB.ContentSerialization; 4 | using FEZRepacker.Core.XNB.ContentSerialization.System; 5 | 6 | namespace FEZRepacker.Core.XNB.ContentTypes 7 | { 8 | internal class MapContentIdentity : XnbPrimaryContentIdentity 9 | { 10 | protected override List ContentTypesFactory => new() 11 | { 12 | new GenericContentSerializer(), 13 | new GenericContentSerializer(), 14 | new ListContentSerializer(), 15 | new GenericContentSerializer(), 16 | new EnumContentSerializer(), 17 | new Int32ContentSerializer(), 18 | new EnumContentSerializer(), 19 | new GenericContentSerializer(), 20 | new ListContentSerializer() 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Core/XNB/ContentTypes/NpcMetadataContentIdentity.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.Definitions.Game.Common; 3 | using FEZRepacker.Core.Definitions.Game.NpcMetadata; 4 | using FEZRepacker.Core.XNB.ContentSerialization; 5 | using FEZRepacker.Core.XNB.ContentSerialization.System; 6 | 7 | namespace FEZRepacker.Core.XNB.ContentTypes 8 | { 9 | internal class NpcMetadataContentIdentity : XnbPrimaryContentIdentity 10 | { 11 | protected override List ContentTypesFactory => new() 12 | { 13 | new GenericContentSerializer(), 14 | new StringContentSerializer(), 15 | new ListContentSerializer(true), 16 | new EnumContentSerializer(), 17 | new Int32ContentSerializer() 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Core/XNB/ContentTypes/SkyContentIdentity.cs: -------------------------------------------------------------------------------- 1 |  2 | 3 | using FEZRepacker.Core.Definitions.Game.Sky; 4 | using FEZRepacker.Core.XNB.ContentSerialization; 5 | using FEZRepacker.Core.XNB.ContentSerialization.System; 6 | 7 | namespace FEZRepacker.Core.XNB.ContentTypes 8 | { 9 | internal class SkyContentIdentity : XnbPrimaryContentIdentity 10 | { 11 | protected override List ContentTypesFactory => new() 12 | { 13 | new GenericContentSerializer(), 14 | new ListContentSerializer(), 15 | new GenericContentSerializer(), 16 | new ListContentSerializer(), 17 | new StringContentSerializer() 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Core/XNB/ContentTypes/SoundEffectContentIdentity.cs: -------------------------------------------------------------------------------- 1 |  2 | 3 | using FEZRepacker.Core.Definitions.Game.XNA; 4 | using FEZRepacker.Core.XNB.ContentSerialization; 5 | using FEZRepacker.Core.XNB.ContentSerialization.System; 6 | 7 | namespace FEZRepacker.Core.XNB.ContentTypes 8 | { 9 | internal class SoundEffectContentIdentity : XnbPrimaryContentIdentity 10 | { 11 | protected override List ContentTypesFactory => new() 12 | { 13 | new GenericContentSerializer(), 14 | new ByteArrayContentSerializer() 15 | }; 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Core/XNB/ContentTypes/SpriteFontContentIdentity.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.Definitions.Game.XNA; 3 | using FEZRepacker.Core.XNB.ContentSerialization; 4 | using FEZRepacker.Core.XNB.ContentSerialization.System; 5 | 6 | namespace FEZRepacker.Core.XNB.ContentTypes 7 | { 8 | internal class SpriteFontContentIdentity : XnbPrimaryContentIdentity 9 | { 10 | protected override List ContentTypesFactory => new() 11 | { 12 | new GenericContentSerializer(), 13 | new GenericContentSerializer(), 14 | new EnumContentSerializer(), 15 | new ByteArrayContentSerializer(), 16 | new ListContentSerializer(true), 17 | new GenericContentSerializer(), 18 | new ListContentSerializer(), 19 | new CharContentSerializer(), 20 | new ListContentSerializer(true), 21 | new GenericContentSerializer(), 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Core/XNB/ContentTypes/TextStorageContentIdentity.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.XNB.ContentSerialization; 2 | using FEZRepacker.Core.XNB.ContentSerialization.System; 3 | 4 | namespace FEZRepacker.Core.XNB.ContentTypes 5 | { 6 | internal class TextStorageContentIdentity : XnbPrimaryContentIdentity 7 | { 8 | protected override List ContentTypesFactory => new() 9 | { 10 | new DictionaryContentSerializer>(), 11 | new StringContentSerializer(false), 12 | new DictionaryContentSerializer() 13 | }; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Core/XNB/ContentTypes/TextureContentIdentity.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.Definitions.Game.XNA; 3 | using FEZRepacker.Core.XNB.ContentSerialization; 4 | using FEZRepacker.Core.XNB.ContentSerialization.System; 5 | 6 | namespace FEZRepacker.Core.XNB.ContentTypes 7 | { 8 | internal class TextureContentIdentity : XnbPrimaryContentIdentity 9 | { 10 | protected override List ContentTypesFactory => new() 11 | { 12 | new GenericContentSerializer(), 13 | new EnumContentSerializer(), 14 | new ByteArrayContentSerializer() 15 | }; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Core/XNB/ContentTypes/TrackedSongContentIdentity.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.Definitions.Game.TrackedSong; 3 | using FEZRepacker.Core.XNB.ContentSerialization; 4 | using FEZRepacker.Core.XNB.ContentSerialization.System; 5 | 6 | namespace FEZRepacker.Core.XNB.ContentTypes 7 | { 8 | internal class TrackedSongContentIdentity : XnbPrimaryContentIdentity 9 | { 10 | protected override List ContentTypesFactory => new() 11 | { 12 | new GenericContentSerializer(), 13 | new ListContentSerializer(), 14 | new GenericContentSerializer(), 15 | new ArrayContentSerializer(), 16 | new EnumContentSerializer(), 17 | new Int32ContentSerializer(), 18 | new EnumContentSerializer(), 19 | new ArrayContentSerializer(), 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Core/XNB/ContentTypes/TrileSetContentIdentity.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.Definitions.Game.ArtObject; 3 | using FEZRepacker.Core.Definitions.Game.Common; 4 | using FEZRepacker.Core.Definitions.Game.Graphics; 5 | using FEZRepacker.Core.Definitions.Game.TrileSet; 6 | using FEZRepacker.Core.Definitions.Game.XNA; 7 | using FEZRepacker.Core.XNB.ContentSerialization; 8 | using FEZRepacker.Core.XNB.ContentSerialization.System; 9 | 10 | namespace FEZRepacker.Core.XNB.ContentTypes 11 | { 12 | internal class TrileSetContentIdentity : XnbPrimaryContentIdentity 13 | { 14 | protected override List ContentTypesFactory => new() 15 | { 16 | new GenericContentSerializer(), 17 | new DictionaryContentSerializer(), 18 | new Int32ContentSerializer(), 19 | new GenericContentSerializer(), 20 | new DictionaryContentSerializer(true, true), 21 | new EnumContentSerializer(), 22 | new EnumContentSerializer(), 23 | new GenericContentSerializer>(), 24 | new GenericContentSerializer(), 25 | new GenericContentSerializer(), 26 | new EnumContentSerializer(), 27 | new ArrayContentSerializer(), 28 | new ArrayContentSerializer(), 29 | new UInt16ContentSerializer(), 30 | new EnumContentSerializer(), 31 | new EnumContentSerializer(), 32 | new GenericContentSerializer(), 33 | new EnumContentSerializer(), 34 | new ByteArrayContentSerializer() 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Core/XNB/XnbCompressor.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | using Microsoft.Xna.Framework.Content; 4 | 5 | namespace FEZRepacker.Core.XNB 6 | { 7 | public class XnbCompressor 8 | { 9 | 10 | /// 11 | /// Attempts to decompress given stream containing XNB file. 12 | /// 13 | /// A stream to convert 14 | /// 15 | /// New stream containing decompressed XNB file, with position at its start. 16 | /// If given stream doesn't have a valid XNB file, returns a copy of input stream. 17 | /// 18 | /// Thrown when compressed data is invalid. 19 | public static Stream Decompress(Stream xnbStream) 20 | { 21 | var decompressedStream = new MemoryStream(); 22 | 23 | if (!XnbHeader.TryRead(xnbStream, out var header) || (header.Flags & XnbHeader.XnbFlags.Compressed) == 0) 24 | { 25 | xnbStream.Position = 0; 26 | xnbStream.CopyTo(decompressedStream); 27 | } 28 | else 29 | { 30 | header.Flags -= XnbHeader.XnbFlags.Compressed; 31 | header.Write(decompressedStream); 32 | 33 | using var decompressedDataStream = new MemoryStream(); 34 | 35 | using var xnbReader = new BinaryReader(xnbStream, Encoding.UTF8, true); 36 | LzxDecoder decoder = new LzxDecoder(16); 37 | 38 | int compressedSize = xnbReader.ReadInt32(); 39 | int decompressedSize = xnbReader.ReadInt32(); 40 | 41 | long startPos = xnbStream.Position; 42 | long pos = startPos; 43 | 44 | while (pos - startPos < compressedSize) 45 | { 46 | // all of these shorts are big endian 47 | int flag = xnbStream.ReadByte(); 48 | int frameSize, blockSize; 49 | if (flag == 0xFF) 50 | { 51 | frameSize = (xnbStream.ReadByte() << 8) | xnbStream.ReadByte(); 52 | blockSize = (xnbStream.ReadByte() << 8) | xnbStream.ReadByte(); 53 | pos += 5; 54 | } 55 | else 56 | { 57 | frameSize = 0x8000; 58 | blockSize = (flag << 8) | xnbStream.ReadByte(); 59 | pos += 2; 60 | } 61 | 62 | 63 | if (blockSize == 0 || frameSize == 0) break; 64 | 65 | decoder.Decompress(xnbStream, blockSize, decompressedDataStream, frameSize); 66 | pos += blockSize; 67 | 68 | xnbStream.Position = pos; 69 | } 70 | 71 | if (decompressedDataStream.Position != decompressedSize) 72 | { 73 | throw new InvalidDataException("XNBDecompressor failed!"); 74 | } 75 | 76 | new BinaryWriter(decompressedStream).Write(decompressedSize); 77 | 78 | decompressedDataStream.Position = 0; 79 | decompressedDataStream.CopyTo(decompressedStream); 80 | } 81 | 82 | decompressedStream.Position = 0; 83 | return decompressedStream; 84 | } 85 | 86 | public static Stream Compress(Stream xnbStream) 87 | { 88 | throw new NotImplementedException(); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Core/XNB/XnbContentReader.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Text; 3 | 4 | using FEZRepacker.Core.XNB.ContentSerialization; 5 | using FEZRepacker.Core.XNB.ContentTypes; 6 | 7 | namespace FEZRepacker.Core.XNB 8 | { 9 | /// 10 | /// Extension of BinaryReader that allows reading XNB content from streams, 11 | /// using individual type serializers defined within provided . 12 | /// 13 | internal class XnbContentReader : BinaryReader 14 | { 15 | public readonly XnbPrimaryContentIdentity Identity; 16 | public XnbContentReader(Stream input, XnbPrimaryContentIdentity identity, bool leaveOpen = true) 17 | : base(input, Encoding.UTF8, leaveOpen) { 18 | Identity = identity; 19 | } 20 | 21 | public T? ReadContent(bool skipIdentifier = false) 22 | { 23 | object? read = ReadContent(typeof(T), skipIdentifier); 24 | return read != null ? (T)read : default; 25 | } 26 | 27 | public object? ReadContent(Type T, bool skipIdentifier = false) 28 | { 29 | if (T.IsPrimitive) skipIdentifier = true; 30 | 31 | int type = skipIdentifier ? 0 : Read7BitEncodedInt(); 32 | 33 | if (type > 0 || skipIdentifier) 34 | { 35 | XnbContentSerializer? typeConverter = Identity.ContentTypes.ToList().Find(t => t.ContentType == T); 36 | if (typeConverter != null) 37 | { 38 | return typeConverter.Deserialize(this); 39 | } 40 | else 41 | { 42 | throw new InvalidDataException($"Cannot convert value of type {T.FullName}"); 43 | } 44 | } 45 | 46 | return null; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Core/XNB/XnbContentWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace FEZRepacker.Core.XNB 4 | { 5 | /// 6 | /// Extension of BinaryWriter that allows writing XNB content to streams, 7 | /// using individual type serializers defined within provided . 8 | /// 9 | internal class XnbContentWriter : BinaryWriter 10 | { 11 | public readonly XnbPrimaryContentIdentity Identity; 12 | 13 | public XnbContentWriter(Stream stream, XnbPrimaryContentIdentity identity, bool leaveOpen = false) 14 | : base(stream, Encoding.UTF8, leaveOpen) 15 | { 16 | Identity = identity; 17 | } 18 | 19 | public void WriteContent(T data, bool skipIdentifier = false) 20 | { 21 | WriteContent(typeof(T), data, skipIdentifier); 22 | } 23 | 24 | public void WriteContent(Type T, object? data, bool skipIdentifier = false) 25 | { 26 | if (T.IsPrimitive) skipIdentifier = true; 27 | 28 | int typeID = Identity.ContentTypes.FindIndex(t => t.ContentType == T); 29 | if (typeID >= 0 && data != null) 30 | { 31 | if (!skipIdentifier && Identity.ContentTypes[typeID].IsEmpty(data)) 32 | { 33 | Write((byte)0x00); 34 | } 35 | else 36 | { 37 | if (!skipIdentifier) 38 | { 39 | int publicTypeID = Identity.PublicContentTypes.FindIndex(t => t.ContentType == T); 40 | if (publicTypeID < 0) 41 | { 42 | throw new InvalidOperationException($"Attempted to write index of private content type {Identity.ContentTypes[typeID].Name}"); 43 | } 44 | Write7BitEncodedInt(publicTypeID + 1); 45 | } 46 | Identity.ContentTypes[typeID].Serialize(data, this); 47 | } 48 | } 49 | else 50 | { 51 | if (!skipIdentifier) 52 | { 53 | Console.WriteLine($"WARNING! Couldn't assign type for {data} in {GetType()}"); 54 | Write((byte)0x00); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Core/XNB/XnbHeader.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace FEZRepacker.Core.XNB 4 | { 5 | /// 6 | /// Stores information present in XNB file header. 7 | /// 8 | public struct XnbHeader 9 | { 10 | [Flags] 11 | public enum XnbFlags 12 | { 13 | None = 0x00, 14 | HiDefProfile = 0x01, 15 | Compressed = 0x80, 16 | } 17 | 18 | public enum XnbPlatformIdentifier 19 | { 20 | Windows = 'w', 21 | Mobile = 'm', 22 | XBox = 'x', 23 | } 24 | 25 | public string FormatIdentifier; 26 | public XnbPlatformIdentifier PlatformIdentifier; 27 | public byte Version; 28 | public XnbFlags Flags; 29 | 30 | public static XnbHeader Default => new XnbHeader 31 | { 32 | FormatIdentifier = "XNB", 33 | PlatformIdentifier = XnbPlatformIdentifier.Windows, 34 | Version = 5, 35 | Flags = XnbFlags.HiDefProfile, 36 | }; 37 | 38 | /// 39 | /// Attempts to read XNB header from given stream. 40 | /// 41 | /// Stream position can change even if reading fails. 42 | /// Stream to read header to 43 | /// When read successfully, stores XNB header. 44 | /// True if read header from given stream successfully, false otherwise. 45 | public static bool TryRead(Stream stream, out XnbHeader header) 46 | { 47 | using var reader = new BinaryReader(stream, Encoding.UTF8, true); 48 | 49 | header = new XnbHeader(); 50 | header.FormatIdentifier = new string(reader.ReadChars(3)); 51 | if (header.FormatIdentifier != "XNB") return false; 52 | if (!Enum.TryParse(reader.ReadByte().ToString(), out XnbPlatformIdentifier platformIdentifier)) 53 | { 54 | return false; 55 | } 56 | header.PlatformIdentifier = platformIdentifier; 57 | header.Version = reader.ReadByte(); 58 | header.Flags = (XnbFlags)reader.ReadByte(); 59 | 60 | return true; 61 | } 62 | 63 | /// 64 | /// Writes XNB header into the stream. 65 | /// 66 | /// Steam to write XNB header to. 67 | public void Write(Stream stream) 68 | { 69 | var formatIdentifierBytes = FormatIdentifier.ToCharArray().Select(c => (byte)c).ToArray(); 70 | stream.Write(formatIdentifierBytes, (int)stream.Position, formatIdentifierBytes.Length); 71 | stream.WriteByte((byte)PlatformIdentifier); 72 | stream.WriteByte(Version); 73 | stream.WriteByte((byte)Flags); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Core/XNB/XnbPrimaryContentIdentity.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.XNB.ContentSerialization; 2 | 3 | namespace FEZRepacker.Core.XNB 4 | { 5 | /// 6 | /// Structure storing a description of a primary content type that 7 | /// can be found in an XNB file. is expected 8 | /// to be overloaded to return a list of s 9 | /// representing this content type (with the first one in the list being 10 | /// the primary type). 11 | /// 12 | /// 13 | /// Each content type uses more-or-less the same types. Instead of generating 14 | /// them dynamically, each primary content type has its own identity structure 15 | /// defining all types it could possibly used. This can create disparity to the 16 | /// original XNB asset format, but FEZ is fine with it, and we gain some 17 | /// performance from it. 18 | /// 19 | internal abstract class XnbPrimaryContentIdentity 20 | { 21 | protected abstract List ContentTypesFactory { get; } 22 | 23 | public readonly List ContentTypes; 24 | public readonly List PublicContentTypes; 25 | public XnbContentSerializer PrimaryContentType => PublicContentTypes[0]; 26 | public string FormatName => PrimaryContentType.Name.Name.Replace("Reader", ""); 27 | 28 | public XnbPrimaryContentIdentity() 29 | { 30 | ContentTypes = ContentTypesFactory; 31 | PublicContentTypes = ContentTypes.Where(t => !t.IsPrivate).ToList(); 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Core/XNB/XnbPrimaryContents.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.XNB.ContentTypes; 2 | 3 | namespace FEZRepacker.Core.XNB 4 | { 5 | /// 6 | /// Contains a statically declared list of all 7 | /// structures needed to parse all XNB files contained within FEZ. 8 | /// 9 | internal static class XnbPrimaryContents 10 | { 11 | public static readonly List List = new() 12 | { 13 | new AnimatedTextureContentIdentity(), 14 | new ArtObjectContentIdentity(), 15 | new EffectContentIdentity(), 16 | new LevelContentIdentity(), 17 | new MapContentIdentity(), 18 | new NpcMetadataContentIdentity(), 19 | new SkyContentIdentity(), 20 | new SoundEffectContentIdentity(), 21 | new SpriteFontContentIdentity(), 22 | new TextStorageContentIdentity(), 23 | new TextureContentIdentity(), 24 | new TrackedSongContentIdentity(), 25 | new TrileSetContentIdentity() 26 | }; 27 | 28 | public static XnbPrimaryContentIdentity? FindByQualifier(XnbAssemblyQualifier qualifier) 29 | { 30 | return List.Where(i => i.PrimaryContentType.Name.Equals(qualifier)).FirstOrDefault(); 31 | } 32 | 33 | public static XnbPrimaryContentIdentity? FindByType(Type type) 34 | { 35 | return List.Where(i => i.PrimaryContentType.ContentType == type).FirstOrDefault(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Core/XNB/XnbSerializationException.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Core.XNB 2 | { 3 | [Serializable] 4 | public class XnbSerializationException : Exception 5 | { 6 | internal string? _message; 7 | public override string Message 8 | { 9 | get => _message ?? base.Message; 10 | } 11 | 12 | public XnbSerializationException(string message) : base(message) 13 | { 14 | _message = message; 15 | } 16 | 17 | public XnbSerializationException(string message, Exception innerException) : base(message, innerException) 18 | { 19 | _message = message; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /FEZRepacker.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32407.343 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FEZRepacker.Core", "Core\FEZRepacker.Core.csproj", "{B0186A91-03B6-4E71-8A4A-547CCAAA1300}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FEZRepacker.Interface", "Interface\FEZRepacker.Interface.csproj", "{390DE9FA-4CE4-42BA-871B-064A3C5027B6}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{14BA18E6-0546-4326-B548-B44F64C2C627}" 11 | ProjectSection(SolutionItems) = preProject 12 | .editorconfig = .editorconfig 13 | EndProjectSection 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FEZRepacker.Tests", "Tests\FEZRepacker.Tests.csproj", "{5A131157-3B7A-4C41-9397-EFEED5F2FA66}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {B0186A91-03B6-4E71-8A4A-547CCAAA1300}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {B0186A91-03B6-4E71-8A4A-547CCAAA1300}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {B0186A91-03B6-4E71-8A4A-547CCAAA1300}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {B0186A91-03B6-4E71-8A4A-547CCAAA1300}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {390DE9FA-4CE4-42BA-871B-064A3C5027B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {390DE9FA-4CE4-42BA-871B-064A3C5027B6}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {390DE9FA-4CE4-42BA-871B-064A3C5027B6}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {390DE9FA-4CE4-42BA-871B-064A3C5027B6}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {5A131157-3B7A-4C41-9397-EFEED5F2FA66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {5A131157-3B7A-4C41-9397-EFEED5F2FA66}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {5A131157-3B7A-4C41-9397-EFEED5F2FA66}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {5A131157-3B7A-4C41-9397-EFEED5F2FA66}.Release|Any CPU.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(SolutionProperties) = preSolution 37 | HideSolutionNode = FALSE 38 | EndGlobalSection 39 | GlobalSection(ExtensibilityGlobals) = postSolution 40 | SolutionGuid = {B9818649-66F5-477E-B7FF-3AADD90AFC07} 41 | EndGlobalSection 42 | EndGlobal 43 | -------------------------------------------------------------------------------- /Interface/Actions/ConvertFromXnbAction.cs: -------------------------------------------------------------------------------- 1 |  2 | using FEZRepacker.Core.FileSystem; 3 | using FEZRepacker.Core.XNB; 4 | 5 | namespace FEZRepacker.Interface.Actions 6 | { 7 | internal class ConvertFromXnbAction : CommandLineAction 8 | { 9 | public string Name => "--convert-from-xnb"; 10 | 11 | public string[] Aliases => new[] { "-x" }; 12 | 13 | public string Description => 14 | "Attempts to convert given XNB input (this can be a path to a single asset or an entire directory) " + 15 | "and save it at given output directory. If input is a directory, dumps all converted files in specified " + 16 | "path recursively. If output directory is not given, outputs next to the input file(s)."; 17 | 18 | public CommandLineArgument[] Arguments => new[] { 19 | new CommandLineArgument("xnb-input"), 20 | new CommandLineArgument("file-output", true) 21 | }; 22 | 23 | 24 | private List FindXnbFilesAtPath(string path) 25 | { 26 | if (Directory.Exists(path)) 27 | { 28 | var xnbFiles = Directory.GetFiles(path, "*.xnb", SearchOption.AllDirectories).ToList(); 29 | Console.WriteLine($"Found {xnbFiles.Count()} XNB files in given directory."); 30 | return xnbFiles; 31 | } 32 | else if (File.Exists(path)) 33 | { 34 | if (Path.GetExtension(path) != ".xnb") 35 | { 36 | throw new Exception("An input file must be an .XNB file."); 37 | } 38 | return new List { path }; 39 | } 40 | else 41 | { 42 | throw new FileNotFoundException("Specified input path does not lead to any file or a directory"); 43 | } 44 | } 45 | 46 | 47 | 48 | public void Execute(string[] args) 49 | { 50 | var inputPath = args[0]; 51 | var outputPath = args.Length > 1 ? args[1] : inputPath; 52 | 53 | if (File.Exists(outputPath)) 54 | { 55 | outputPath = Path.GetDirectoryName(outputPath) ?? ""; 56 | } 57 | Directory.CreateDirectory(outputPath); 58 | 59 | var xnbFilesToConvert = FindXnbFilesAtPath(inputPath); 60 | 61 | Console.WriteLine($"Converting {xnbFilesToConvert.Count()} XNB files..."); 62 | 63 | var filesDone = 0; 64 | foreach (var xnbPath in xnbFilesToConvert) 65 | { 66 | Console.WriteLine($"({filesDone + 1}/{xnbFilesToConvert.Count}) {xnbPath}"); 67 | 68 | using var xnbStream = File.OpenRead(xnbPath); 69 | 70 | using var outputBundle = UnpackAction.UnpackFile(".xnb", xnbStream, UnpackAction.UnpackingMode.Converted); 71 | 72 | var relativePath = Path.GetRelativePath(inputPath, xnbPath) 73 | .Replace("/", "\\") 74 | .Replace(".xnb", "", StringComparison.InvariantCultureIgnoreCase); 75 | outputBundle.BundlePath = Path.Combine(outputPath, relativePath + outputBundle.MainExtension); 76 | var outputDirectory = Path.GetDirectoryName(outputBundle.BundlePath) ?? ""; 77 | 78 | 79 | Directory.CreateDirectory(outputDirectory); 80 | foreach (var outputFile in outputBundle.Files) 81 | { 82 | using var fileOutputStream = File.Open(outputBundle.BundlePath + outputFile.Extension, FileMode.Create); 83 | outputFile.Data.CopyTo(fileOutputStream); 84 | } 85 | 86 | filesDone++; 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Interface/Actions/ConvertToXnbAction.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.IO; 3 | 4 | using FEZRepacker.Core.Conversion; 5 | using FEZRepacker.Core.FileSystem; 6 | using FEZRepacker.Core.XNB; 7 | 8 | namespace FEZRepacker.Interface.Actions 9 | { 10 | internal class ConvertToXnbAction : CommandLineAction 11 | { 12 | public string Name => "--convert-to-xnb"; 13 | 14 | public string[] Aliases => new[] { "-X" }; 15 | 16 | public string Description => 17 | "Attempts to convert given input (this can be a path to a single file or an entire directory) " + 18 | "into XNB file(s) and save it at given output directory. If input is a directory, dumps all converted files in" + 19 | "specified path recursively. If output directory is not given, outputs next to the input file(s)."; 20 | 21 | public CommandLineArgument[] Arguments => new[] { 22 | new CommandLineArgument("file-input"), 23 | new CommandLineArgument("xnb-output", true) 24 | }; 25 | 26 | 27 | public delegate void ConversionFunc(string path, string extension, Stream stream, bool converted); 28 | public static void PerformBatchConversion(List fileBundles, ConversionFunc processFileFunc) 29 | { 30 | Console.WriteLine($"Converting {fileBundles.Count()} assets..."); 31 | var filesDone = 0; 32 | foreach (var fileBundle in fileBundles) 33 | { 34 | Console.WriteLine($"({filesDone + 1}/{fileBundles.Count}) {fileBundle.BundlePath}"); 35 | 36 | try 37 | { 38 | object convertedData = FormatConversion.Deconvert(fileBundle)!; 39 | var data = XnbSerializer.Serialize(convertedData); 40 | 41 | Console.WriteLine($" Format {fileBundle.MainExtension} deconverted into {convertedData.GetType().Name}."); 42 | processFileFunc(fileBundle.BundlePath, ".xnb", data, true); 43 | } 44 | catch (Exception ex) 45 | { 46 | Console.Error.WriteLine($" Unable to convert asset: {ex.Message}"); 47 | foreach (var file in fileBundle.Files) 48 | { 49 | file.Data.Seek(0, SeekOrigin.Begin); 50 | var ext = fileBundle.MainExtension + file.Extension; 51 | processFileFunc(fileBundle.BundlePath, ext, file.Data, false); 52 | } 53 | } 54 | filesDone++; 55 | 56 | fileBundle.Dispose(); 57 | } 58 | } 59 | 60 | public void Execute(string[] args) 61 | { 62 | string inputPath = args[0]; 63 | string outputPath = args.Length > 1 ? args[1] : inputPath; 64 | 65 | if (File.Exists(outputPath)) 66 | { 67 | outputPath = Path.GetDirectoryName(outputPath) ?? ""; 68 | } 69 | Directory.CreateDirectory(outputPath); 70 | 71 | var fileBundles = FileBundle.BundleFilesAtPath(inputPath); 72 | Console.WriteLine($"Found {fileBundles.Count()} file bundles."); 73 | 74 | 75 | PerformBatchConversion(fileBundles, (path, extension, stream, converted) => 76 | { 77 | if (!converted) return; 78 | 79 | var assetOutputFullPath = Path.Combine(outputPath, $"{path}{extension}"); 80 | 81 | Directory.CreateDirectory(Path.GetDirectoryName(assetOutputFullPath) ?? ""); 82 | 83 | using var assetFile = File.Create(assetOutputFullPath); 84 | stream.CopyTo(assetFile); 85 | }); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Interface/Actions/HelpAction.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Interface.Actions 2 | { 3 | internal class HelpAction : CommandLineAction 4 | { 5 | public string Name => "--help"; 6 | public string[] Aliases => new[] { "help", "?", "-?", "-h" }; 7 | public string Description => "Displays help for all commands or help for given command."; 8 | public CommandLineArgument[] Arguments => new[] { 9 | new CommandLineArgument("command", true) 10 | }; 11 | 12 | public void Execute(string[] args) 13 | { 14 | if(args.Length == 0) 15 | { 16 | foreach (var cmd in CommandLineInterface.Commands) 17 | { 18 | ShowHelpFor(cmd); 19 | Console.WriteLine(); 20 | } 21 | return; 22 | } 23 | 24 | var command = CommandLineInterface.FindCommand(args[0]); 25 | if (command != null) 26 | { 27 | ShowHelpFor(command); 28 | } 29 | else 30 | { 31 | Console.WriteLine($"Unknown command \"{args[0]}\"."); 32 | Console.WriteLine($"Use \"--help\" parameter for a list of commands."); 33 | } 34 | } 35 | 36 | private void ShowHelpFor(CommandLineAction command) 37 | { 38 | string allNames = command.Name; 39 | if (command.Aliases.Length > 0) 40 | { 41 | allNames = $"[{command.Name}, {String.Join(", ", command.Aliases)}]"; 42 | } 43 | 44 | string argsStr = String.Join(" ", command.Arguments.Select(arg => arg.Optional ? $"<{arg.Name}>" : $"[{arg.Name}]")); 45 | 46 | Console.WriteLine($"Usage: {allNames} {argsStr}"); 47 | Console.WriteLine($"Description: {command.Description}"); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Interface/Actions/ListPackageContentAction.cs: -------------------------------------------------------------------------------- 1 | using FEZRepacker.Core.FileSystem; 2 | 3 | namespace FEZRepacker.Interface.Actions 4 | { 5 | internal class ListPackageContentAction : CommandLineAction 6 | { 7 | public string Name => "--list"; 8 | public string[] Aliases => new[] { "-l" }; 9 | public string Description => "Lists all files contained withing given .PAK package."; 10 | public CommandLineArgument[] Arguments => new[] { 11 | new CommandLineArgument("pak-path") 12 | }; 13 | 14 | public void Execute(string[] args) 15 | { 16 | var pakPath = args[0]; 17 | 18 | var pakPackage = PakPackage.ReadFromFile(pakPath); 19 | 20 | Console.WriteLine($"PAK package \"{pakPath}\" with {pakPackage.Entries.Count} files."); 21 | Console.WriteLine(); 22 | 23 | foreach (var entry in pakPackage.Entries) 24 | { 25 | var extension = entry.FindExtension(); 26 | var typeText = extension.Length == 0 ? "unknown" : extension; 27 | Console.WriteLine($"{entry.Path} ({typeText} file, size: {entry.Length} bytes)"); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Interface/Actions/UnpackConvertAction.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Interface.Actions 2 | { 3 | internal class UnpackConvertAction : UnpackAction 4 | { 5 | protected override UnpackingMode Mode => UnpackingMode.Converted; 6 | public override string Name => "--unpack"; 7 | public override string[] Aliases => new[] { "-u" }; 8 | public override string Description => 9 | "Unpacks entire .PAK package into specified directory (creates one if doesn't exist) " + 10 | "and attempts to convert XNB assets into their corresponding format in the process."; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Interface/Actions/UnpackDecompressedAction.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Interface.Actions 2 | { 3 | internal class UnpackDecompressedAction : UnpackAction 4 | { 5 | protected override UnpackingMode Mode => UnpackingMode.DecompressedXNB; 6 | public override string Name => "--unpack-decompressed"; 7 | public override string[] Aliases => new string[] {}; 8 | public override string Description => 9 | "Unpacks entire .PAK package into specified directory (creates one if doesn't exist)." + 10 | "and attempts to decompress all XNB assets, but does not convert them."; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Interface/Actions/UnpackGameAction.cs: -------------------------------------------------------------------------------- 1 | using static FEZRepacker.Interface.Actions.UnpackAction; 2 | 3 | namespace FEZRepacker.Interface.Actions 4 | { 5 | internal class UnpackGameAction : CommandLineAction 6 | { 7 | public string Name => "--unpack-fez-content"; 8 | 9 | public string[] Aliases => new[] { "-g" }; 10 | 11 | public string Description => 12 | "Unpacks and converts all game assets into specified directory (creates one if doesn't exist)."; 13 | 14 | public CommandLineArgument[] Arguments => new[] { 15 | new CommandLineArgument("fez-content-directory"), 16 | new CommandLineArgument("destination-folder") 17 | }; 18 | 19 | public void Execute(string[] args) 20 | { 21 | var contentPath = args[0]; 22 | var outputDir = args[1]; 23 | 24 | var packagePaths = new string[] { "Essentials.pak", "Music.pak", "Other.pak", "Updates.pak" } 25 | .Select(path => Path.Combine(contentPath, path)).ToArray(); 26 | 27 | foreach (var packagePath in packagePaths) 28 | { 29 | if (!File.Exists(packagePath)) 30 | { 31 | throw new Exception($"Given directory is not FEZ's Content directory (missing {Path.GetFileName(packagePath)})."); 32 | } 33 | } 34 | 35 | foreach (var packagePath in packagePaths) 36 | { 37 | var actualOutputDir = outputDir; 38 | if (packagePath.EndsWith("Music.pak")) 39 | { 40 | // Special Repacker behaviour - instead of dumping music tracks in 41 | // the same folder as other assets, put them in separate music folder. 42 | actualOutputDir = Path.Combine(outputDir, "music"); 43 | } 44 | UnpackPackage(packagePath, actualOutputDir, UnpackingMode.Converted); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Interface/Actions/UnpackRawAction.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Interface.Actions 2 | { 3 | internal class UnpackRawAction : UnpackAction 4 | { 5 | protected override UnpackingMode Mode => UnpackingMode.Raw; 6 | public override string Name => "--unpack-raw"; 7 | public override string[] Aliases => new string[] {}; 8 | public override string Description => 9 | "Unpacks entire .PAK package into specified directory (creates one " + 10 | "if doesn't exist) leaving XNB assets in their original form."; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Interface/CommandLineAction.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Interface 2 | { 3 | internal interface CommandLineAction 4 | { 5 | public string Name { get; } 6 | public string[] Aliases { get; } 7 | public string Description { get; } 8 | public CommandLineArgument[] Arguments { get; } 9 | public void Execute(string[] args); 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Interface/CommandLineArgument.cs: -------------------------------------------------------------------------------- 1 | namespace FEZRepacker.Interface 2 | { 3 | public struct CommandLineArgument 4 | { 5 | public string Name; 6 | public bool Optional; 7 | public CommandLineArgument(string name, bool optional = false) 8 | { 9 | Name = name; 10 | Optional = optional; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Interface/FEZRepacker.Interface.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | $(SolutionName) 7 | latest 8 | 9 | enable 10 | enable 11 | 12 | 13 | 14 | false 15 | true 16 | true 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Interface/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | using FEZRepacker.Core.XNB; 4 | 5 | 6 | namespace FEZRepacker.Interface 7 | { 8 | internal class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | string version = typeof(XnbSerializer).Assembly.GetName().Version?.ToString() ?? ""; 13 | version = string.Join(".", version.Split('.').Take(3)); 14 | // showoff 15 | Console.WriteLine($"=== FEZRepacker {version} by Krzyhau & FEZModding Team ===\n"); 16 | 17 | if (args.Length > 0) 18 | { 19 | CommandLineInterface.ParseCommandLine(args); 20 | } 21 | else 22 | { 23 | CommandLineInterface.RunInteractiveMode(); 24 | } 25 | } 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | .\Results 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Tests/FEZRepacker.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | latest 8 | 9 | false 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tests/TestPacking.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using FEZRepacker.Core.FileSystem; 3 | 4 | namespace FEZRepacker.Tests 5 | { 6 | [TestClass] 7 | public class TestPacking 8 | { 9 | [TestMethod] 10 | [DynamicData(nameof(TestUtils.PackagePathsTestData), typeof(TestUtils))] 11 | public void RepackAndComparePackage(string packagePath) 12 | { 13 | var pakData = File.ReadAllBytes(packagePath); 14 | 15 | using var pakStream = new MemoryStream(pakData); 16 | using var pakReader = new PakReader(pakStream); 17 | 18 | using var repackStream = new MemoryStream(); 19 | using var repackWriter = new PakWriter(repackStream); 20 | 21 | foreach (var item in pakReader.ReadFiles()) 22 | { 23 | repackWriter.WriteFile(item.Path, new MemoryStream(item.Payload)); 24 | } 25 | 26 | repackWriter.Dispose(); 27 | var repackData = repackStream.ToArray(); 28 | 29 | Assert.IsTrue(repackData.SequenceEqual(pakData)); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Tests/TestUtils.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | namespace FEZRepacker.Tests 4 | { 5 | [TestClass] 6 | public class TestUtils 7 | { 8 | public static TestContext Context; 9 | 10 | [AssemblyInitialize] 11 | public static void SetupTestContext(TestContext testContext) 12 | { 13 | Context = testContext; 14 | Assert.IsTrue( 15 | Directory.Exists(GetGameAssetsDirectory()), 16 | "You forgot to put FEZ's Content path into .runsettings file" 17 | ); 18 | } 19 | 20 | public static IEnumerable PackagePathsTestData => 21 | GetPathsToPackages().Select(name => new object[] { name }); 22 | 23 | public static string GetGameAssetsDirectory() 24 | { 25 | return Context.Properties["FEZContentDirPath"].ToString(); 26 | } 27 | 28 | public static IEnumerable GetPathsToPackages() 29 | { 30 | return Directory.EnumerateFiles(GetGameAssetsDirectory(), "*.pak", SearchOption.AllDirectories); 31 | } 32 | } 33 | } 34 | --------------------------------------------------------------------------------