diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..fb45e5e --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,119 @@ +on: + workflow_dispatch: + release: + types: + - published + +permissions: + contents: write + +jobs: + get-version: + name: Extract Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Extract version tag + id: version + run: | + git fetch --tags --force + VERSION_TAG=$(git tag --points-at "$GITHUB_SHA" | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$' | head -n 1 || echo "") + if [ -n "$VERSION_TAG" ]; then + echo "version=$VERSION_TAG" >> $GITHUB_OUTPUT + echo "Found version tag: $VERSION_TAG" + else + SHORT_SHA=$(git rev-parse --short HEAD) + echo "version=${SHORT_SHA}" >> $GITHUB_OUTPUT + echo "No version tag found, using: ${SHORT_SHA}" + fi + + build: + name: ${{ matrix.rid }} + needs: get-version + strategy: + fail-fast: false + matrix: + rid: + - win-x64 + - linux-x64 + - linux-arm64 + - osx-arm64 + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: '10' + + - name: Publish + run: > + dotnet publish RePKG/RePKG.csproj + -c Release + -r ${{ matrix.rid }} + --no-self-contained + -p:PublishSingleFile=true + -p:PublishReadyToRun=true + -p:GeneratePackageOnBuild=false + -o publish/out + + - name: Upload artifact + uses: actions/upload-artifact@v7 + with: + name: repkg-${{ needs.get-version.outputs.version }}-${{ matrix.rid }} + path: publish/out/ + if-no-files-found: error + + verify-builds: + name: Verify All Builds Passed + if: github.event_name == 'workflow_dispatch' + needs: + - get-version + - build + runs-on: ubuntu-latest + steps: + - name: All builds succeeded + run: | + echo "✅ All matrix builds passed for version: ${{ needs.get-version.outputs.version }}" + echo "Artifacts are NOT uploaded to any release (manual trigger)." + + publish-release: + name: Upload to Release + if: github.event_name == 'release' + needs: + - get-version + - build + runs-on: ubuntu-latest + steps: + - name: Download all artifacts + uses: actions/download-artifact@v7 + with: + path: release-assets + merge-multiple: false + + - name: Package artifacts + run: | + mkdir -p packaged + for dir in release-assets/*/; do + name=$(basename "$dir") + if [[ "$name" == *win-x64* ]]; then + (cd "$dir" && zip -r "${GITHUB_WORKSPACE}/packaged/${name}.zip" .) + else + tar -czf "packaged/${name}.tar.gz" -C "$dir" . + fi + done + echo "--- Packaged files ---" + ls -lh packaged/ + + - name: Upload release assets + uses: softprops/action-gh-release@v3 + with: + files: packaged/* + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 7db3595..5e8b1a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,429 @@ -packages -.vs -.idea -obj -bin -Publish -RePKG.Tests/Output/* -RePKG.Tests/TestTextures/* -RePKG.Tests/TestTexturesValidated/* \ No newline at end of file +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore +/tmp +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates +*.env + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ + +[Dd]ebug/x64/ +[Dd]ebugPublic/x64/ +[Rr]elease/x64/ +[Rr]eleases/x64/ +bin/x64/ +obj/x64/ + +[Dd]ebug/x86/ +[Dd]ebugPublic/x86/ +[Rr]elease/x86/ +[Rr]eleases/x86/ +bin/x86/ +obj/x86/ + +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +[Aa][Rr][Mm]64[Ee][Cc]/ +bld/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Build results on 'Bin' directories +**/[Bb]in/* +# Uncomment if you have tasks that rely on *.refresh files to move binaries +# (https://github.com/github/gitignore/pull/3736) +#!**/[Bb]in/*.refresh + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* +*.trx + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Approval Tests result files +*.received.* + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +.artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.idb +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +# but not Directory.Build.rsp, as it configures directory-level build defaults +!Directory.Build.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +**/.paket/paket.exe +paket-files/ + +# FAKE - F# Make +**/.fake/ + +# CodeRush personal settings +**/.cr/personal + +# Python Tools for Visual Studio (PTVS) +**/__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +#tools/** +#!tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog +MSBuild_Logs/ + +# AWS SAM Build and Temporary Artifacts folder +.aws-sam + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +**/.mfractor/ + +# Local History for Visual Studio +**/.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +**/.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp \ No newline at end of file diff --git a/RePKG.Application/RePKG.Application.csproj b/RePKG.Application/RePKG.Application.csproj deleted file mode 100644 index 64b2f6b..0000000 --- a/RePKG.Application/RePKG.Application.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - netstandard2.0 - 0.4.0 - Copyright © NotScuffed 2025 - - - - - - - - - - - - - diff --git a/RePKG.Application/RePKG.Application.csproj.DotSettings b/RePKG.Application/RePKG.Application.csproj.DotSettings deleted file mode 100644 index 65899f2..0000000 --- a/RePKG.Application/RePKG.Application.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - True \ No newline at end of file diff --git a/RePKG.Application/Constants.cs b/RePKG.Core/Constants.cs similarity index 90% rename from RePKG.Application/Constants.cs rename to RePKG.Core/Constants.cs index 225bdb2..a2670c0 100644 --- a/RePKG.Application/Constants.cs +++ b/RePKG.Core/Constants.cs @@ -1,4 +1,4 @@ -namespace RePKG.Application +namespace RePKG.Core { internal static class Constants { diff --git a/RePKG.Application/Exceptions/EnumNotValidException.cs b/RePKG.Core/Exceptions/EnumNotValidException.cs similarity index 86% rename from RePKG.Application/Exceptions/EnumNotValidException.cs rename to RePKG.Core/Exceptions/EnumNotValidException.cs index 234e919..8f55f8b 100644 --- a/RePKG.Application/Exceptions/EnumNotValidException.cs +++ b/RePKG.Core/Exceptions/EnumNotValidException.cs @@ -1,6 +1,4 @@ -using System; - -namespace RePKG.Application.Exceptions +namespace RePKG.Core.Exceptions { /// /// Thrown when enum raw value doesn't match any label diff --git a/RePKG.Application/Exceptions/UnknownMagicException.cs b/RePKG.Core/Exceptions/UnknownMagicException.cs similarity index 88% rename from RePKG.Application/Exceptions/UnknownMagicException.cs rename to RePKG.Core/Exceptions/UnknownMagicException.cs index 62db092..fe5cde7 100644 --- a/RePKG.Application/Exceptions/UnknownMagicException.cs +++ b/RePKG.Core/Exceptions/UnknownMagicException.cs @@ -1,6 +1,4 @@ -using System; - -namespace RePKG.Application.Exceptions +namespace RePKG.Core.Exceptions { /// /// Thrown on magic values that have not been tested before @@ -11,7 +9,7 @@ public UnknownMagicException(string source, string magic) : base( $"Unknown magic: '{magic}' in '{source}'") { } - + public UnknownMagicException(string source, string property, string magic) : base( $"Unknown magic: '{magic}' in '{source}:{property}'") { diff --git a/RePKG.Application/Exceptions/UnsafeTexException.cs b/RePKG.Core/Exceptions/UnsafeTexException.cs similarity index 81% rename from RePKG.Application/Exceptions/UnsafeTexException.cs rename to RePKG.Core/Exceptions/UnsafeTexException.cs index 6dded54..7826bc3 100644 --- a/RePKG.Application/Exceptions/UnsafeTexException.cs +++ b/RePKG.Core/Exceptions/UnsafeTexException.cs @@ -1,6 +1,4 @@ -using System; - -namespace RePKG.Application.Exceptions +namespace RePKG.Core.Exceptions { /// /// Thrown on unorganic values. @@ -9,6 +7,6 @@ namespace RePKG.Application.Exceptions public class UnsafeTexException : Exception { public UnsafeTexException(string reason) : base($"Unsafe TEX detected, reason: {reason}") - {} + { } } } \ No newline at end of file diff --git a/RePKG.Application/Extensions.cs b/RePKG.Core/Extensions.cs similarity index 69% rename from RePKG.Application/Extensions.cs rename to RePKG.Core/Extensions.cs index 5a8924f..ccc91d4 100644 --- a/RePKG.Application/Extensions.cs +++ b/RePKG.Core/Extensions.cs @@ -1,15 +1,13 @@ -using System; -using System.IO; using System.Text; -namespace RePKG.Application +namespace RePKG.Core { internal static class Extensions { public static string ReadNString(this BinaryReader reader, int maxLength = -1) { - if (reader == null) throw new ArgumentNullException(nameof(reader)); - + ArgumentNullException.ThrowIfNull(reader); + var builder = new StringBuilder(maxLength <= 0 ? 16 : maxLength); var c = reader.ReadChar(); @@ -24,16 +22,16 @@ public static string ReadNString(this BinaryReader reader, int maxLength = -1) public static void WriteNString(this BinaryWriter writer, string input) { - if (writer == null) throw new ArgumentNullException(nameof(writer)); - if (input == null) throw new ArgumentNullException(nameof(input)); + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(input); writer.Write(Encoding.UTF8.GetBytes(input)); - writer.Write((byte) 0); + writer.Write((byte)0); } public static string ReadStringI32Size(this BinaryReader reader, int maxLength = -1) { - if (reader == null) throw new ArgumentNullException(nameof(reader)); + ArgumentNullException.ThrowIfNull(reader); var size = reader.ReadInt32(); @@ -50,8 +48,8 @@ public static string ReadStringI32Size(this BinaryReader reader, int maxLength = public static void WriteStringI32Size(this BinaryWriter writer, string input) { - if (writer == null) throw new ArgumentNullException(nameof(writer)); - if (input == null) throw new ArgumentNullException(nameof(input)); + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(input); writer.Write(input.Length); writer.Write(Encoding.UTF8.GetBytes(input)); diff --git a/RePKG.Core/Package/Interfaces/IPackageReader.cs b/RePKG.Core/Package/Interfaces/IPackageReader.cs index 184dd93..62cd416 100644 --- a/RePKG.Core/Package/Interfaces/IPackageReader.cs +++ b/RePKG.Core/Package/Interfaces/IPackageReader.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Package.Interfaces { public interface IPackageReader diff --git a/RePKG.Core/Package/Interfaces/IPackageWriter.cs b/RePKG.Core/Package/Interfaces/IPackageWriter.cs index 4cd0061..5c9cc30 100644 --- a/RePKG.Core/Package/Interfaces/IPackageWriter.cs +++ b/RePKG.Core/Package/Interfaces/IPackageWriter.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Package.Interfaces { public interface IPackageWriter diff --git a/RePKG.Core/Package/Package.cs b/RePKG.Core/Package/Package.cs index bc15656..8d730a8 100644 --- a/RePKG.Core/Package/Package.cs +++ b/RePKG.Core/Package/Package.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace RePKG.Core.Package { public class Package diff --git a/RePKG.Core/Package/PackageEntry.cs b/RePKG.Core/Package/PackageEntry.cs index 409e324..166181b 100644 --- a/RePKG.Core/Package/PackageEntry.cs +++ b/RePKG.Core/Package/PackageEntry.cs @@ -1,4 +1,3 @@ -using System.IO; using RePKG.Core.Package.Enums; namespace RePKG.Core.Package @@ -10,7 +9,7 @@ public class PackageEntry public int Length { get; set; } public byte[] Bytes { get; set; } public EntryType Type { get; set; } - + public string Name => Path.GetFileNameWithoutExtension(FullPath); public string Extension => Path.GetExtension(FullPath); public string DirectoryPath => Path.GetDirectoryName(FullPath); diff --git a/RePKG.Core/Package/PackageEntryTypeGetter.cs b/RePKG.Core/Package/PackageEntryTypeGetter.cs index 9b22e1b..8e4b234 100644 --- a/RePKG.Core/Package/PackageEntryTypeGetter.cs +++ b/RePKG.Core/Package/PackageEntryTypeGetter.cs @@ -1,4 +1,3 @@ -using System.IO; using RePKG.Core.Package.Enums; namespace RePKG.Core.Package diff --git a/RePKG.Application/Package/PackageReader.cs b/RePKG.Core/Package/PackageReader.cs similarity index 86% rename from RePKG.Application/Package/PackageReader.cs rename to RePKG.Core/Package/PackageReader.cs index a7e9a3e..9422262 100644 --- a/RePKG.Application/Package/PackageReader.cs +++ b/RePKG.Core/Package/PackageReader.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.IO; -using RePKG.Core.Package; using RePKG.Core.Package.Interfaces; -namespace RePKG.Application.Package +namespace RePKG.Core.Package { public class PackageReader : IPackageReader { @@ -13,7 +9,7 @@ public class PackageReader : IPackageReader public Core.Package.Package ReadFrom(BinaryReader reader) { if (reader == null) throw new ArgumentNullException(nameof(reader)); - + var packageStart = reader.BaseStream.Position; var package = new Core.Package.Package { @@ -22,8 +18,8 @@ public Core.Package.Package ReadFrom(BinaryReader reader) ReadEntries(package.Entries, reader); - var dataStart = (int) reader.BaseStream.Position; - package.HeaderSize = (int) (dataStart - packageStart); + var dataStart = (int)reader.BaseStream.Position; + package.HeaderSize = (int)(dataStart - packageStart); if (!ReadEntryBytes) return package; diff --git a/RePKG.Application/Package/PackageWriter.cs b/RePKG.Core/Package/PackageWriter.cs similarity index 93% rename from RePKG.Application/Package/PackageWriter.cs rename to RePKG.Core/Package/PackageWriter.cs index 18f02d7..183a237 100644 --- a/RePKG.Application/Package/PackageWriter.cs +++ b/RePKG.Core/Package/PackageWriter.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.IO; -using RePKG.Core.Package; using RePKG.Core.Package.Interfaces; -namespace RePKG.Application.Package +namespace RePKG.Core.Package { public class PackageWriter : IPackageWriter { @@ -15,7 +11,7 @@ public void WriteTo(BinaryWriter writer, Core.Package.Package package) if (package.Entries.Count == 0) throw new Exception("Package entries is empty"); - + writer.WriteStringI32Size(package.Magic); WriteEntriesHeader(package.Entries, writer); WriteBody(package.Entries, writer); diff --git a/RePKG.Core/RePKG.Core.csproj b/RePKG.Core/RePKG.Core.csproj index 17562c0..b22c0b5 100644 --- a/RePKG.Core/RePKG.Core.csproj +++ b/RePKG.Core/RePKG.Core.csproj @@ -1,7 +1,14 @@  - netstandard2.0 - 0.4.0 + net10.0 + 0.5.0 Copyright © NotScuffed 2025 + enable + enable + + + + + diff --git a/RePKG.Core/Texture/Enums/DXTFlags.cs b/RePKG.Core/Texture/Enums/DXTFlags.cs index ed2e909..f7fcb3c 100644 --- a/RePKG.Core/Texture/Enums/DXTFlags.cs +++ b/RePKG.Core/Texture/Enums/DXTFlags.cs @@ -1,5 +1,3 @@ -using System; - namespace RePKG.Core.Texture { [Flags] diff --git a/RePKG.Core/Texture/Enums/MipmapFormat.cs b/RePKG.Core/Texture/Enums/MipmapFormat.cs index 93ca4c2..dff0ba8 100644 --- a/RePKG.Core/Texture/Enums/MipmapFormat.cs +++ b/RePKG.Core/Texture/Enums/MipmapFormat.cs @@ -3,7 +3,7 @@ namespace RePKG.Core.Texture public enum MipmapFormat { Invalid = 0, - + /// /// Raw pixels (4 bytes per pixel) (RGBA8888) /// @@ -33,7 +33,7 @@ public enum MipmapFormat /// Raw pixels compressed using DXT1 /// CompressedDXT1, - + /// /// MP4 Video /// diff --git a/RePKG.Core/Texture/Enums/TexFlags.cs b/RePKG.Core/Texture/Enums/TexFlags.cs index 63bed4d..b130603 100644 --- a/RePKG.Core/Texture/Enums/TexFlags.cs +++ b/RePKG.Core/Texture/Enums/TexFlags.cs @@ -1,5 +1,3 @@ -using System; - namespace RePKG.Core.Texture { [Flags] @@ -9,8 +7,10 @@ public enum TexFlags NoInterpolation = 1, ClampUVs = 2, IsGif = 4, + // Placeholders Unk3 = 8, + Unk4 = 16, IsVideoTexture = 32, Unk6 = 64, diff --git a/RePKG.Application/Texture/Helpers/DXT.cs b/RePKG.Core/Texture/Helpers/DXT.cs similarity index 99% rename from RePKG.Application/Texture/Helpers/DXT.cs rename to RePKG.Core/Texture/Helpers/DXT.cs index bea30a9..42a87df 100644 --- a/RePKG.Application/Texture/Helpers/DXT.cs +++ b/RePKG.Core/Texture/Helpers/DXT.cs @@ -6,10 +6,10 @@ * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR @@ -21,9 +21,7 @@ // most of the algorithms and data used in this Class-file has been ported from LibSquish! // http://code.google.com/p/libsquish/ -using RePKG.Core.Texture; - -namespace RePKG.Application.Texture.Helpers +namespace RePKG.Core.Texture.Helpers { public static class DXT { @@ -243,4 +241,4 @@ public static byte[] DecompressImage(int width, int height, byte[] data, DXTFlag return rgba; } } -} +} \ No newline at end of file diff --git a/RePKG.Core/Texture/Helpers/EnumExtensions.cs b/RePKG.Core/Texture/Helpers/EnumExtensions.cs index e814ec7..dc30212 100644 --- a/RePKG.Core/Texture/Helpers/EnumExtensions.cs +++ b/RePKG.Core/Texture/Helpers/EnumExtensions.cs @@ -14,14 +14,15 @@ public static bool IsValid(this TexFormat format) case TexFormat.RG88: case TexFormat.R8: return true; + default: return false; } } - + public static bool IsValid(this FreeImageFormat format) { - var formatId = (int) format; + var formatId = (int)format; return formatId >= -1 && formatId <= 35; } diff --git a/RePKG.Core/Texture/Helpers/MipmapFormatExtensions.cs b/RePKG.Core/Texture/Helpers/MipmapFormatExtensions.cs index 6b91509..8cfbe6a 100644 --- a/RePKG.Core/Texture/Helpers/MipmapFormatExtensions.cs +++ b/RePKG.Core/Texture/Helpers/MipmapFormatExtensions.cs @@ -1,5 +1,3 @@ -using System; - namespace RePKG.Core.Texture { public static class MipmapFormatExtensions @@ -9,18 +7,18 @@ public static class MipmapFormatExtensions /// public static bool IsImage(this MipmapFormat format) { - return (int) format >= 1000; + return (int)format >= 1000; } - + /// /// Checks if the mipmap format is an raw uncompressed format /// public static bool IsRawFormat(this MipmapFormat format) { - var formatId = (int) format; + var formatId = (int)format; return formatId >= 1 && formatId <= 3; } - + /// /// Checks if the mipmap format is an raw compressed format /// @@ -32,7 +30,7 @@ public static bool IsCompressed(this MipmapFormat format) case MipmapFormat.CompressedDXT3: case MipmapFormat.CompressedDXT1: return true; - + default: return false; } @@ -48,75 +46,109 @@ public static string GetFileExtension(this MipmapFormat format) { case MipmapFormat.ImageBMP: return "bmp"; + case MipmapFormat.ImageICO: return "ico"; + case MipmapFormat.ImageJPEG: return "jpg"; + case MipmapFormat.ImageJNG: return "jng"; + case MipmapFormat.ImageKOALA: return "koa"; + case MipmapFormat.ImageLBM: return "lbm"; + case MipmapFormat.ImageIFF: return "iff"; + case MipmapFormat.ImageMNG: return "mng"; + case MipmapFormat.ImagePBM: case MipmapFormat.ImagePBMRAW: return "pbm"; + case MipmapFormat.ImagePCD: return "pcd"; + case MipmapFormat.ImagePCX: return "pcx"; + case MipmapFormat.ImagePGM: case MipmapFormat.ImagePGMRAW: return "pgm"; + case MipmapFormat.ImagePNG: return "png"; + case MipmapFormat.ImagePPM: case MipmapFormat.ImagePPMRAW: return "ppm"; + case MipmapFormat.ImageRAS: return "ras"; + case MipmapFormat.ImageTARGA: return "tga"; + case MipmapFormat.ImageTIFF: return "tif"; + case MipmapFormat.ImageWBMP: return "wbmp"; + case MipmapFormat.ImagePSD: return "psd"; + case MipmapFormat.ImageCUT: return "cut"; + case MipmapFormat.ImageXBM: return "xbm"; + case MipmapFormat.ImageXPM: return "xpm"; + case MipmapFormat.ImageDDS: return "dds"; + case MipmapFormat.ImageGIF: return "gif"; + case MipmapFormat.ImageHDR: return "hdr"; + case MipmapFormat.ImageFAXG3: return "g3"; + case MipmapFormat.ImageSGI: return "sgi"; + case MipmapFormat.ImageEXR: return "exr"; + case MipmapFormat.ImageJ2K: return "j2k"; + case MipmapFormat.ImageJP2: return "jp2"; + case MipmapFormat.ImagePFM: return "pfm"; + case MipmapFormat.ImagePICT: return "pict"; + case MipmapFormat.ImageRAW: return "raw"; + case MipmapFormat.VideoMp4: return "mp4"; + default: throw new ArgumentOutOfRangeException(nameof(format), format, null); } diff --git a/RePKG.Application/Texture/Helpers/RG88.cs b/RePKG.Core/Texture/Helpers/RG88.cs similarity index 96% rename from RePKG.Application/Texture/Helpers/RG88.cs rename to RePKG.Core/Texture/Helpers/RG88.cs index e1caf47..2ddd283 100644 --- a/RePKG.Application/Texture/Helpers/RG88.cs +++ b/RePKG.Core/Texture/Helpers/RG88.cs @@ -1,10 +1,10 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.PixelFormats; -namespace RePKG.Application.Texture.Helpers +namespace RePKG.Core.Texture.Helpers { [StructLayout(LayoutKind.Sequential)] internal struct RG88 : IPixel, IPackedVector @@ -21,6 +21,7 @@ public RG88(byte r, byte g) R = r; G = g; } + public ushort Rg { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -33,7 +34,7 @@ public ushort Rg public uint PackedValue { get => Rg; - set => Rg = (ushort) value; + set => Rg = (ushort)value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -136,7 +137,7 @@ public void ToRgba32(ref Rgba32 dest) public static byte DownScaleFrom16BitTo8Bit(ushort component) { - return (byte) (component * byte.MaxValue + 32895 >> 16); + return (byte)(component * byte.MaxValue + 32895 >> 16); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -170,8 +171,8 @@ private void Pack(ref Vector4 vector) vector += Half; vector = Vector4.Clamp(vector, Vector4.Zero, MaxBytes); - R = (byte) vector.X; - G = (byte) vector.Y; + R = (byte)vector.X; + G = (byte)vector.Y; } } } \ No newline at end of file diff --git a/RePKG.Core/Texture/Helpers/TexMipmapFormatGetter.cs b/RePKG.Core/Texture/Helpers/TexMipmapFormatGetter.cs index 2f0fd83..6df4c46 100644 --- a/RePKG.Core/Texture/Helpers/TexMipmapFormatGetter.cs +++ b/RePKG.Core/Texture/Helpers/TexMipmapFormatGetter.cs @@ -1,5 +1,3 @@ -using System; - namespace RePKG.Core.Texture { public static class TexMipmapFormatGetter @@ -8,26 +6,32 @@ public static MipmapFormat GetFormatForTex(Tex tex) { return GetFormatForTex(tex.ImagesContainer.ImageFormat, tex.Header.Format); } - + public static MipmapFormat GetFormatForTex(FreeImageFormat imageFormat, TexFormat texFormat) { if (imageFormat != FreeImageFormat.FIF_UNKNOWN) return FreeImageFormatToMipmapFormat(imageFormat); - + switch (texFormat) { case TexFormat.RGBA8888: return MipmapFormat.RGBA8888; + case TexFormat.DXT5: return MipmapFormat.CompressedDXT5; + case TexFormat.DXT3: return MipmapFormat.CompressedDXT3; + case TexFormat.DXT1: return MipmapFormat.CompressedDXT1; + case TexFormat.R8: return MipmapFormat.R8; + case TexFormat.RG88: return MipmapFormat.RG88; + default: throw new ArgumentOutOfRangeException(); } @@ -39,113 +43,115 @@ private static MipmapFormat FreeImageFormatToMipmapFormat(FreeImageFormat freeIm { case FreeImageFormat.FIF_UNKNOWN: throw new Exception($"Can't convert '{freeImageFormat}' to '{typeof(MipmapFormat)}'"); - + case FreeImageFormat.FIF_BMP: return MipmapFormat.ImageBMP; - + case FreeImageFormat.FIF_ICO: return MipmapFormat.ImageICO; - + case FreeImageFormat.FIF_JPEG: return MipmapFormat.ImageJPEG; - + case FreeImageFormat.FIF_JNG: return MipmapFormat.ImageJNG; - + case FreeImageFormat.FIF_KOALA: return MipmapFormat.ImageKOALA; - + case FreeImageFormat.FIF_LBM: return MipmapFormat.ImageLBM; - + case FreeImageFormat.FIF_MNG: return MipmapFormat.ImageMNG; - + case FreeImageFormat.FIF_PBM: return MipmapFormat.ImagePBM; - + case FreeImageFormat.FIF_PBMRAW: return MipmapFormat.ImagePBMRAW; - + case FreeImageFormat.FIF_PCD: return MipmapFormat.ImagePCD; - + case FreeImageFormat.FIF_PCX: return MipmapFormat.ImagePCX; - + case FreeImageFormat.FIF_PGM: return MipmapFormat.ImagePGM; - + case FreeImageFormat.FIF_PGMRAW: return MipmapFormat.ImagePGMRAW; - + case FreeImageFormat.FIF_PNG: return MipmapFormat.ImagePNG; - + case FreeImageFormat.FIF_PPM: return MipmapFormat.ImagePPM; - + case FreeImageFormat.FIF_PPMRAW: return MipmapFormat.ImagePPMRAW; - + case FreeImageFormat.FIF_RAS: return MipmapFormat.ImageRAS; - + case FreeImageFormat.FIF_TARGA: return MipmapFormat.ImageTARGA; - + case FreeImageFormat.FIF_TIFF: return MipmapFormat.ImageTIFF; - + case FreeImageFormat.FIF_WBMP: return MipmapFormat.ImageWBMP; - + case FreeImageFormat.FIF_PSD: return MipmapFormat.ImagePSD; - + case FreeImageFormat.FIF_CUT: return MipmapFormat.ImageCUT; - + case FreeImageFormat.FIF_XBM: return MipmapFormat.ImageXBM; - + case FreeImageFormat.FIF_XPM: return MipmapFormat.ImageXPM; - + case FreeImageFormat.FIF_DDS: return MipmapFormat.ImageDDS; - + case FreeImageFormat.FIF_GIF: return MipmapFormat.ImageGIF; - + case FreeImageFormat.FIF_HDR: return MipmapFormat.ImageHDR; - + case FreeImageFormat.FIF_FAXG3: return MipmapFormat.ImageFAXG3; - + case FreeImageFormat.FIF_SGI: return MipmapFormat.ImageSGI; - + case FreeImageFormat.FIF_EXR: return MipmapFormat.ImageEXR; - + case FreeImageFormat.FIF_J2K: return MipmapFormat.ImageJ2K; - + case FreeImageFormat.FIF_JP2: return MipmapFormat.ImageJP2; - + case FreeImageFormat.FIF_PFM: return MipmapFormat.ImagePFM; - + case FreeImageFormat.FIF_PICT: return MipmapFormat.ImagePICT; - + case FreeImageFormat.FIF_RAW: return MipmapFormat.ImageRAW; + case FreeImageFormat.FIF_MP4: return MipmapFormat.VideoMp4; + default: throw new ArgumentOutOfRangeException(nameof(freeImageFormat), freeImageFormat, null); } diff --git a/RePKG.Core/Texture/Interfaces/Data/ITexFrameInfoContainer.cs b/RePKG.Core/Texture/Interfaces/Data/ITexFrameInfoContainer.cs index dba1e68..b678cf0 100644 --- a/RePKG.Core/Texture/Interfaces/Data/ITexFrameInfoContainer.cs +++ b/RePKG.Core/Texture/Interfaces/Data/ITexFrameInfoContainer.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace RePKG.Core.Texture { public interface ITexFrameInfoContainer diff --git a/RePKG.Core/Texture/Interfaces/Data/ITexImage.cs b/RePKG.Core/Texture/Interfaces/Data/ITexImage.cs index 1a22092..7c35dee 100644 --- a/RePKG.Core/Texture/Interfaces/Data/ITexImage.cs +++ b/RePKG.Core/Texture/Interfaces/Data/ITexImage.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace RePKG.Core.Texture { public interface ITexImage diff --git a/RePKG.Core/Texture/Interfaces/Data/ITexImageContainer.cs b/RePKG.Core/Texture/Interfaces/Data/ITexImageContainer.cs index f1eff15..593623c 100644 --- a/RePKG.Core/Texture/Interfaces/Data/ITexImageContainer.cs +++ b/RePKG.Core/Texture/Interfaces/Data/ITexImageContainer.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace RePKG.Core.Texture { public interface ITexImageContainer diff --git a/RePKG.Core/Texture/Interfaces/Data/ITexMipmap.cs b/RePKG.Core/Texture/Interfaces/Data/ITexMipmap.cs index ffcfc61..c64970b 100644 --- a/RePKG.Core/Texture/Interfaces/Data/ITexMipmap.cs +++ b/RePKG.Core/Texture/Interfaces/Data/ITexMipmap.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Texture { public interface ITexMipmap diff --git a/RePKG.Core/Texture/Interfaces/ITexFrameInfoContainerReader.cs b/RePKG.Core/Texture/Interfaces/ITexFrameInfoContainerReader.cs index 59f85cd..ec8aa3f 100644 --- a/RePKG.Core/Texture/Interfaces/ITexFrameInfoContainerReader.cs +++ b/RePKG.Core/Texture/Interfaces/ITexFrameInfoContainerReader.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Texture { public interface ITexFrameInfoContainerReader diff --git a/RePKG.Core/Texture/Interfaces/ITexHeaderReader.cs b/RePKG.Core/Texture/Interfaces/ITexHeaderReader.cs index 7d8a6aa..437d6da 100644 --- a/RePKG.Core/Texture/Interfaces/ITexHeaderReader.cs +++ b/RePKG.Core/Texture/Interfaces/ITexHeaderReader.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Texture { public interface ITexHeaderReader diff --git a/RePKG.Core/Texture/Interfaces/ITexImageContainerReader.cs b/RePKG.Core/Texture/Interfaces/ITexImageContainerReader.cs index 8b3ca66..cffe1d6 100644 --- a/RePKG.Core/Texture/Interfaces/ITexImageContainerReader.cs +++ b/RePKG.Core/Texture/Interfaces/ITexImageContainerReader.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Texture { public interface ITexImageContainerReader diff --git a/RePKG.Core/Texture/Interfaces/ITexImageReader.cs b/RePKG.Core/Texture/Interfaces/ITexImageReader.cs index fca3f59..6bb02f2 100644 --- a/RePKG.Core/Texture/Interfaces/ITexImageReader.cs +++ b/RePKG.Core/Texture/Interfaces/ITexImageReader.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Texture { public interface ITexImageReader diff --git a/RePKG.Core/Texture/Interfaces/ITexMipmapDecompressor.cs b/RePKG.Core/Texture/Interfaces/ITexMipmapDecompressor.cs index 8ceedbe..0a02996 100644 --- a/RePKG.Core/Texture/Interfaces/ITexMipmapDecompressor.cs +++ b/RePKG.Core/Texture/Interfaces/ITexMipmapDecompressor.cs @@ -1,4 +1,4 @@ -namespace RePKG.Core.Texture +namespace RePKG.Core.Texture.Interfaces { public interface ITexMipmapDecompressor { diff --git a/RePKG.Core/Texture/Interfaces/ITexReader.cs b/RePKG.Core/Texture/Interfaces/ITexReader.cs index 5273125..8346917 100644 --- a/RePKG.Core/Texture/Interfaces/ITexReader.cs +++ b/RePKG.Core/Texture/Interfaces/ITexReader.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Texture { public interface ITexReader diff --git a/RePKG.Core/Texture/Interfaces/Writer/ITexFrameInfoContainerWriter.cs b/RePKG.Core/Texture/Interfaces/Writer/ITexFrameInfoContainerWriter.cs index 0d98425..af5b069 100644 --- a/RePKG.Core/Texture/Interfaces/Writer/ITexFrameInfoContainerWriter.cs +++ b/RePKG.Core/Texture/Interfaces/Writer/ITexFrameInfoContainerWriter.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Texture { public interface ITexFrameInfoContainerWriter diff --git a/RePKG.Core/Texture/Interfaces/Writer/ITexHeaderWriter.cs b/RePKG.Core/Texture/Interfaces/Writer/ITexHeaderWriter.cs index a002aef..d2c4854 100644 --- a/RePKG.Core/Texture/Interfaces/Writer/ITexHeaderWriter.cs +++ b/RePKG.Core/Texture/Interfaces/Writer/ITexHeaderWriter.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Texture { public interface ITexHeaderWriter diff --git a/RePKG.Core/Texture/Interfaces/Writer/ITexImageContainerWriter.cs b/RePKG.Core/Texture/Interfaces/Writer/ITexImageContainerWriter.cs index a7ad172..57a6063 100644 --- a/RePKG.Core/Texture/Interfaces/Writer/ITexImageContainerWriter.cs +++ b/RePKG.Core/Texture/Interfaces/Writer/ITexImageContainerWriter.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Texture { public interface ITexImageContainerWriter diff --git a/RePKG.Core/Texture/Interfaces/Writer/ITexImageWriter.cs b/RePKG.Core/Texture/Interfaces/Writer/ITexImageWriter.cs index 7b6a4f8..2a4c0c8 100644 --- a/RePKG.Core/Texture/Interfaces/Writer/ITexImageWriter.cs +++ b/RePKG.Core/Texture/Interfaces/Writer/ITexImageWriter.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Texture { public interface ITexImageWriter diff --git a/RePKG.Core/Texture/Interfaces/Writer/ITexWriter.cs b/RePKG.Core/Texture/Interfaces/Writer/ITexWriter.cs index de75769..71ea6d3 100644 --- a/RePKG.Core/Texture/Interfaces/Writer/ITexWriter.cs +++ b/RePKG.Core/Texture/Interfaces/Writer/ITexWriter.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Texture { public interface ITexWriter diff --git a/RePKG.Core/Texture/Tex.cs b/RePKG.Core/Texture/Tex.cs index ec633a8..60c04ef 100644 --- a/RePKG.Core/Texture/Tex.cs +++ b/RePKG.Core/Texture/Tex.cs @@ -1,5 +1,3 @@ -using System.Linq; - namespace RePKG.Core.Texture { public class Tex : ITex @@ -9,11 +7,11 @@ public class Tex : ITex public ITexHeader Header { get; set; } public ITexImageContainer ImagesContainer { get; set; } public ITexFrameInfoContainer FrameInfoContainer { get; set; } - + public bool IsGif => HasFlag(TexFlags.IsGif); public bool IsVideoTexture => HasFlag(TexFlags.IsVideoTexture); public ITexImage FirstImage => ImagesContainer?.Images.FirstOrDefault(); - + public bool HasFlag(TexFlags flag) { if (Header == null) diff --git a/RePKG.Core/Texture/TexFrameInfoContainer.cs b/RePKG.Core/Texture/TexFrameInfoContainer.cs index 48516c7..a5d1d10 100644 --- a/RePKG.Core/Texture/TexFrameInfoContainer.cs +++ b/RePKG.Core/Texture/TexFrameInfoContainer.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace RePKG.Core.Texture { public class TexFrameInfoContainer : ITexFrameInfoContainer diff --git a/RePKG.Application/Texture/TexFrameInfoContainerReader.cs b/RePKG.Core/Texture/TexFrameInfoContainerReader.cs similarity index 88% rename from RePKG.Application/Texture/TexFrameInfoContainerReader.cs rename to RePKG.Core/Texture/TexFrameInfoContainerReader.cs index 7134d24..84c88d5 100644 --- a/RePKG.Application/Texture/TexFrameInfoContainerReader.cs +++ b/RePKG.Core/Texture/TexFrameInfoContainerReader.cs @@ -1,16 +1,13 @@ -using System; -using System.IO; -using RePKG.Application.Exceptions; -using RePKG.Core.Texture; +using RePKG.Core.Exceptions; -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexFrameInfoContainerReader : ITexFrameInfoContainerReader { public ITexFrameInfoContainer ReadFrom(BinaryReader reader) { - if (reader == null) throw new ArgumentNullException(nameof(reader)); - + ArgumentNullException.ThrowIfNull(reader); + var container = new TexFrameInfoContainer { Magic = reader.ReadNString(maxLength: 16) @@ -54,7 +51,7 @@ public ITexFrameInfoContainer ReadFrom(BinaryReader reader) }); } break; - + case "TEXS0002": case "TEXS0003": for (var i = 0; i < frameCount; i++) @@ -72,7 +69,7 @@ public ITexFrameInfoContainer ReadFrom(BinaryReader reader) }); } break; - + default: throw new UnknownMagicException(nameof(TexFrameInfoContainerReader), container.Magic); } @@ -82,8 +79,8 @@ public ITexFrameInfoContainer ReadFrom(BinaryReader reader) if (container.GifWidth == 0 || container.GifHeight == 0) { - container.GifWidth = (int) container.Frames[0].Width; - container.GifHeight = (int) container.Frames[0].Height; + container.GifWidth = (int)container.Frames[0].Width; + container.GifHeight = (int)container.Frames[0].Height; } return container; diff --git a/RePKG.Application/Texture/TexHeaderReader.cs b/RePKG.Core/Texture/TexHeaderReader.cs similarity index 71% rename from RePKG.Application/Texture/TexHeaderReader.cs rename to RePKG.Core/Texture/TexHeaderReader.cs index 39963b0..9c3fce0 100644 --- a/RePKG.Application/Texture/TexHeaderReader.cs +++ b/RePKG.Core/Texture/TexHeaderReader.cs @@ -1,20 +1,17 @@ -using System; -using System.IO; -using RePKG.Application.Exceptions; -using RePKG.Core.Texture; +using RePKG.Core.Exceptions; -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexHeaderReader : ITexHeaderReader { public ITexHeader ReadFrom(BinaryReader reader) { if (reader == null) throw new ArgumentNullException(nameof(reader)); - + var header = new TexHeader { - Format = (TexFormat) reader.ReadInt32(), - Flags = (TexFlags) reader.ReadInt32(), + Format = (TexFormat)reader.ReadInt32(), + Flags = (TexFlags)reader.ReadInt32(), TextureWidth = reader.ReadInt32(), TextureHeight = reader.ReadInt32(), ImageWidth = reader.ReadInt32(), @@ -24,7 +21,7 @@ public ITexHeader ReadFrom(BinaryReader reader) if (!header.Format.IsValid()) throw new EnumNotValidException(header.Format); - + return header; } } diff --git a/RePKG.Core/Texture/TexImage.cs b/RePKG.Core/Texture/TexImage.cs index f21f379..5aba22c 100644 --- a/RePKG.Core/Texture/TexImage.cs +++ b/RePKG.Core/Texture/TexImage.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Linq; - namespace RePKG.Core.Texture { public class TexImage : ITexImage diff --git a/RePKG.Core/Texture/TexImageContainer.cs b/RePKG.Core/Texture/TexImageContainer.cs index ffc551f..c59fc5d 100644 --- a/RePKG.Core/Texture/TexImageContainer.cs +++ b/RePKG.Core/Texture/TexImageContainer.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace RePKG.Core.Texture { public class TexImageContainer : ITexImageContainer @@ -7,7 +5,7 @@ public class TexImageContainer : ITexImageContainer public string Magic { get; set; } public FreeImageFormat ImageFormat { get; set; } = FreeImageFormat.FIF_UNKNOWN; public IList Images { get; } = new List(); - + public TexImageContainerVersion ImageContainerVersion { get; set; } } } \ No newline at end of file diff --git a/RePKG.Application/Texture/TexImageContainerReader.cs b/RePKG.Core/Texture/TexImageContainerReader.cs similarity index 83% rename from RePKG.Application/Texture/TexImageContainerReader.cs rename to RePKG.Core/Texture/TexImageContainerReader.cs index dfb1ed3..2b0fc2f 100644 --- a/RePKG.Application/Texture/TexImageContainerReader.cs +++ b/RePKG.Core/Texture/TexImageContainerReader.cs @@ -1,9 +1,6 @@ -using System; -using System.IO; -using RePKG.Application.Exceptions; -using RePKG.Core.Texture; +using RePKG.Core.Exceptions; -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexImageContainerReader : ITexImageContainerReader { @@ -16,7 +13,7 @@ public TexImageContainerReader(ITexImageReader texImageReader) public ITexImageContainer ReadFrom(BinaryReader reader, TexFormat texFormat) { - if (reader == null) throw new ArgumentNullException(nameof(reader)); + ArgumentNullException.ThrowIfNull(reader); if (!texFormat.IsValid()) throw new EnumNotValidException(texFormat); @@ -37,9 +34,11 @@ public ITexImageContainer ReadFrom(BinaryReader reader, TexFormat texFormat) case "TEXB0001": case "TEXB0002": break; + case "TEXB0003": - container.ImageFormat = (FreeImageFormat) reader.ReadInt32(); + container.ImageFormat = (FreeImageFormat)reader.ReadInt32(); break; + case "TEXB0004": var format = (FreeImageFormat)reader.ReadInt32(); var isVideoMp4 = reader.ReadInt32() == 1; @@ -52,19 +51,20 @@ public ITexImageContainer ReadFrom(BinaryReader reader, TexFormat texFormat) } container.ImageFormat = format; break; + default: throw new UnknownMagicException(nameof(TexImageContainerReader), container.Magic); } - int version = Convert.ToInt32(container.Magic.Substring(4)); + int version = Convert.ToInt32(container.Magic[4..]); container.ImageContainerVersion = (TexImageContainerVersion)version; - if(container.ImageContainerVersion == TexImageContainerVersion.Version4 + if (container.ImageContainerVersion == TexImageContainerVersion.Version4 && container.ImageFormat != FreeImageFormat.FIF_MP4) { container.ImageContainerVersion = TexImageContainerVersion.Version3; } - + if (!container.ImageFormat.IsValid()) throw new EnumNotValidException(container.ImageFormat); diff --git a/RePKG.Application/Texture/TexImageReader.cs b/RePKG.Core/Texture/TexImageReader.cs similarity index 77% rename from RePKG.Application/Texture/TexImageReader.cs rename to RePKG.Core/Texture/TexImageReader.cs index 7c5fa5e..b47d790 100644 --- a/RePKG.Application/Texture/TexImageReader.cs +++ b/RePKG.Core/Texture/TexImageReader.cs @@ -1,9 +1,7 @@ -using System; -using System.IO; -using RePKG.Application.Exceptions; -using RePKG.Core.Texture; +using RePKG.Core.Exceptions; +using RePKG.Core.Texture.Interfaces; -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexImageReader : ITexImageReader { @@ -21,22 +19,22 @@ public ITexImage ReadFrom( ITexImageContainer container, TexFormat texFormat) { - if (reader == null) throw new ArgumentNullException(nameof(reader)); - if (container == null) throw new ArgumentNullException(nameof(container)); - + ArgumentNullException.ThrowIfNull(reader); + ArgumentNullException.ThrowIfNull(container); + if (!texFormat.IsValid()) throw new EnumNotValidException(texFormat); var mipmapCount = reader.ReadInt32(); - + if (mipmapCount > Constants.MaximumMipmapCount) throw new UnsafeTexException( $"Mipmap count exceeds limit: {mipmapCount}/{Constants.MaximumMipmapCount}"); - + var readFunction = PickMipmapReader(container.ImageContainerVersion); var format = TexMipmapFormatGetter.GetFormatForTex(container.ImageFormat, texFormat); var image = new TexImage(); - + for (var i = 0; i < mipmapCount; i++) { var mipmap = readFunction(reader); @@ -72,24 +70,25 @@ private TexMipmap ReadMipmapV2And3(BinaryReader reader) Bytes = ReadBytes(reader) }; } + private TexMipmap ReadMipmapV4(BinaryReader reader) { /**FIXME - * The role of the following param* parameters cannot be confirmed, + * The role of the following param* parameters cannot be confirmed, * it may be a parameter used in the built-in display of the wallpaper editor and does not need to be processed */ var param1 = reader.ReadInt32(); - if(param1 != 1) + if (param1 != 1) { throw new UnsafeTexException($"ReadMipmapV4 unknow param1 :{param1}"); } - var param2= reader.ReadInt32(); + var param2 = reader.ReadInt32(); if (param2 != 2) { throw new UnsafeTexException($"ReadMipmapV4 unknow param2 :{param2}"); } var conditionJson = reader.ReadNString(); - + var param3 = reader.ReadInt32(); if (param3 != 1) { @@ -104,10 +103,11 @@ private TexMipmap ReadMipmapV4(BinaryReader reader) Bytes = ReadBytes(reader) }; } + private byte[] ReadBytes(BinaryReader reader) { var byteCount = reader.ReadInt32(); - + if (reader.BaseStream.Position + byteCount > reader.BaseStream.Length) throw new UnsafeTexException("Detected invalid mipmap byte count - exceeds stream length"); @@ -122,31 +122,20 @@ private byte[] ReadBytes(BinaryReader reader) } var bytes = new byte[byteCount]; - var bytesRead = reader.Read(bytes, 0, byteCount); - - if (bytesRead != byteCount) - throw new Exception("Failed to read bytes from stream while reading mipmap"); - + reader.BaseStream.ReadExactly(bytes); return bytes; } private Func PickMipmapReader(TexImageContainerVersion containerVersion) { - switch (containerVersion) + return containerVersion switch { - case TexImageContainerVersion.Version1: - return ReadMipmapV1; - - case TexImageContainerVersion.Version2: - case TexImageContainerVersion.Version3: - return ReadMipmapV2And3; - - case TexImageContainerVersion.Version4: - return ReadMipmapV4; - default: - throw new InvalidOperationException( - $"Tex image container version: {containerVersion} is not supported!"); - } + TexImageContainerVersion.Version1 => ReadMipmapV1, + TexImageContainerVersion.Version2 or TexImageContainerVersion.Version3 => ReadMipmapV2And3, + TexImageContainerVersion.Version4 => ReadMipmapV4, + _ => throw new InvalidOperationException( + $"Tex image container version: {containerVersion} is not supported!"), + }; } } -} +} \ No newline at end of file diff --git a/RePKG.Application/Texture/TexJsonInfoGenerator.cs b/RePKG.Core/Texture/TexJsonInfoGenerator.cs similarity index 83% rename from RePKG.Application/Texture/TexJsonInfoGenerator.cs rename to RePKG.Core/Texture/TexJsonInfoGenerator.cs index 0383bc1..00b1e5d 100644 --- a/RePKG.Application/Texture/TexJsonInfoGenerator.cs +++ b/RePKG.Core/Texture/TexJsonInfoGenerator.cs @@ -1,9 +1,7 @@ -using System; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using RePKG.Core.Texture; +using System.Text.Json; +using System.Text.Json.Nodes; -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexJsonInfoGenerator : ITexJsonInfoGenerator { @@ -11,7 +9,7 @@ public string GenerateInfo(ITex tex) { if (tex == null) throw new ArgumentNullException(nameof(tex)); - var json = new JObject + var json = new JsonObject { ["bleedtransparentcolors"] = true, ["clampuvs"] = tex.HasFlag(TexFlags.ClampUVs), @@ -26,10 +24,10 @@ public string GenerateInfo(ITex tex) { if (tex.FrameInfoContainer == null) throw new InvalidOperationException("TEX is animated but doesn't have frame info container"); - - json["spritesheetsequences"] = new JArray + + json["spritesheetsequences"] = new JsonArray { - new JObject + new JsonObject { ["duration"] = 1, // not sure what this value is used for ["frames"] = tex.FrameInfoContainer.Frames.Count, @@ -39,7 +37,7 @@ public string GenerateInfo(ITex tex) }; } - return JsonConvert.SerializeObject(json, Formatting.Indented); + return json.ToJsonString(new JsonSerializerOptions { WriteIndented = true }); } private static bool NumberIsPowerOfTwo(int n) diff --git a/RePKG.Core/Texture/TexMipmap.cs b/RePKG.Core/Texture/TexMipmap.cs index 4a7140e..05f7cb0 100644 --- a/RePKG.Core/Texture/TexMipmap.cs +++ b/RePKG.Core/Texture/TexMipmap.cs @@ -1,5 +1,3 @@ -using System.IO; - namespace RePKG.Core.Texture { public class TexMipmap : ITexMipmap @@ -10,7 +8,7 @@ public class TexMipmap : ITexMipmap public int DecompressedBytesCount { get; set; } public bool IsLZ4Compressed { get; set; } public MipmapFormat Format { get; set; } - + public Stream GetBytesStream() { return new MemoryStream(Bytes); diff --git a/RePKG.Application/Texture/TexMipmapDecompressor.cs b/RePKG.Core/Texture/TexMipmapDecompressor.cs similarity index 82% rename from RePKG.Application/Texture/TexMipmapDecompressor.cs rename to RePKG.Core/Texture/TexMipmapDecompressor.cs index b036b9a..5cd52e4 100644 --- a/RePKG.Application/Texture/TexMipmapDecompressor.cs +++ b/RePKG.Core/Texture/TexMipmapDecompressor.cs @@ -1,15 +1,14 @@ -using System; using K4os.Compression.LZ4; -using RePKG.Application.Texture.Helpers; -using RePKG.Core.Texture; +using RePKG.Core.Texture.Helpers; +using RePKG.Core.Texture.Interfaces; -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexMipmapDecompressor : ITexMipmapDecompressor { public void DecompressMipmap(ITexMipmap mipmap) { - if (mipmap == null) throw new ArgumentNullException(nameof(mipmap)); + ArgumentNullException.ThrowIfNull(mipmap); if (mipmap.IsLZ4Compressed) { @@ -26,10 +25,12 @@ public void DecompressMipmap(ITexMipmap mipmap) mipmap.Bytes = DXT.DecompressImage(mipmap.Width, mipmap.Height, mipmap.Bytes, DXTFlags.DXT5); mipmap.Format = MipmapFormat.RGBA8888; break; + case MipmapFormat.CompressedDXT3: mipmap.Bytes = DXT.DecompressImage(mipmap.Width, mipmap.Height, mipmap.Bytes, DXTFlags.DXT3); mipmap.Format = MipmapFormat.RGBA8888; break; + case MipmapFormat.CompressedDXT1: mipmap.Bytes = DXT.DecompressImage(mipmap.Width, mipmap.Height, mipmap.Bytes, DXTFlags.DXT1); mipmap.Format = MipmapFormat.RGBA8888; @@ -39,12 +40,10 @@ public void DecompressMipmap(ITexMipmap mipmap) private byte[] Lz4Decompress(byte[] bytes, int knownLength) { - var buffer = new byte[knownLength]; - - LZ4Codec.Decode( - bytes, 0, bytes.Length, - buffer, 0, buffer.Length); + ArgumentNullException.ThrowIfNull(bytes); + var buffer = new byte[knownLength]; + LZ4Codec.Decode(bytes.AsSpan(), buffer.AsSpan()); return buffer; } } diff --git a/RePKG.Application/Texture/TexReader.cs b/RePKG.Core/Texture/TexReader.cs similarity index 87% rename from RePKG.Application/Texture/TexReader.cs rename to RePKG.Core/Texture/TexReader.cs index 82a8c4b..499bc19 100644 --- a/RePKG.Application/Texture/TexReader.cs +++ b/RePKG.Core/Texture/TexReader.cs @@ -1,9 +1,6 @@ -using System; -using System.IO; -using RePKG.Application.Exceptions; -using RePKG.Core.Texture; +using RePKG.Core.Exceptions; -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexReader : ITexReader { @@ -37,9 +34,9 @@ public static TexReader Default public ITex ReadFrom(BinaryReader reader) { - if (reader == null) throw new ArgumentNullException(nameof(reader)); + ArgumentNullException.ThrowIfNull(reader); - var tex = new Tex {Magic1 = reader.ReadNString(maxLength: 16)}; + var tex = new Tex { Magic1 = reader.ReadNString(maxLength: 16) }; if (tex.Magic1 != "TEXV0005") throw new UnknownMagicException(nameof(TexReader), nameof(tex.Magic1), tex.Magic1); diff --git a/RePKG.Application/Texture/TexToImageConverter.cs b/RePKG.Core/Texture/TexToImageConverter.cs similarity index 91% rename from RePKG.Application/Texture/TexToImageConverter.cs rename to RePKG.Core/Texture/TexToImageConverter.cs index 2839e25..7d487b2 100644 --- a/RePKG.Application/Texture/TexToImageConverter.cs +++ b/RePKG.Core/Texture/TexToImageConverter.cs @@ -1,14 +1,11 @@ -using System; -using System.IO; -using System.Text; -using RePKG.Application.Texture.Helpers; -using RePKG.Core.Texture; +using RePKG.Core.Texture.Helpers; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using System.Text; -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexToImageConverter { @@ -18,7 +15,7 @@ public ImageResult ConvertToImage(ITex tex) if (tex.IsGif) return ConvertToGif(tex); - + var sourceMipmap = tex.FirstImage.FirstMipmap; if (tex.IsVideoTexture) @@ -36,7 +33,7 @@ public ImageResult ConvertToImage(ITex tex) { throw new InvalidOperationException("Expected mp4 magic header"); } - + return new ImageResult { Bytes = sourceMipmap.Bytes, @@ -104,7 +101,7 @@ private static ImageResult ConvertToGif(ITex tex) var image = ImageFromRawFormat(frameFormat, null, tex.FrameInfoContainer.GifWidth, tex.FrameInfoContainer.GifHeight); - + var sequenceImages = new Image[tex.ImagesContainer.Images.Count]; for (var i = 0; i < sequenceImages.Length; i++) @@ -128,14 +125,14 @@ private static ImageResult ConvertToGif(ITex tex) var frame = sequenceImages[frameInfo.ImageId].Clone( context => context.Crop(new Rectangle( - (int) x, - (int) y, - (int) Math.Abs(width), - (int) Math.Abs(height)) - ).Rotate((float) Math.Round(rotationAngle * 180 / Math.PI))); + (int)x, + (int)y, + (int)Math.Abs(width), + (int)Math.Abs(height)) + ).Rotate((float)Math.Round(rotationAngle * 180 / Math.PI))); var metadata = frame.Frames.RootFrame.Metadata.GetFormatMetadata(GifFormat.Instance); - metadata.FrameDelay = (int) Math.Round(frameInfo.Frametime * 100.0f); + metadata.FrameDelay = (int)Math.Round(frameInfo.Frametime * 100.0f); image.Frames.AddFrame(frame.Frames[0]); } @@ -145,7 +142,7 @@ private static ImageResult ConvertToGif(ITex tex) using (var memoryStream = new MemoryStream()) { - image.SaveAsGif(memoryStream, new GifEncoder {ColorTableMode = GifColorTableMode.Local}); + image.SaveAsGif(memoryStream, new GifEncoder { ColorTableMode = GifColorTableMode.Local }); return new ImageResult { diff --git a/RePKG.Application/Texture/Writer/TexFrameInfoContainerWriter.cs b/RePKG.Core/Texture/Writer/TexFrameInfoContainerWriter.cs similarity index 84% rename from RePKG.Application/Texture/Writer/TexFrameInfoContainerWriter.cs rename to RePKG.Core/Texture/Writer/TexFrameInfoContainerWriter.cs index 2356633..2cecbd7 100644 --- a/RePKG.Application/Texture/Writer/TexFrameInfoContainerWriter.cs +++ b/RePKG.Core/Texture/Writer/TexFrameInfoContainerWriter.cs @@ -1,9 +1,6 @@ -using System; -using System.IO; -using RePKG.Application.Exceptions; -using RePKG.Core.Texture; +using RePKG.Core.Exceptions; -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexFrameInfoContainerWriter : ITexFrameInfoContainerWriter { @@ -40,12 +37,12 @@ private static void WriteV1(ITexFrameInfoContainer container, BinaryWriter write { writer.Write(frame.ImageId); writer.Write(frame.Frametime); - writer.Write((int) frame.X); - writer.Write((int) frame.Y); - writer.Write((int) frame.Width); - writer.Write((int) frame.WidthY); - writer.Write((int) frame.HeightX); - writer.Write((int) frame.Height); + writer.Write((int)frame.X); + writer.Write((int)frame.Y); + writer.Write((int)frame.Width); + writer.Write((int)frame.WidthY); + writer.Write((int)frame.HeightX); + writer.Write((int)frame.Height); } } diff --git a/RePKG.Application/Texture/Writer/TexHeaderWriter.cs b/RePKG.Core/Texture/Writer/TexHeaderWriter.cs similarity index 74% rename from RePKG.Application/Texture/Writer/TexHeaderWriter.cs rename to RePKG.Core/Texture/Writer/TexHeaderWriter.cs index 45915b7..259fc99 100644 --- a/RePKG.Application/Texture/Writer/TexHeaderWriter.cs +++ b/RePKG.Core/Texture/Writer/TexHeaderWriter.cs @@ -1,8 +1,4 @@ -using System; -using System.IO; -using RePKG.Core.Texture; - -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexHeaderWriter : ITexHeaderWriter { @@ -11,8 +7,8 @@ public void WriteTo(BinaryWriter writer, ITexHeader header) if (writer == null) throw new ArgumentNullException(nameof(writer)); if (header == null) throw new ArgumentNullException(nameof(header)); - writer.Write((int) header.Format); - writer.Write((int) header.Flags); + writer.Write((int)header.Format); + writer.Write((int)header.Flags); writer.Write(header.TextureWidth); writer.Write(header.TextureHeight); writer.Write(header.ImageWidth); diff --git a/RePKG.Application/Texture/Writer/TexImageContainerWriter.cs b/RePKG.Core/Texture/Writer/TexImageContainerWriter.cs similarity index 85% rename from RePKG.Application/Texture/Writer/TexImageContainerWriter.cs rename to RePKG.Core/Texture/Writer/TexImageContainerWriter.cs index b422296..ca366e2 100644 --- a/RePKG.Application/Texture/Writer/TexImageContainerWriter.cs +++ b/RePKG.Core/Texture/Writer/TexImageContainerWriter.cs @@ -1,9 +1,6 @@ -using System; -using System.IO; -using RePKG.Application.Exceptions; -using RePKG.Core.Texture; +using RePKG.Core.Exceptions; -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexImageContainerWriter : ITexImageContainerWriter { @@ -18,7 +15,7 @@ public void WriteTo(BinaryWriter writer, ITexImageContainer imageContainer) { if (writer == null) throw new ArgumentNullException(nameof(writer)); if (imageContainer == null) throw new ArgumentNullException(nameof(imageContainer)); - + writer.WriteNString(imageContainer.Magic); writer.Write(imageContainer.Images.Count); @@ -29,13 +26,13 @@ public void WriteTo(BinaryWriter writer, ITexImageContainer imageContainer) break; case TexImageContainerVersion.Version3: - writer.Write((int) imageContainer.ImageFormat); + writer.Write((int)imageContainer.ImageFormat); break; default: throw new UnknownMagicException(nameof(TexImageContainerWriter), imageContainer.Magic); } - + foreach (var image in imageContainer.Images) { _texImageWriter.WriteTo(writer, imageContainer.ImageContainerVersion, image); diff --git a/RePKG.Application/Texture/Writer/TexImageWriter.cs b/RePKG.Core/Texture/Writer/TexImageWriter.cs similarity index 91% rename from RePKG.Application/Texture/Writer/TexImageWriter.cs rename to RePKG.Core/Texture/Writer/TexImageWriter.cs index 954452f..794c900 100644 --- a/RePKG.Application/Texture/Writer/TexImageWriter.cs +++ b/RePKG.Core/Texture/Writer/TexImageWriter.cs @@ -1,8 +1,4 @@ -using System; -using System.IO; -using RePKG.Core.Texture; - -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexImageWriter : ITexImageWriter { @@ -32,7 +28,7 @@ private static void WriteMipmapV1(BinaryWriter writer, ITexMipmap mipmap) using (var stream = mipmap.GetBytesStream()) { - writer.Write((int) stream.Length); + writer.Write((int)stream.Length); writer.Flush(); stream.CopyTo(writer.BaseStream); } @@ -47,7 +43,7 @@ private static void WriteMipmapV2And3(BinaryWriter writer, ITexMipmap mipmap) using (var stream = mipmap.GetBytesStream()) { - writer.Write((int) stream.Length); + writer.Write((int)stream.Length); writer.Flush(); stream.CopyTo(writer.BaseStream); } diff --git a/RePKG.Application/Texture/Writer/TexMipmapCompressor.cs b/RePKG.Core/Texture/Writer/TexMipmapCompressor.cs similarity index 91% rename from RePKG.Application/Texture/Writer/TexMipmapCompressor.cs rename to RePKG.Core/Texture/Writer/TexMipmapCompressor.cs index cb69ec0..af45983 100644 --- a/RePKG.Application/Texture/Writer/TexMipmapCompressor.cs +++ b/RePKG.Core/Texture/Writer/TexMipmapCompressor.cs @@ -1,8 +1,6 @@ -using System; using K4os.Compression.LZ4; -using RePKG.Core.Texture; -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexMipmapCompressor : ITexMipmapCompressor { @@ -16,7 +14,7 @@ public void CompressMipmap( if (targetCompressFormat != mipmap.Format) throw new NotSupportedException("Changing mipmap format is not yet supported"); - + if (lz4Compress) LZ4Compress(mipmap); } @@ -29,10 +27,10 @@ private static void LZ4Compress(ITexMipmap mipmap) var compressedSize = LZ4Codec.Encode( bytes, 0, bytes.Length, buffer, 0, buffer.Length); - + if (compressedSize < maximumSize) Array.Resize(ref buffer, compressedSize); - + mipmap.DecompressedBytesCount = bytes.Length; mipmap.Bytes = buffer; mipmap.IsLZ4Compressed = true; diff --git a/RePKG.Application/Texture/Writer/TexWriter.cs b/RePKG.Core/Texture/Writer/TexWriter.cs similarity index 93% rename from RePKG.Application/Texture/Writer/TexWriter.cs rename to RePKG.Core/Texture/Writer/TexWriter.cs index d0f164a..76491f7 100644 --- a/RePKG.Application/Texture/Writer/TexWriter.cs +++ b/RePKG.Core/Texture/Writer/TexWriter.cs @@ -1,9 +1,6 @@ -using System; -using System.IO; -using RePKG.Application.Exceptions; -using RePKG.Core.Texture; +using RePKG.Core.Exceptions; -namespace RePKG.Application.Texture +namespace RePKG.Core.Texture { public class TexWriter : ITexWriter { diff --git a/RePKG.Tests/PkgWriterTests.cs b/RePKG.Tests/PkgWriterTests.cs index b7ec8ec..52345ce 100644 --- a/RePKG.Tests/PkgWriterTests.cs +++ b/RePKG.Tests/PkgWriterTests.cs @@ -1,9 +1,7 @@ -using System.IO; -using System.Text; using NUnit.Framework; -using RePKG.Application.Package; using RePKG.Core.Package; using RePKG.Core.Package.Interfaces; +using System.Text; namespace RePKG.Tests { @@ -13,7 +11,7 @@ public class PkgWriterTests [Test] public void TestWriteAndRead() { - var package = new Package {Magic = "PKGV0005"}; + var package = new Package { Magic = "PKGV0005" }; package.Entries.Add(new PackageEntry { @@ -37,8 +35,8 @@ public void TestWriteAndRead() // Read stream.Position = 0; - var packageReader = new PackageReader {ReadEntryBytes = true}; - + var packageReader = new PackageReader { ReadEntryBytes = true }; + Package readPackage; using (var binaryReader = new BinaryReader(stream, Encoding.UTF8, true)) { diff --git a/RePKG.Tests/RePKG.Tests.csproj b/RePKG.Tests/RePKG.Tests.csproj index b5df6b5..53ea47f 100644 --- a/RePKG.Tests/RePKG.Tests.csproj +++ b/RePKG.Tests/RePKG.Tests.csproj @@ -1,17 +1,19 @@  - net472 + net10.0 false true + enable + enable - - - + + + - + diff --git a/RePKG.Tests/TestHelper.cs b/RePKG.Tests/TestHelper.cs index 4356eca..1f8dabd 100644 --- a/RePKG.Tests/TestHelper.cs +++ b/RePKG.Tests/TestHelper.cs @@ -1,5 +1,3 @@ -using System; - namespace RePKG.Tests { public static class TestHelper @@ -7,10 +5,10 @@ public static class TestHelper static TestHelper() { BasePath = AppContext.BaseDirectory.Split( - new[] {"RePKG.Tests"}, + new[] { "RePKG.Tests" }, StringSplitOptions.RemoveEmptyEntries)[0] + "RePKG.Tests"; } - + public static string BasePath { get; } } } \ No newline at end of file diff --git a/RePKG.Tests/TexDecompressingTests.cs b/RePKG.Tests/TexDecompressingTests.cs index 72bab05..afb61a5 100644 --- a/RePKG.Tests/TexDecompressingTests.cs +++ b/RePKG.Tests/TexDecompressingTests.cs @@ -1,9 +1,6 @@ -using System; -using System.IO; -using System.Text; using NUnit.Framework; -using RePKG.Application.Texture; using RePKG.Core.Texture; +using System.Text; namespace RePKG.Tests { @@ -71,9 +68,9 @@ public void TestTexDecompressing( private void ConvertToImageAndSave(ITex tex, string name) { var resultImage = _texToImageConverter.ConvertToImage(tex); - + var path = $"{TestHelper.BasePath}\\{OutputDirectoryName}\\{name}.{resultImage.Format.GetFileExtension()}"; - + File.WriteAllBytes(path, resultImage.Bytes); } diff --git a/RePKG.Tests/TexWriterTests.cs b/RePKG.Tests/TexWriterTests.cs index cfc6935..1d04dd0 100644 --- a/RePKG.Tests/TexWriterTests.cs +++ b/RePKG.Tests/TexWriterTests.cs @@ -1,9 +1,6 @@ -using System; -using System.IO; -using System.Text; using NUnit.Framework; -using RePKG.Application.Texture; using RePKG.Core.Texture; +using System.Text; namespace RePKG.Tests { @@ -31,7 +28,6 @@ public void Setup() _writer = TexWriter.Default; } - // V%i - The number is TexImageContainer.ImageContainerVersion [Test] [TestCase("V1_DXT5")] @@ -48,13 +44,12 @@ public void Setup() [TestCase("V3_DXT5")] [TestCase("V3_RGBA8888_GIF_TEXS0003")] [TestCase("V3_VIDEOTEXTURE_MP4")] - // TODO: V4_ ones are failing but we don't really need writing functionality public void TestWriteAndRead(string name) { // Load file var inputFileReader = TexDecompressingTests.LoadTestFile(name); var inputBytes = new byte[inputFileReader.BaseStream.Length]; - var bytesRead = inputFileReader.Read(inputBytes, 0, (int) inputFileReader.BaseStream.Length); + var bytesRead = inputFileReader.Read(inputBytes, 0, (int)inputFileReader.BaseStream.Length); Assert.AreEqual(inputFileReader.BaseStream.Length, bytesRead, "Failed to read input file"); inputFileReader.Close(); diff --git a/RePKG.sln b/RePKG.sln deleted file mode 100644 index 788a73a..0000000 --- a/RePKG.sln +++ /dev/null @@ -1,43 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.271 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RePKG", "RePKG\RePKG.csproj", "{A014E252-FB25-4388-B17A-8DD1956B2093}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RePKG.Tests", "RePKG.Tests\RePKG.Tests.csproj", "{00DDEB73-18AF-463B-BEDC-7BA3DCBFA223}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RePKG.Core", "RePKG.Core\RePKG.Core.csproj", "{43CFFA8A-BDB4-4A09-9D07-95FA09CCCEC9}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RePKG.Application", "RePKG.Application\RePKG.Application.csproj", "{DDFFD8D1-3961-4F1B-B778-1CAC5622AA19}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A014E252-FB25-4388-B17A-8DD1956B2093}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A014E252-FB25-4388-B17A-8DD1956B2093}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A014E252-FB25-4388-B17A-8DD1956B2093}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A014E252-FB25-4388-B17A-8DD1956B2093}.Release|Any CPU.Build.0 = Release|Any CPU - {00DDEB73-18AF-463B-BEDC-7BA3DCBFA223}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {00DDEB73-18AF-463B-BEDC-7BA3DCBFA223}.Debug|Any CPU.Build.0 = Debug|Any CPU - {00DDEB73-18AF-463B-BEDC-7BA3DCBFA223}.Release|Any CPU.ActiveCfg = Release|Any CPU - {00DDEB73-18AF-463B-BEDC-7BA3DCBFA223}.Release|Any CPU.Build.0 = Release|Any CPU - {43CFFA8A-BDB4-4A09-9D07-95FA09CCCEC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {43CFFA8A-BDB4-4A09-9D07-95FA09CCCEC9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {43CFFA8A-BDB4-4A09-9D07-95FA09CCCEC9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {43CFFA8A-BDB4-4A09-9D07-95FA09CCCEC9}.Release|Any CPU.Build.0 = Release|Any CPU - {DDFFD8D1-3961-4F1B-B778-1CAC5622AA19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DDFFD8D1-3961-4F1B-B778-1CAC5622AA19}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DDFFD8D1-3961-4F1B-B778-1CAC5622AA19}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DDFFD8D1-3961-4F1B-B778-1CAC5622AA19}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {6B20EA40-8A64-4322-911F-56D09593E7E2} - EndGlobalSection -EndGlobal diff --git a/RePKG.slnx b/RePKG.slnx new file mode 100644 index 0000000..8aeeaa3 --- /dev/null +++ b/RePKG.slnx @@ -0,0 +1,5 @@ + + + + + diff --git a/RePKG/Command/Extract.cs b/RePKG/Command/Extract.cs deleted file mode 100644 index 8e4efce..0000000 --- a/RePKG/Command/Extract.cs +++ /dev/null @@ -1,416 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Text; -using CommandLine; -using Newtonsoft.Json; -using RePKG.Application.Package; -using RePKG.Application.Texture; -using RePKG.Core.Package; -using RePKG.Core.Package.Enums; -using RePKG.Core.Package.Interfaces; -using RePKG.Core.Texture; - -namespace RePKG.Command -{ - public static class Extract - { - private static ExtractOptions _options; - private static string[] _skipExtArray; - private static string[] _onlyExtArray; - private static readonly string[] ProjectFiles = {"project.json"}; - - private static readonly ITexReader _texReader; - private static readonly ITexJsonInfoGenerator _texJsonInfoGenerator; - private static readonly IPackageReader _packageReader; - private static readonly TexToImageConverter _texToImageConverter; - - static Extract() - { - _texReader = TexReader.Default; - _texJsonInfoGenerator = new TexJsonInfoGenerator(); - _texToImageConverter = new TexToImageConverter(); - - _packageReader = new PackageReader(); - } - - public static void Action(ExtractOptions options) - { - _options = options; - - if (string.IsNullOrEmpty(options.OutputDirectory)) - { - options.OutputDirectory = Directory.GetCurrentDirectory(); - } - - if (!string.IsNullOrEmpty(_options.IgnoreExts)) - _skipExtArray = NormalizeExtensions(_options.IgnoreExts.Split(',')); - - if (!string.IsNullOrEmpty(_options.OnlyExts)) - _onlyExtArray = NormalizeExtensions(_options.OnlyExts.Split(',')); - - var fileInfo = new FileInfo(options.Input); - var directoryInfo = new DirectoryInfo(options.Input); - - if (!fileInfo.Exists) - { - if (directoryInfo.Exists) - { - if (_options.TexDirectory) - ExtractTexDirectory(directoryInfo); - else - ExtractPkgDirectory(directoryInfo); - - Console.WriteLine("Done"); - return; - } - - Console.WriteLine("Input file not found"); - Console.WriteLine(options.Input); - return; - } - - ExtractFile(fileInfo); - Console.WriteLine("Done"); - } - - private static string[] NormalizeExtensions(string[] array) - { - for (int i = 0; i < array.Length; i++) - { - if (array[i].StartsWith(".")) - continue; - array[i] = '.' + array[i]; - } - - return array; - } - - private static void ExtractTexDirectory(DirectoryInfo directoryInfo) - { - var flags = SearchOption.TopDirectoryOnly; - - if (_options.Recursive) - flags = SearchOption.AllDirectories; - - Directory.CreateDirectory(_options.OutputDirectory); - - foreach (var fileInfo in directoryInfo.EnumerateFiles("*.tex", flags)) - { - if (!fileInfo.Extension.Equals(".tex", StringComparison.OrdinalIgnoreCase)) - continue; - - try - { - var tex = LoadTex(File.ReadAllBytes(fileInfo.FullName), fileInfo.FullName); - - if (tex == null) - continue; - - var filePath = Path.Combine(_options.OutputDirectory, - Path.GetFileNameWithoutExtension(fileInfo.Name)); - - ConvertToImageAndSave(tex, filePath, _options.Overwrite); - var jsonInfo = _texJsonInfoGenerator.GenerateInfo(tex); - File.WriteAllText($"{filePath}.tex-json", jsonInfo); - } - catch (Exception e) - { - Console.WriteLine("Failed to write texture"); - Console.WriteLine(e); - } - } - } - - private static void ExtractPkgDirectory(DirectoryInfo directoryInfo) - { - var rootDirectoryLength = directoryInfo.FullName.Length + 1; - - if (_options.Recursive) - { - foreach (var file in directoryInfo.EnumerateFiles("*.pkg", SearchOption.AllDirectories)) - { - if (file.Directory == null || file.Directory.FullName.Length < rootDirectoryLength) - ExtractPkg(file); - else - ExtractPkg(file, true, file.Directory.FullName.Substring(rootDirectoryLength)); - } - - return; - } - - foreach (var directory in directoryInfo.EnumerateDirectories()) - { - foreach (var file in directory.EnumerateFiles("*.pkg")) - { - ExtractPkg(file, true, directory.FullName.Substring(rootDirectoryLength)); - } - } - } - - private static void ExtractFile(FileInfo fileInfo) - { - Directory.CreateDirectory(_options.OutputDirectory); - - if (fileInfo.Extension.Equals(".pkg", StringComparison.OrdinalIgnoreCase)) - ExtractPkg(fileInfo); - else if (fileInfo.Extension.Equals(".tex", StringComparison.OrdinalIgnoreCase)) - { - var tex = LoadTex(File.ReadAllBytes(fileInfo.FullName), fileInfo.FullName); - - if (tex == null) - return; - - try - { - var filePath = Path.Combine(_options.OutputDirectory, - Path.GetFileNameWithoutExtension(fileInfo.Name)); - - ConvertToImageAndSave(tex, filePath, _options.Overwrite); - var jsonInfo = _texJsonInfoGenerator.GenerateInfo(tex); - File.WriteAllText($"{filePath}.tex-json", jsonInfo); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - else - Console.WriteLine($"Unrecognized file extension: {fileInfo.Extension}"); - } - - private static void ExtractPkg(FileInfo file, bool appendFolderName = false, string defaultProjectName = "") - { - Console.WriteLine($"\r\n### Extracting package: {file.FullName}"); - - // Load package - Package package; - - using (var reader = new BinaryReader(file.Open(FileMode.Open, FileAccess.Read, FileShare.Read))) - { - package = _packageReader.ReadFrom(reader); - } - - // Get output directory - string outputDirectory; - var preview = string.Empty; - if (appendFolderName) - GetProjectFolderNameAndPreviewImage(file, defaultProjectName, out outputDirectory, out preview); - else - outputDirectory = _options.OutputDirectory; - - // Extract package entries - var entries = FilterEntries(package.Entries); - foreach (var entry in entries) - { - ExtractEntry(entry, ref outputDirectory); - } - - // Copy project files project.json/preview image - if (!_options.CopyProject || _options.SingleDir || file.Directory == null) - return; - - var files = file.Directory.GetFiles().Where(x => - x.Name.Equals(preview, StringComparison.OrdinalIgnoreCase) || - ProjectFiles.Contains(x.Name, StringComparer.OrdinalIgnoreCase)); - - CopyFiles(files, outputDirectory); - } - - private static void CopyFiles(IEnumerable files, string outputDirectory) - { - foreach (var file in files) - { - var outputPath = Path.Combine(outputDirectory, file.Name); - - if (!_options.Overwrite && File.Exists(outputPath)) - Console.WriteLine($"* Skipping, already exists: {outputPath}"); - else - { - File.Copy(file.FullName, outputPath, true); - Console.WriteLine($"* Copying: {file.FullName}"); - } - } - } - - private static IEnumerable FilterEntries(IEnumerable entries) - { - if (!string.IsNullOrEmpty(_options.IgnoreExts)) - { - return from entry in entries - where !_skipExtArray.Any(s => entry.FullPath.EndsWith(s, StringComparison.OrdinalIgnoreCase)) - select entry; - } - - if (!string.IsNullOrEmpty(_options.OnlyExts)) - { - return from entry in entries - where _onlyExtArray.Any(s => entry.FullPath.EndsWith(s, StringComparison.OrdinalIgnoreCase)) - select entry; - } - - return entries; - } - - [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] - private static void ExtractEntry(PackageEntry entry, ref string outputDirectory) - { - if (Program.Closing) - Environment.Exit(0); - - // save raw - var filePathWithoutExtension = _options.SingleDir - ? Path.Combine(outputDirectory, entry.Name) - : Path.Combine(outputDirectory, entry.DirectoryPath, entry.Name); - - var filePath = filePathWithoutExtension + entry.Extension; - - Directory.CreateDirectory(Path.GetDirectoryName(filePathWithoutExtension)); - - if (!_options.Overwrite && File.Exists(filePath)) - Console.WriteLine($"* Skipping, already exists: {filePath}"); - else - { - Console.WriteLine($"* Extracting: {entry.FullPath}"); - - File.WriteAllBytes(filePath, entry.Bytes); - } - - // convert and save - if (_options.NoTexConvert || entry.Type != EntryType.Tex) - return; - - var tex = LoadTex(entry.Bytes, entry.FullPath); - - if (tex == null) - return; - - try - { - ConvertToImageAndSave(tex, filePathWithoutExtension, _options.Overwrite); - var jsonInfo = _texJsonInfoGenerator.GenerateInfo(tex); - File.WriteAllText($"{filePathWithoutExtension}.tex-json", jsonInfo); - } - catch (Exception e) - { - Console.WriteLine("Failed to write texture"); - Console.WriteLine(e); - } - } - - private static void GetProjectInfo(FileInfo packageFile, ref string title, ref string preview) - { - var directory = packageFile.Directory; - if (directory == null) - return; - - var projectJson = directory.GetFiles("project.json"); - if (projectJson.Length == 0 || !projectJson[0].Exists) - return; - - dynamic json = JsonConvert.DeserializeObject(File.ReadAllText(projectJson[0].FullName)); - title = json.title; - preview = json.preview; - } - - private static void GetProjectFolderNameAndPreviewImage(FileInfo packageFile, string defaultProjectName, - out string outputDirectory, out string preview) - { - preview = string.Empty; - - if (_options.SingleDir) - { - outputDirectory = _options.OutputDirectory; - return; - } - - if (_options.UseName) - { - var name = defaultProjectName; - GetProjectInfo(packageFile, ref name, ref preview); - outputDirectory = Path.Combine(_options.OutputDirectory, name.GetSafeFilename()); - return; - } - - outputDirectory = Path.Combine(_options.OutputDirectory, defaultProjectName); - } - - private static ITex LoadTex(byte[] bytes, string name) - { - if (Program.Closing) - Environment.Exit(0); - - Console.WriteLine("* Reading: {0}", name); - - try - { - using (var reader = new BinaryReader(new MemoryStream(bytes), Encoding.UTF8)) - { - return _texReader.ReadFrom(reader); - } - } - catch (Exception e) - { - Console.WriteLine("Failed to read texture"); - Console.WriteLine(e); - } - - return null; - } - - private static void ConvertToImageAndSave(ITex tex, string path, bool overwrite) - { - var format = _texToImageConverter.GetConvertedFormat(tex); - var outputPath = $"{path}.{format.GetFileExtension()}"; - - if (!overwrite && File.Exists(outputPath)) - return; - - var resultImage = _texToImageConverter.ConvertToImage(tex); - - File.WriteAllBytes(outputPath, resultImage.Bytes); - } - } - - [Verb("extract", HelpText = "Extract PKG/Convert TEX into image.")] - public class ExtractOptions - { - [Option('o', "output", Required = false, HelpText = "Output directory", Default = "./output")] - public string OutputDirectory { get; set; } - - [Option('i', "ignoreexts", HelpText = - "Don't extract files with specified extensions (delimited by comma \",\")")] - public string IgnoreExts { get; set; } - - [Option('e', "onlyexts", HelpText = "Only extract files with specified extensions (delimited by comma \",\")")] - public string OnlyExts { get; set; } - - [Option('t', "tex", HelpText = "Convert all tex files into images from specified directory in input")] - public bool TexDirectory { get; set; } - - [Option('s', "singledir", HelpText = - "Should all extracted files be put in one directory instead of their entry path")] - public bool SingleDir { get; set; } - - [Option('r', "recursive", HelpText = "Recursive search in all subfolders of specified directory")] - public bool Recursive { get; set; } - - [Option('c', "copyproject", HelpText = - "Copy project.json and preview.jpg from beside PKG into output directory")] - public bool CopyProject { get; set; } - - [Option('n', "usename", HelpText = "Use name from project.json as project subfolder name instead of id")] - public bool UseName { get; set; } - - [Option("no-tex-convert", HelpText = "Don't convert TEX files into images while extracting PKG")] - public bool NoTexConvert { get; set; } - - [Option("overwrite", HelpText = "Overwrite all existing files")] - public bool Overwrite { get; set; } - - [Value(0, Required = true, HelpText = "Path to file/directory", MetaName = "Input")] - public string Input { get; set; } - } -} \ No newline at end of file diff --git a/RePKG/Command/Info.cs b/RePKG/Command/Info.cs deleted file mode 100644 index 2a105a2..0000000 --- a/RePKG/Command/Info.cs +++ /dev/null @@ -1,205 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using CommandLine; -using Newtonsoft.Json; -using RePKG.Application.Package; -using RePKG.Core.Package; -using RePKG.Core.Package.Interfaces; - -namespace RePKG.Command -{ - public class Info - { - private static InfoOptions _options; - private static string[] _projectInfoToPrint; - - private static readonly IPackageReader _reader; - - static Info() - { - _reader = new PackageReader(); - } - - public static void Action(InfoOptions options) - { - _options = options; - - if (string.IsNullOrEmpty(_options.ProjectInfo)) - _projectInfoToPrint = null; - else - _projectInfoToPrint = _options.ProjectInfo.Split(','); - - var fileInfo = new FileInfo(options.Input); - var directoryInfo = new DirectoryInfo(options.Input); - - if (!fileInfo.Exists) - { - if (directoryInfo.Exists) - { - if (_options.TexDirectory) - InfoTexDirectory(directoryInfo); - else - InfoPkgDirectory(directoryInfo); - - Console.WriteLine("Done"); - return; - } - - Console.WriteLine("Input file/directory doesn't exist!"); - Console.WriteLine(options.Input); - return; - } - - InfoFile(fileInfo); - Console.WriteLine("Done"); - } - - private static void InfoPkgDirectory(DirectoryInfo directoryInfo) - { - var rootDirectoryLength = directoryInfo.FullName.Length; - - foreach (var directory in directoryInfo.EnumerateDirectories()) - { - foreach (var file in directory.EnumerateFiles("*.pkg")) - { - InfoPkg(file, file.FullName.Substring(rootDirectoryLength)); - } - } - } - - private static void InfoTexDirectory(DirectoryInfo directoryInfo) - { - } - - private static void InfoFile(FileInfo file) - { - if (file.Extension.Equals(".pkg", StringComparison.OrdinalIgnoreCase)) - InfoPkg(file, Path.GetFullPath(file.Name)); - else if (file.Extension.Equals(".tex", StringComparison.OrdinalIgnoreCase)) - InfoTex(file); - else - Console.WriteLine($"Unrecognized file extension: {file.Extension}"); - } - - private static void InfoPkg(FileInfo file, string name) - { - var projectInfo = GetProjectInfo(file); - - if (!MatchesFilter(projectInfo)) - return; - - Console.WriteLine($"\r\n### Package info: {name}"); - - if (projectInfo != null && _projectInfoToPrint?.Length > 0) - { - IEnumerable projectInfoEnumerator; - - if (_projectInfoToPrint.Length == 1 && _projectInfoToPrint[0] == "*") - projectInfoEnumerator = Helper.GetPropertyKeysForDynamic(projectInfo); - else - { - projectInfoEnumerator = Helper.GetPropertyKeysForDynamic(projectInfo); - projectInfoEnumerator = projectInfoEnumerator.Where(x => - _projectInfoToPrint.Contains(x, StringComparer.OrdinalIgnoreCase)); - } - - foreach (var key in projectInfoEnumerator) - { - if (projectInfo[key] == null) - Console.WriteLine(key + @": null"); - else - Console.WriteLine(key + @": " + projectInfo[key].ToString()); - } - } - - if (_options.PrintEntries) - { - Console.WriteLine("Package entries:"); - - Package package; - using (var reader = new BinaryReader(file.Open(FileMode.Open, FileAccess.Read, FileShare.Read))) - { - package = _reader.ReadFrom(reader); - } - - var entries = package.Entries; - - if (_options.Sort) - { - if (_options.SortBy == "extension") - entries.Sort((a, b) => - String.Compare(a.FullPath, b.FullPath, StringComparison.OrdinalIgnoreCase)); - else if (_options.SortBy == "size") - entries.Sort((a, b) => a.Length.CompareTo(b.Length)); - else - entries.Sort((a, b) => - String.Compare(a.FullPath, b.FullPath, StringComparison.OrdinalIgnoreCase)); - } - - foreach (var entry in entries) - { - Console.WriteLine(@"* " + entry.FullPath + $@" - {entry.Length} bytes"); - } - } - } - - private static void InfoTex(FileInfo file) - { - } - - private static dynamic GetProjectInfo(FileInfo packageFile) - { - var directory = packageFile.Directory; - if (directory == null) - return null; - - var projectJson = directory.GetFiles("project.json"); - if (projectJson.Length == 0 || !projectJson[0].Exists) - return null; - - return JsonConvert.DeserializeObject(File.ReadAllText(projectJson[0].FullName)); - } - - private static bool MatchesFilter(dynamic project) - { - if (project == null) - return true; - - if (!string.IsNullOrEmpty(_options.TitleFilter)) - { - var title = (string) project.title; - if (!title.Contains(_options.TitleFilter, StringComparison.OrdinalIgnoreCase)) - return false; - } - - return true; - } - } - - [Verb("info", HelpText = "Dumps PKG/TEX info.")] - public class InfoOptions - { - [Value(0, Required = true, HelpText = "Path to file which you want to get info about", MetaName = "Input file")] - public string Input { get; set; } - - [Option('s', "sort", HelpText = "Sort entries a-z", Default = false)] - public bool Sort { get; set; } - - [Option('b', "sortby", HelpText = "Sort by ... (available options: name, extension, size)", Default = "name")] - public string SortBy { get; set; } - - [Option('t', "tex", HelpText = "Dump info about all tex files from specified directory")] - public bool TexDirectory { get; set; } - - [Option('p', "projectinfo", HelpText = "Keys to dump from project.json (delimit using comma) (* for all)")] - public string ProjectInfo { get; set; } - - [Option('e', "printentries", HelpText = "Print entries in packages")] - public bool PrintEntries { get; set; } - - [Option("title-filter", HelpText = "Title filter")] - public string TitleFilter { get; set; } - } -} \ No newline at end of file diff --git a/RePKG/Commands/Extract.cs b/RePKG/Commands/Extract.cs new file mode 100644 index 0000000..4451854 --- /dev/null +++ b/RePKG/Commands/Extract.cs @@ -0,0 +1,378 @@ +using RePKG.Core.Package; +using RePKG.Core.Package.Enums; +using RePKG.Core.Package.Interfaces; +using RePKG.Core.Texture; +using System.CommandLine; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Text.Json.Nodes; + +namespace RePKG.Commands +{ + public static class Extract + { + private static readonly string[] ProjectFiles = ["project.json"]; + private static readonly ITexReader _texReader = TexReader.Default; + private static readonly ITexJsonInfoGenerator _texJsonInfoGenerator = new TexJsonInfoGenerator(); + private static readonly IPackageReader _packageReader = new PackageReader(); + private static readonly TexToImageConverter _texToImageConverter = new(); + + public static void Action(ExtractOptions o) + { + if (string.IsNullOrEmpty(o.OutputDirectory)) + o.OutputDirectory = Directory.GetCurrentDirectory(); + + var fileInfo = new FileInfo(o.Input); + var directoryInfo = new DirectoryInfo(o.Input); + + if (!fileInfo.Exists) + { + if (directoryInfo.Exists) + { + if (o.TexDirectory) + ExtractTexDirectory(o, directoryInfo); + else + ExtractPkgDirectory(o, directoryInfo); + + Console.WriteLine("Done"); + return; + } + + Console.WriteLine("Input file not found"); + Console.WriteLine(o.Input); + return; + } + + ExtractFile(o, fileInfo); + Console.WriteLine("Done"); + } + + private static void ExtractTexDirectory(ExtractOptions o, DirectoryInfo directoryInfo) + { + var flags = o.Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; + + Directory.CreateDirectory(o.OutputDirectory); + + foreach (var fileInfo in directoryInfo.EnumerateFiles("*.tex", flags)) + { + if (!fileInfo.Extension.Equals(".tex", StringComparison.OrdinalIgnoreCase)) + continue; + + try + { + var tex = LoadTex(o, File.ReadAllBytes(fileInfo.FullName), fileInfo.FullName); + if (tex == null) + continue; + + var filePath = Path.Combine(o.OutputDirectory, Path.GetFileNameWithoutExtension(fileInfo.Name)); + ConvertToImageAndSave(o, tex, filePath); + File.WriteAllText($"{filePath}.tex-json", _texJsonInfoGenerator.GenerateInfo(tex)); + } + catch (Exception e) + { + Console.WriteLine("Failed to write texture"); + Console.WriteLine(e); + } + } + } + + private static void ExtractPkgDirectory(ExtractOptions o, DirectoryInfo directoryInfo) + { + var rootLen = directoryInfo.FullName.Length + 1; + + if (o.Recursive) + { + foreach (var file in directoryInfo.EnumerateFiles("*.pkg", SearchOption.AllDirectories)) + { + if (file.Directory == null || file.Directory.FullName.Length < rootLen) + ExtractPkg(o, file); + else + ExtractPkg(o, file, true, file.Directory.FullName[rootLen..]); + } + return; + } + + foreach (var directory in directoryInfo.EnumerateDirectories()) + { + foreach (var file in directory.EnumerateFiles("*.pkg")) + ExtractPkg(o, file, true, directory.FullName[rootLen..]); + } + } + + private static void ExtractFile(ExtractOptions o, FileInfo fileInfo) + { + Directory.CreateDirectory(o.OutputDirectory); + + if (fileInfo.Extension.Equals(".pkg", StringComparison.OrdinalIgnoreCase)) + { + ExtractPkg(o, fileInfo); + } + else if (fileInfo.Extension.Equals(".tex", StringComparison.OrdinalIgnoreCase)) + { + var tex = LoadTex(o, File.ReadAllBytes(fileInfo.FullName), fileInfo.FullName); + if (tex == null) + return; + + try + { + var filePath = Path.Combine(o.OutputDirectory, Path.GetFileNameWithoutExtension(fileInfo.Name)); + ConvertToImageAndSave(o, tex, filePath); + File.WriteAllText($"{filePath}.tex-json", _texJsonInfoGenerator.GenerateInfo(tex)); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + else + { + Console.WriteLine($"Unrecognized file extension: {fileInfo.Extension}"); + } + } + + private static void ExtractPkg(ExtractOptions o, FileInfo file, bool appendFolderName = false, string defaultProjectName = "") + { + Console.WriteLine($"\r\n### Extracting package: {file.FullName}"); + + Core.Package.Package package; + using (var reader = new BinaryReader(file.Open(FileMode.Open, FileAccess.Read, FileShare.Read))) + package = _packageReader.ReadFrom(reader); + + string outputDirectory; + var preview = string.Empty; + if (appendFolderName) + GetProjectFolderNameAndPreviewImage(o, file, defaultProjectName, out outputDirectory, out preview); + else + outputDirectory = o.OutputDirectory; + + foreach (var entry in FilterEntries(o, package.Entries)) + ExtractEntry(o, entry, ref outputDirectory); + + if (!o.CopyProject || o.SingleDir || file.Directory == null) + return; + + var files = file.Directory.GetFiles().Where(x => + x.Name.Equals(preview, StringComparison.OrdinalIgnoreCase) || + ProjectFiles.Contains(x.Name, StringComparer.OrdinalIgnoreCase)); + + CopyFiles(o, files, outputDirectory); + } + + private static void CopyFiles(ExtractOptions o, IEnumerable files, string outputDirectory) + { + foreach (var file in files) + { + var outputPath = Path.Combine(outputDirectory, file.Name); + if (!o.Overwrite && File.Exists(outputPath)) + Console.WriteLine($"* Skipping, already exists: {outputPath}"); + else + { + File.Copy(file.FullName, outputPath, true); + Console.WriteLine($"* Copying: {file.FullName}"); + } + } + } + + private static IEnumerable FilterEntries(ExtractOptions o, IEnumerable entries) + { + if (!string.IsNullOrEmpty(o.IgnoreExts)) + { + var skip = NormalizeExtensions(o.IgnoreExts.Split(',')); + return entries.Where(e => !skip.Any(s => e.FullPath.EndsWith(s, StringComparison.OrdinalIgnoreCase))); + } + + if (!string.IsNullOrEmpty(o.OnlyExts)) + { + var only = NormalizeExtensions(o.OnlyExts.Split(',')); + return entries.Where(e => only.Any(s => e.FullPath.EndsWith(s, StringComparison.OrdinalIgnoreCase))); + } + + return entries; + } + + [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] + private static void ExtractEntry(ExtractOptions o, PackageEntry entry, ref string outputDirectory) + { + if (Program.Closing) + Environment.Exit(0); + + var filePathNoExt = o.SingleDir + ? Path.Combine(outputDirectory, entry.Name) + : Path.Combine(outputDirectory, entry.DirectoryPath, entry.Name); + + var filePath = filePathNoExt + entry.Extension; + + Directory.CreateDirectory(Path.GetDirectoryName(filePathNoExt)); + + if (!o.Overwrite && File.Exists(filePath)) + Console.WriteLine($"* Skipping, already exists: {filePath}"); + else + { + Console.WriteLine($"* Extracting: {entry.FullPath}"); + File.WriteAllBytes(filePath, entry.Bytes); + } + + if (o.NoTexConvert || entry.Type != EntryType.Tex) + return; + + var tex = LoadTex(o, entry.Bytes, entry.FullPath); + if (tex == null) + return; + + try + { + ConvertToImageAndSave(o, tex, filePathNoExt); + File.WriteAllText($"{filePathNoExt}.tex-json", _texJsonInfoGenerator.GenerateInfo(tex)); + } + catch (Exception e) + { + Console.WriteLine("Failed to write texture"); + Console.WriteLine(e); + } + } + + private static void GetProjectFolderNameAndPreviewImage(ExtractOptions o, FileInfo packageFile, + string defaultProjectName, out string outputDirectory, out string preview) + { + preview = string.Empty; + + if (o.SingleDir) + { + outputDirectory = o.OutputDirectory; + return; + } + + if (o.UseName) + { + var name = defaultProjectName; + GetProjectInfo(packageFile, ref name, ref preview); + outputDirectory = Path.Combine(o.OutputDirectory, name.GetSafeFilename()); + return; + } + + outputDirectory = Path.Combine(o.OutputDirectory, defaultProjectName); + } + + private static void GetProjectInfo(FileInfo packageFile, ref string title, ref string preview) + { + var directory = packageFile.Directory; + if (directory == null) + return; + + var projectJson = directory.GetFiles("project.json"); + if (projectJson.Length == 0 || !projectJson[0].Exists) + return; + + var json = JsonNode.Parse(File.ReadAllText(projectJson[0].FullName)); + if (json is JsonObject obj) + { + title = obj["title"]?.GetValue() ?? title; + preview = obj["preview"]?.GetValue() ?? preview; + } + } + + private static ITex? LoadTex(ExtractOptions o, byte[] bytes, string name) + { + if (Program.Closing) + Environment.Exit(0); + + Console.WriteLine("* Reading: {0}", name); + + try + { + using var reader = new BinaryReader(new MemoryStream(bytes), Encoding.UTF8); + return _texReader.ReadFrom(reader); + } + catch (Exception e) + { + Console.WriteLine("Failed to read texture"); + Console.WriteLine(e); + } + + return null; + } + + private static void ConvertToImageAndSave(ExtractOptions o, ITex tex, string path) + { + var format = _texToImageConverter.GetConvertedFormat(tex); + var outputPath = $"{path}.{format.GetFileExtension()}"; + + if (!o.Overwrite && File.Exists(outputPath)) + return; + + File.WriteAllBytes(outputPath, _texToImageConverter.ConvertToImage(tex).Bytes); + } + + private static string[] NormalizeExtensions(string[] array) + { + ArgumentNullException.ThrowIfNull(array); + + for (int i = 0; i < array.Length; i++) + if (!array[i].StartsWith(".")) + array[i] = '.' + array[i]; + return array; + } + + public static Command BuildCommand() + { + var inputArg = new Argument("input") { Description = "Path to file/directory" }; + var outputOpt = new Option("--output", ["-o"]) { Description = "Output directory", DefaultValueFactory = _ => "./output" }; + var ignoreOpt = new Option("--ignoreexts", ["-i"]) { Description = "Don't extract files with these extensions (comma-separated)" }; + var onlyOpt = new Option("--onlyexts", ["-e"]) { Description = "Only extract files with these extensions (comma-separated)" }; + var texOpt = new Option("--tex", ["-t"]) { Description = "Convert all tex files into images from specified directory" }; + var sdirOpt = new Option("--singledir", ["-s"]) { Description = "Put all extracted files in one directory" }; + var recurOpt = new Option("--recursive", ["-r"]) { Description = "Recursive search in all subfolders" }; + var copyOpt = new Option("--copyproject", ["-c"]) { Description = "Copy project.json and preview.jpg into output directory" }; + var nameOpt = new Option("--usename", ["-n"]) { Description = "Use project.json title as subfolder name instead of id" }; + var noTexOpt = new Option("--no-tex-convert") { Description = "Don't convert TEX files into images while extracting PKG" }; + var overOpt = new Option("--overwrite") { Description = "Overwrite all existing files" }; + + var cmd = new Command("extract", "Extract PKG / Convert TEX into image") + { + inputArg, + outputOpt, + ignoreOpt, + onlyOpt, + texOpt, + sdirOpt, + recurOpt, + copyOpt, + nameOpt, + noTexOpt, + overOpt + }; + + cmd.SetAction(pr => Action(new ExtractOptions + { + Input = pr.GetValue(inputArg)!, + OutputDirectory = pr.GetValue(outputOpt) ?? "./output", + IgnoreExts = pr.GetValue(ignoreOpt), + OnlyExts = pr.GetValue(onlyOpt), + TexDirectory = pr.GetValue(texOpt), + SingleDir = pr.GetValue(sdirOpt), + Recursive = pr.GetValue(recurOpt), + CopyProject = pr.GetValue(copyOpt), + UseName = pr.GetValue(nameOpt), + NoTexConvert = pr.GetValue(noTexOpt), + Overwrite = pr.GetValue(overOpt), + })); + + return cmd; + } + } + + public class ExtractOptions + { + public string Input { get; set; } = string.Empty; + public string OutputDirectory { get; set; } = string.Empty; + public string? IgnoreExts { get; set; } + public string? OnlyExts { get; set; } + public bool TexDirectory { get; set; } + public bool SingleDir { get; set; } + public bool Recursive { get; set; } + public bool CopyProject { get; set; } + public bool UseName { get; set; } + public bool NoTexConvert { get; set; } + public bool Overwrite { get; set; } + } +} \ No newline at end of file diff --git a/RePKG/Commands/Info.cs b/RePKG/Commands/Info.cs new file mode 100644 index 0000000..d0ffeed --- /dev/null +++ b/RePKG/Commands/Info.cs @@ -0,0 +1,177 @@ +using RePKG.Core.Package; +using RePKG.Core.Package.Interfaces; +using System.CommandLine; +using System.Text.Json.Nodes; + +namespace RePKG.Commands +{ + public static class Info + { + private static readonly IPackageReader _reader = new PackageReader(); + + public static void Action(InfoOptions o) + { + var projectInfoToPrint = string.IsNullOrEmpty(o.ProjectInfo) ? null : o.ProjectInfo.Split(','); + + var fileInfo = new FileInfo(o.Input); + var directoryInfo = new DirectoryInfo(o.Input); + + if (!fileInfo.Exists) + { + if (directoryInfo.Exists) + { + if (o.TexDirectory) + InfoTexDirectory(o, directoryInfo); + else + InfoPkgDirectory(o, directoryInfo, projectInfoToPrint); + + Console.WriteLine("Done"); + return; + } + + Console.WriteLine("Input file/directory doesn''t exist!"); + Console.WriteLine(o.Input); + return; + } + + InfoFile(o, fileInfo, projectInfoToPrint); + Console.WriteLine("Done"); + } + + private static void InfoPkgDirectory(InfoOptions o, DirectoryInfo directoryInfo, string[]? projectInfoToPrint) + { + var rootLen = directoryInfo.FullName.Length; + + foreach (var directory in directoryInfo.EnumerateDirectories()) + { + foreach (var file in directory.EnumerateFiles("*.pkg")) + InfoPkg(o, file, file.FullName.Substring(rootLen), projectInfoToPrint); + } + } + + private static void InfoTexDirectory(InfoOptions o, DirectoryInfo directoryInfo) + { + } + + private static void InfoFile(InfoOptions o, FileInfo file, string[]? projectInfoToPrint) + { + if (file.Extension.Equals(".pkg", StringComparison.OrdinalIgnoreCase)) + InfoPkg(o, file, Path.GetFullPath(file.Name), projectInfoToPrint); + else if (file.Extension.Equals(".tex", StringComparison.OrdinalIgnoreCase)) + InfoTex(o, file); + else + Console.WriteLine($"Unrecognized file extension: {file.Extension}"); + } + + private static void InfoPkg(InfoOptions o, FileInfo file, string name, string[]? projectInfoToPrint) + { + var projectInfo = GetProjectInfo(file); + + if (!MatchesFilter(o, projectInfo)) + return; + + Console.WriteLine($"\r\n### Package info: {name}"); + + if (projectInfo is JsonObject projectJson && projectInfoToPrint?.Length > 0) + { + var keys = Helper.GetPropertyKeysForDynamic(projectJson); + + if (!(projectInfoToPrint.Length == 1 && projectInfoToPrint[0] == "*")) + keys = keys.Where(x => projectInfoToPrint.Contains(x, StringComparer.OrdinalIgnoreCase)); + + foreach (var key in keys) + { + var value = projectJson[key]; + Console.WriteLine(value == null ? $"{key}: null" : $"{key}: {value}"); + } + } + + if (!o.PrintEntries) + return; + + Console.WriteLine("Package entries:"); + + Core.Package.Package package; + using (var reader = new BinaryReader(file.Open(FileMode.Open, FileAccess.Read, FileShare.Read))) + package = _reader.ReadFrom(reader); + + var entries = package.Entries; + + if (o.Sort) + { + entries.Sort(o.SortBy == "size" + ? (a, b) => a.Length.CompareTo(b.Length) + : (a, b) => String.Compare(a.FullPath, b.FullPath, StringComparison.OrdinalIgnoreCase)); + } + + foreach (var entry in entries) + Console.WriteLine($"* {entry.FullPath} - {entry.Length} bytes"); + } + + private static void InfoTex(InfoOptions o, FileInfo file) + { + } + + private static JsonNode? GetProjectInfo(FileInfo packageFile) + { + var directory = packageFile.Directory; + if (directory == null) + return null; + + var projectJson = directory.GetFiles("project.json"); + if (projectJson.Length == 0 || !projectJson[0].Exists) + return null; + + return JsonNode.Parse(File.ReadAllText(projectJson[0].FullName)); + } + + private static bool MatchesFilter(InfoOptions o, JsonNode? project) + { + if (project == null || string.IsNullOrEmpty(o.TitleFilter)) + return true; + + var title = project["title"]?.GetValue(); + return title == null || title.Contains(o.TitleFilter, StringComparison.OrdinalIgnoreCase); + } + + public static Command BuildCommand() + { + var inputArg = new Argument("input") { Description = "Path to file/directory" }; + var sortOpt = new Option("--sort", ["-s"]) { Description = "Sort entries a-z" }; + var sortByOpt = new Option("--sortby", ["-b"]) { Description = "Sort by name/extension/size", DefaultValueFactory = _ => "name" }; + var texOpt = new Option("--tex", ["-t"]) { Description = "Dump info about all tex files from specified directory" }; + var projOpt = new Option("--projectinfo", ["-p"]) { Description = "Keys from project.json (comma-separated, * for all)" }; + var printOpt = new Option("--printentries", ["-e"]) { Description = "Print entries in packages" }; + var filterOpt = new Option("--title-filter") { Description = "Title filter" }; + + var cmd = new Command("info", "Dumps PKG/TEX info"); + cmd.Add(inputArg); + cmd.Add(sortOpt); cmd.Add(sortByOpt); cmd.Add(texOpt); + cmd.Add(projOpt); cmd.Add(printOpt); cmd.Add(filterOpt); + + cmd.SetAction((ParseResult pr) => Action(new InfoOptions + { + Input = pr.GetValue(inputArg)!, + Sort = pr.GetValue(sortOpt), + SortBy = pr.GetValue(sortByOpt)!, + TexDirectory = pr.GetValue(texOpt), + ProjectInfo = pr.GetValue(projOpt), + PrintEntries = pr.GetValue(printOpt), + TitleFilter = pr.GetValue(filterOpt), + })); + + return cmd; + } + } + + public class InfoOptions + { + public string Input { get; set; } = string.Empty; + public bool Sort { get; set; } + public string SortBy { get; set; } = "name"; + public bool TexDirectory { get; set; } + public string? ProjectInfo { get; set; } + public bool PrintEntries { get; set; } + public string? TitleFilter { get; set; } + } +} \ No newline at end of file diff --git a/RePKG/Extensions.cs b/RePKG/Extensions.cs index 3d8f3ea..77995b8 100644 --- a/RePKG/Extensions.cs +++ b/RePKG/Extensions.cs @@ -1,7 +1,4 @@ -using System; -using System.IO; - -namespace RePKG +namespace RePKG { public static class Extensions { @@ -12,33 +9,7 @@ public static bool Contains(this string haystack, string needle, StringCompariso public static string GetSafeFilename(this string filename) { - return string.Join("_", filename.Split(Path.GetInvalidFileNameChars())); - - } - - public static string[] SplitArguments(this string commandLine) - { - var parmChars = commandLine.ToCharArray(); - var inSingleQuote = false; - var inDoubleQuote = false; - - for (var index = 0; index < parmChars.Length; index++) - { - if (parmChars[index] == '"' && !inSingleQuote) - { - inDoubleQuote = !inDoubleQuote; - parmChars[index] = '\n'; - } - if (parmChars[index] == '\'' && !inDoubleQuote) - { - inSingleQuote = !inSingleQuote; - parmChars[index] = '\n'; - } - if (!inSingleQuote && !inDoubleQuote && parmChars[index] == ' ') - parmChars[index] = '\n'; - } - return new string(parmChars).Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); } } -} +} \ No newline at end of file diff --git a/RePKG/Helper.cs b/RePKG/Helper.cs index 316beaf..adf1f37 100644 --- a/RePKG/Helper.cs +++ b/RePKG/Helper.cs @@ -1,21 +1,12 @@ -using System.Collections.Generic; -using Newtonsoft.Json.Linq; +using System.Text.Json.Nodes; namespace RePKG { public static class Helper { - public static IEnumerable GetPropertyKeysForDynamic(dynamic dynamicToGetPropertiesFor) + public static IEnumerable GetPropertyKeysForDynamic(JsonObject jsonObject) { - JObject attributesAsJObject = dynamicToGetPropertiesFor; - var values = attributesAsJObject.ToObject>(); - var toReturn = new List(); - foreach (var key in values.Keys) - { - toReturn.Add(key); - } - - return toReturn; + return jsonObject.Select(x => x.Key); } } } \ No newline at end of file diff --git a/RePKG/Program.cs b/RePKG/Program.cs index bf7a1a3..b34ac80 100644 --- a/RePKG/Program.cs +++ b/RePKG/Program.cs @@ -1,6 +1,6 @@ -using System; -using CommandLine; -using RePKG.Command; +using RePKG.Commands; +using System.CommandLine; +using System.CommandLine.Help; namespace RePKG { @@ -8,43 +8,24 @@ internal class Program { public static bool Closing; - private static void Main(string[] args) + private static int Main(string[] args) { Console.CancelKeyPress += Cancel; - if (args.Length > 0 && args[0] == "interactive") + var root = new RootCommand("RePKG - Wallpaper Engine package tool") { - InteractiveConsole(); - return; - } - - Parser.Default.ParseArguments(args) - .WithParsed(Extract.Action) - .WithParsed(Info.Action); + Extract.BuildCommand(), + Info.BuildCommand() + }; + root.Action = new HelpAction(); + return root.Parse(args).Invoke(); } - private static void Cancel(object sender, ConsoleCancelEventArgs e) + private static void Cancel(object? sender, ConsoleCancelEventArgs e) { Closing = true; e.Cancel = true; Console.WriteLine("Terminating..."); } - - private static void InteractiveConsole() - { - Console.WriteLine("RePKG started in interactive mode. You can now type commands"); - Console.WriteLine("Type \"help\" for commands"); - - string line; - - while (!string.IsNullOrEmpty(line = Console.ReadLine())) - { - var interactiveArgs = line.SplitArguments(); - - Parser.Default.ParseArguments(interactiveArgs) - .WithParsed(Extract.Action) - .WithParsed(Info.Action); - } - } } -} +} \ No newline at end of file diff --git a/RePKG/RePKG.csproj b/RePKG/RePKG.csproj index 892f7ac..390eeaf 100644 --- a/RePKG/RePKG.csproj +++ b/RePKG/RePKG.csproj @@ -2,25 +2,22 @@ RePKG Exe - net472 - 0.4.0 + net10.0 + 0.5.0 Copyright © NotScuffed 2025 + enable + enable - - - - - - + - \ No newline at end of file +