├── .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