├── .gitignore ├── EffectConverter.sln ├── EffectConverter ├── EffectConverter.csproj └── Program.cs ├── EffectLibrary ├── EffectLibrary.csproj ├── Enums.cs ├── FileData │ ├── EFFN │ │ └── NamcoEffectFile.cs │ ├── EFT1 │ │ └── PtclFile.cs │ ├── EFT2 │ │ ├── EmitterList.cs │ │ ├── EmitterStructs │ │ │ ├── Emitter.cs │ │ │ └── EmitterAnimation.cs │ │ ├── Primitives.cs │ │ ├── PtclFile.cs │ │ ├── Shaders.cs │ │ ├── Structs.cs │ │ └── Textures.cs │ └── EFTB │ │ └── TextureArrayGX2.cs ├── LIb │ ├── BfresLibrary.dll │ ├── ShaderLibrary.deps.json │ ├── ShaderLibrary.dll │ ├── Syroot.BinaryData.dll │ ├── Syroot.Maths.dll │ ├── Syroot.NintenTools.NSW.Bntx.deps.json │ ├── Syroot.NintenTools.NSW.Bntx.dll │ └── Syroot.NintenTools.NSW.Bntx.xml ├── Shared │ ├── Enums.cs │ ├── PtclSerialize.cs │ ├── Structs.cs │ ├── Utils.cs │ └── VersionCheck.cs └── Tools │ ├── PtclFileCreator.cs │ └── PtclFileDumper.cs ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # Tye 66 | .tye/ 67 | 68 | # ASP.NET Scaffolding 69 | ScaffoldingReadMe.txt 70 | 71 | # StyleCop 72 | StyleCopReport.xml 73 | 74 | # Files built by Visual Studio 75 | *_i.c 76 | *_p.c 77 | *_h.h 78 | *.ilk 79 | *.meta 80 | *.obj 81 | *.iobj 82 | *.pch 83 | *.pdb 84 | *.ipdb 85 | *.pgc 86 | *.pgd 87 | *.rsp 88 | *.sbr 89 | *.tlb 90 | *.tli 91 | *.tlh 92 | *.tmp 93 | *.tmp_proj 94 | *_wpftmp.csproj 95 | *.log 96 | *.vspscc 97 | *.vssscc 98 | .builds 99 | *.pidb 100 | *.svclog 101 | *.scc 102 | 103 | # Chutzpah Test files 104 | _Chutzpah* 105 | 106 | # Visual C++ cache files 107 | ipch/ 108 | *.aps 109 | *.ncb 110 | *.opendb 111 | *.opensdf 112 | *.sdf 113 | *.cachefile 114 | *.VC.db 115 | *.VC.VC.opendb 116 | 117 | # Visual Studio profiler 118 | *.psess 119 | *.vsp 120 | *.vspx 121 | *.sap 122 | 123 | # Visual Studio Trace Files 124 | *.e2e 125 | 126 | # TFS 2012 Local Workspace 127 | $tf/ 128 | 129 | # Guidance Automation Toolkit 130 | *.gpState 131 | 132 | # ReSharper is a .NET coding add-in 133 | _ReSharper*/ 134 | *.[Rr]e[Ss]harper 135 | *.DotSettings.user 136 | 137 | # TeamCity is a build add-in 138 | _TeamCity* 139 | 140 | # DotCover is a Code Coverage Tool 141 | *.dotCover 142 | 143 | # AxoCover is a Code Coverage Tool 144 | .axoCover/* 145 | !.axoCover/settings.json 146 | 147 | # Coverlet is a free, cross platform Code Coverage Tool 148 | coverage*.json 149 | coverage*.xml 150 | coverage*.info 151 | 152 | # Visual Studio code coverage results 153 | *.coverage 154 | *.coveragexml 155 | 156 | # NCrunch 157 | _NCrunch_* 158 | .*crunch*.local.xml 159 | nCrunchTemp_* 160 | 161 | # MightyMoose 162 | *.mm.* 163 | AutoTest.Net/ 164 | 165 | # Web workbench (sass) 166 | .sass-cache/ 167 | 168 | # Installshield output folder 169 | [Ee]xpress/ 170 | 171 | # DocProject is a documentation generator add-in 172 | DocProject/buildhelp/ 173 | DocProject/Help/*.HxT 174 | DocProject/Help/*.HxC 175 | DocProject/Help/*.hhc 176 | DocProject/Help/*.hhk 177 | DocProject/Help/*.hhp 178 | DocProject/Help/Html2 179 | DocProject/Help/html 180 | 181 | # Click-Once directory 182 | publish/ 183 | 184 | # Publish Web Output 185 | *.[Pp]ublish.xml 186 | *.azurePubxml 187 | # Note: Comment the next line if you want to checkin your web deploy settings, 188 | # but database connection strings (with potential passwords) will be unencrypted 189 | *.pubxml 190 | *.publishproj 191 | 192 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 193 | # checkin your Azure Web App publish settings, but sensitive information contained 194 | # in these scripts will be unencrypted 195 | PublishScripts/ 196 | 197 | # NuGet Packages 198 | *.nupkg 199 | # NuGet Symbol Packages 200 | *.snupkg 201 | # The packages folder can be ignored because of Package Restore 202 | **/[Pp]ackages/* 203 | # except build/, which is used as an MSBuild target. 204 | !**/[Pp]ackages/build/ 205 | # Uncomment if necessary however generally it will be regenerated when needed 206 | #!**/[Pp]ackages/repositories.config 207 | # NuGet v3's project.json files produces more ignorable files 208 | *.nuget.props 209 | *.nuget.targets 210 | 211 | # Microsoft Azure Build Output 212 | csx/ 213 | *.build.csdef 214 | 215 | # Microsoft Azure Emulator 216 | ecf/ 217 | rcf/ 218 | 219 | # Windows Store app package directories and files 220 | AppPackages/ 221 | BundleArtifacts/ 222 | Package.StoreAssociation.xml 223 | _pkginfo.txt 224 | *.appx 225 | *.appxbundle 226 | *.appxupload 227 | 228 | # Visual Studio cache files 229 | # files ending in .cache can be ignored 230 | *.[Cc]ache 231 | # but keep track of directories ending in .cache 232 | !?*.[Cc]ache/ 233 | 234 | # Others 235 | ClientBin/ 236 | ~$* 237 | *~ 238 | *.dbmdl 239 | *.dbproj.schemaview 240 | *.jfm 241 | *.pfx 242 | *.publishsettings 243 | orleans.codegen.cs 244 | 245 | # Including strong name files can present a security risk 246 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 247 | #*.snk 248 | 249 | # Since there are multiple workflows, uncomment next line to ignore bower_components 250 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 251 | #bower_components/ 252 | 253 | # RIA/Silverlight projects 254 | Generated_Code/ 255 | 256 | # Backup & report files from converting an old project file 257 | # to a newer Visual Studio version. Backup files are not needed, 258 | # because we have git ;-) 259 | _UpgradeReport_Files/ 260 | Backup*/ 261 | UpgradeLog*.XML 262 | UpgradeLog*.htm 263 | ServiceFabricBackup/ 264 | *.rptproj.bak 265 | 266 | # SQL Server files 267 | *.mdf 268 | *.ldf 269 | *.ndf 270 | 271 | # Business Intelligence projects 272 | *.rdl.data 273 | *.bim.layout 274 | *.bim_*.settings 275 | *.rptproj.rsuser 276 | *- [Bb]ackup.rdl 277 | *- [Bb]ackup ([0-9]).rdl 278 | *- [Bb]ackup ([0-9][0-9]).rdl 279 | 280 | # Microsoft Fakes 281 | FakesAssemblies/ 282 | 283 | # GhostDoc plugin setting file 284 | *.GhostDoc.xml 285 | 286 | # Node.js Tools for Visual Studio 287 | .ntvs_analysis.dat 288 | node_modules/ 289 | 290 | # Visual Studio 6 build log 291 | *.plg 292 | 293 | # Visual Studio 6 workspace options file 294 | *.opt 295 | 296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 297 | *.vbw 298 | 299 | # Visual Studio LightSwitch build output 300 | **/*.HTMLClient/GeneratedArtifacts 301 | **/*.DesktopClient/GeneratedArtifacts 302 | **/*.DesktopClient/ModelManifest.xml 303 | **/*.Server/GeneratedArtifacts 304 | **/*.Server/ModelManifest.xml 305 | _Pvt_Extensions 306 | 307 | # Paket dependency manager 308 | .paket/paket.exe 309 | paket-files/ 310 | 311 | # FAKE - F# Make 312 | .fake/ 313 | 314 | # CodeRush personal settings 315 | .cr/personal 316 | 317 | # Python Tools for Visual Studio (PTVS) 318 | __pycache__/ 319 | *.pyc 320 | 321 | # Cake - Uncomment if you are using it 322 | # tools/** 323 | # !tools/packages.config 324 | 325 | # Tabs Studio 326 | *.tss 327 | 328 | # Telerik's JustMock configuration file 329 | *.jmconfig 330 | 331 | # BizTalk build output 332 | *.btp.cs 333 | *.btm.cs 334 | *.odx.cs 335 | *.xsd.cs 336 | 337 | # OpenCover UI analysis results 338 | OpenCover/ 339 | 340 | # Azure Stream Analytics local run output 341 | ASALocalRun/ 342 | 343 | # MSBuild Binary and Structured Log 344 | *.binlog 345 | 346 | # NVidia Nsight GPU debugger configuration file 347 | *.nvuser 348 | 349 | # MFractors (Xamarin productivity tool) working folder 350 | .mfractor/ 351 | 352 | # Local History for Visual Studio 353 | .localhistory/ 354 | 355 | # BeatPulse healthcheck temp database 356 | healthchecksdb 357 | 358 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 359 | MigrationBackup/ 360 | 361 | # Ionide (cross platform F# VS Code tools) working folder 362 | .ionide/ 363 | 364 | # Fody - auto-generated XML schema 365 | FodyWeavers.xsd 366 | 367 | ## 368 | ## Visual studio for Mac 369 | ## 370 | 371 | 372 | # globs 373 | Makefile.in 374 | *.userprefs 375 | *.usertasks 376 | config.make 377 | config.status 378 | aclocal.m4 379 | install-sh 380 | autom4te.cache/ 381 | *.tar.gz 382 | tarballs/ 383 | test-results/ 384 | 385 | # Mac bundle stuff 386 | *.dmg 387 | *.app 388 | 389 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 390 | # General 391 | .DS_Store 392 | .AppleDouble 393 | .LSOverride 394 | 395 | # Icon must end with two \r 396 | Icon 397 | 398 | 399 | # Thumbnails 400 | ._* 401 | 402 | # Files that might appear in the root of a volume 403 | .DocumentRevisions-V100 404 | .fseventsd 405 | .Spotlight-V100 406 | .TemporaryItems 407 | .Trashes 408 | .VolumeIcon.icns 409 | .com.apple.timemachine.donotpresent 410 | 411 | # Directories potentially created on remote AFP share 412 | .AppleDB 413 | .AppleDesktop 414 | Network Trash Folder 415 | Temporary Items 416 | .apdisk 417 | 418 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 419 | # Windows thumbnail cache files 420 | Thumbs.db 421 | ehthumbs.db 422 | ehthumbs_vista.db 423 | 424 | # Dump file 425 | *.stackdump 426 | 427 | # Folder config file 428 | [Dd]esktop.ini 429 | 430 | # Recycle Bin used on file shares 431 | $RECYCLE.BIN/ 432 | 433 | # Windows Installer files 434 | *.cab 435 | *.msi 436 | *.msix 437 | *.msm 438 | *.msp 439 | 440 | # Windows shortcuts 441 | *.lnk 442 | 443 | # JetBrains Rider 444 | .idea/ 445 | *.sln.iml 446 | 447 | ## 448 | ## Visual Studio Code 449 | ## 450 | .vscode/* 451 | !.vscode/settings.json 452 | !.vscode/tasks.json 453 | !.vscode/launch.json 454 | !.vscode/extensions.json 455 | -------------------------------------------------------------------------------- /EffectConverter.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.8.34219.65 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EffectConverter", "EffectConverter\EffectConverter.csproj", "{1D03C4BC-A334-4748-9F4A-48E5B9042AB1}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EffectLibrary", "EffectLibrary\EffectLibrary.csproj", "{8FEA9A68-01F0-4E36-929E-E6261FD636AC}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {1D03C4BC-A334-4748-9F4A-48E5B9042AB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {1D03C4BC-A334-4748-9F4A-48E5B9042AB1}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {1D03C4BC-A334-4748-9F4A-48E5B9042AB1}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {1D03C4BC-A334-4748-9F4A-48E5B9042AB1}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {8FEA9A68-01F0-4E36-929E-E6261FD636AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {8FEA9A68-01F0-4E36-929E-E6261FD636AC}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {8FEA9A68-01F0-4E36-929E-E6261FD636AC}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {8FEA9A68-01F0-4E36-929E-E6261FD636AC}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {D46ED3B8-5462-4EEE-B9BB-9A4F5C3B615C} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /EffectConverter/EffectConverter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /EffectConverter/Program.cs: -------------------------------------------------------------------------------- 1 | using EffectLibrary; 2 | using EffectLibrary.Tools; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace EffectConverter 7 | { 8 | class Program 9 | { 10 | public static void Main(string[] args) 11 | { 12 | if (args.Length == 0) 13 | { 14 | Console.WriteLine($"Tool by KillzXGaming"); 15 | Console.WriteLine($"To use, drag/drop a ptcl or eff file on exe to dump the contents"); 16 | Console.WriteLine($"Drag/drop the folder to create a new ptcl"); 17 | Console.WriteLine($"Dumped files can be swapped and edited"); 18 | Console.ReadLine(); 19 | return; 20 | } 21 | 22 | foreach (var arg in args) 23 | { 24 | //process particle file 25 | if (File.Exists(arg)) 26 | DumpParticleFile(arg); 27 | else if (Directory.Exists(arg)) 28 | CreateParticleFile(arg); 29 | } 30 | } 31 | 32 | static void DumpParticleFile(string path) 33 | { 34 | string name = Path.GetFileNameWithoutExtension(path); 35 | 36 | string magic = GetMagic(path); 37 | 38 | if (magic == "EFFN") //namco effect 39 | { 40 | NamcoEffectFile namcoEffect = new NamcoEffectFile(path); 41 | PtclFileDumper.DumpAll(namcoEffect.PtclFile, name); 42 | //Dump namco effect header info which includes emitter link data 43 | namcoEffect.Export(Path.Combine(name, $"NamcoFile.json")); 44 | } 45 | else if (magic == "VFXB") //ptcl file 46 | { 47 | PtclFile ptcl = new PtclFile(path); 48 | PtclFileDumper.DumpAll(ptcl, name); 49 | } 50 | else 51 | { 52 | throw new Exception($"Unknown file {path} given! Expected EFFN or VFXB magic, but got {magic}."); 53 | } 54 | } 55 | 56 | static string GetMagic(string path) 57 | { 58 | using (var reader = new BinaryReader(File.OpenRead(path))) 59 | { 60 | return Encoding.ASCII.GetString(reader.ReadBytes(4)); 61 | } 62 | } 63 | 64 | static void CreateParticleFile(string folder) 65 | { 66 | string name = Path.GetFileNameWithoutExtension(folder); 67 | 68 | //base ptcl to edit 69 | string ptcl_base = Path.Combine(folder, "Base.ptcl"); 70 | if (!File.Exists(ptcl_base)) 71 | return; 72 | 73 | PtclFile ptcl = new PtclFile(ptcl_base); 74 | var newPtcl = PtclFileCreator.FromFolder(ptcl, folder); 75 | 76 | string namco_header = Path.Combine(folder, "NamcoFile.json"); 77 | if (File.Exists(namco_header)) 78 | { 79 | NamcoEffectFile namcoEffect = new NamcoEffectFile(newPtcl); 80 | namcoEffect.Import(namco_header); 81 | 82 | namcoEffect.Save($"{name}_NEW.eff"); 83 | } 84 | else 85 | newPtcl.Save($"{name}_NEW.ptcl"); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /EffectLibrary/EffectLibrary.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | enable 6 | disable 7 | 8 | 9 | 10 | 11 | LIb\BfresLibrary.dll 12 | 13 | 14 | LIb\ShaderLibrary.dll 15 | 16 | 17 | LIb\Syroot.BinaryData.dll 18 | 19 | 20 | LIb\Syroot.Maths.dll 21 | 22 | 23 | LIb\Syroot.NintenTools.NSW.Bntx.dll 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /EffectLibrary/Enums.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EffectLibrary 8 | { 9 | [Flags] 10 | public enum EmitterFlags : int 11 | { 12 | ZSort = 0x0200, 13 | ReverseParticleOrder = 0x0400, 14 | GPUParticleCalculations = 0x800000, 15 | }; 16 | 17 | public enum RandomSeedMode 18 | { 19 | PerEmitter, 20 | PerEmitterSet, 21 | Custom, 22 | } 23 | 24 | public enum ParserType 25 | { 26 | Float, 27 | Float2, 28 | Float3, 29 | Float4, 30 | Uint, 31 | Int, 32 | Byte, 33 | Boolean, 34 | } 35 | 36 | public enum FileResourceFlags 37 | { 38 | HasTexture3 = 0x1, 39 | HasAlpha1 = 0x2, 40 | HasGPUBehavior = 0x4, 41 | UseShaderIndex = 0x8, 42 | HasNearFarAlpha = 0x10, 43 | VFXB = 0x20, 44 | } 45 | 46 | public enum CustomActionCallBackID : uint 47 | { 48 | Invalid = 0xFFFFFFFF, 49 | _0 = 0, 50 | _1, _2, _3, _4, _5, _6, _7, 51 | Max = 8, 52 | }; 53 | 54 | public enum CustomShaderCallBackID : uint 55 | { 56 | Max = 9, 57 | } 58 | 59 | public enum FragmentShaderMode 60 | { 61 | Normal, 62 | Refraction, 63 | Distortion, 64 | } 65 | 66 | public enum FragmentColorSrc 67 | { 68 | RGB, 69 | A, 70 | } 71 | 72 | public enum AnimGroupType 73 | { 74 | EmissionRatio = 0, 75 | Lifespan = 1, 76 | ScaleX = 2, 77 | ScaleY = 3, 78 | ScaleZ = 4, 79 | RotationX = 5, 80 | RotationY = 6, 81 | RotationZ = 7, 82 | PositionX = 8, 83 | PositionY = 9, 84 | PositionZ = 10, 85 | Color0R = 11, 86 | Color0G = 12, 87 | Color0B = 13, 88 | Alpha0 = 14, 89 | AllDirectionVelocity = 15, 90 | DirectionVelocity = 16, 91 | PtclScaleX = 17, 92 | PtclScaleY = 18, 93 | Color1R = 19, 94 | Color1G = 20, 95 | Color1B = 21, 96 | EmissionShapeScaleX = 22, 97 | EmissionShapeScaleY = 23, 98 | EmissionShapeScaleZ = 24, 99 | Gravity = 25, 100 | EmitterColor0R = 38, 101 | EmitterColor0G = 39, 102 | EmitterColor0B = 40, 103 | EmitterColor1R = 41, 104 | EmitterColor1G = 42, 105 | EmitterColor1B = 43, 106 | } 107 | 108 | public enum FilterMode : byte 109 | { 110 | Linear, 111 | Nearest, 112 | } 113 | 114 | public enum TexturePatternType 115 | { 116 | None, 117 | FitLifespan, 118 | Clamp, 119 | Loop, 120 | Random, 121 | } 122 | 123 | public enum TextureRepeat 124 | { 125 | Repeat_1x1, 126 | Repeat_2x1, 127 | Repeat_1x2, 128 | Repeat_2x2, 129 | } 130 | 131 | public enum TextureSlot 132 | { 133 | _0, 134 | _1, 135 | _2, 136 | _3, 137 | DepthBuffer, 138 | FrameBuffer, 139 | CubeLightMap, 140 | } 141 | 142 | public enum CpuCore 143 | { 144 | _0 = 0, 145 | _1 = 1, 146 | _2 = 2, 147 | Max = 3, 148 | }; 149 | 150 | public enum VertexRotationMode 151 | { 152 | None = 0, 153 | Rotate_X = 1, 154 | Rotate_Y = 2, 155 | Rotate_Z = 3, 156 | Rotate_XYZ = 4, 157 | Max = 5, 158 | }; 159 | 160 | public enum EmitterType 161 | { 162 | Simple, 163 | Complex, 164 | Max, 165 | } 166 | 167 | public enum PtclType 168 | { 169 | Simple, 170 | Complex, 171 | Child, 172 | Max, 173 | } 174 | 175 | public enum MeshType 176 | { 177 | Particle, 178 | Primitive, 179 | Stripe, 180 | Max, 181 | } 182 | 183 | public enum WrapMode : byte 184 | { 185 | Mirror, 186 | Repeat, 187 | ClampEdge, 188 | MirrorOnce, 189 | } 190 | 191 | public enum DisplayFaceType 192 | { 193 | Both, 194 | Font, 195 | Back, 196 | } 197 | 198 | public enum PtclFollowType 199 | { 200 | SRT = 0, 201 | None = 1, 202 | Translate = 2, 203 | Max = 3, 204 | }; 205 | 206 | public enum ColorSource 207 | { 208 | Constant, 209 | Random, 210 | Key4Value3, 211 | Key8, 212 | } 213 | 214 | public enum ColorMode 215 | { 216 | Color0, 217 | Color0MulTexture, 218 | Color0MulTextureAddColor1MulInvTexture, 219 | Color0MulTextureAddColor1, 220 | } 221 | 222 | public enum AlphaMode 223 | { 224 | TextureAlphaMulAlpha0, 225 | TextureAlphaMinusOneMinusAlpha0Mul2, 226 | TextureRedMulAlpha0, 227 | TextureRedMinusOneMinusAlpha0Mul2, 228 | TextureAlphaMulAlpha0MulAlpha1, 229 | } 230 | 231 | public enum ShaderType 232 | { 233 | Normal = 0, 234 | UserMacro1 = 1, 235 | UserMacro2 = 2, 236 | Max = 3, 237 | }; 238 | 239 | public enum VertexTransformMode 240 | { 241 | Billboard, 242 | PlateXY, 243 | PlateXZ, 244 | DirectionalY, 245 | DirectionalPolygon, 246 | Stripe, 247 | ComplexStripe, 248 | Primitive, 249 | YBillboard, 250 | } 251 | 252 | public enum DepthType 253 | { 254 | DepthTestNoWriteLequal, 255 | NoDepthTest, 256 | DepthTestWriteLequal, 257 | } 258 | 259 | public enum BlendType 260 | { 261 | DefaultAlphaBlend, 262 | AddTranslucent, 263 | SubTranslucent, 264 | Multi, 265 | Screen, 266 | } 267 | 268 | public enum AnimationFunctions 269 | { 270 | Constant, 271 | Key4Value3, 272 | Key8, 273 | Random, 274 | } 275 | 276 | public enum EmitPrimitiveType 277 | { 278 | Vertex, 279 | Random, 280 | EmissionRate, 281 | } 282 | 283 | public enum EmitterFunctions 284 | { 285 | Point, 286 | Circle, 287 | CircleDiv, 288 | CircleFill, 289 | Sphere, 290 | SphereDiv, 291 | SphereDiv64, 292 | SphereFill, 293 | Cylinder, 294 | CylinderFill, 295 | Box, 296 | BoxFill, 297 | Line, 298 | LineDiv, 299 | Rectangle, 300 | Primitive, 301 | Max, 302 | } 303 | 304 | [Flags] 305 | public enum ShaderAvailableAttrib 306 | { 307 | Scale = 0x001, 308 | TexAnim = 0x002, 309 | SubTexAnim = 0x004, 310 | WorldPos = 0x008, 311 | WorldPosDif = 0x010, 312 | Color0 = 0x020, 313 | Color1 = 0x040, 314 | Rot = 0x080, 315 | EmMat = 0x100, 316 | } 317 | 318 | public enum ChildFlags 319 | { 320 | HasChild = 0x0001, 321 | InheritColor0 = 0x0002, 322 | InheritFlucAlpha = 0x0004, 323 | InheritPtclScale = 0x0008, 324 | InheritRotation = 0x0010, 325 | InheritVelocity = 0x0020, 326 | InheritSRT = 0x0040, 327 | DrawBeforeParent = 0x1000, 328 | InheritColor1 = 0x8000, 329 | InheritColorScale = 0x10000, 330 | } 331 | 332 | [Flags] 333 | public enum ParticleBehavior 334 | { 335 | AirResist = 1, 336 | Gravity = 2, 337 | Rotation = 4, 338 | RotationInertia = 8, 339 | WorldDiff = 0x10, 340 | ScaleAnim = 0x40, 341 | Alpha0Anim = 0x80, 342 | Alpha1Anim = 0x100, 343 | Color0Anim = 0x200, 344 | Color1Anim = 0x400, 345 | UVShiftAnim0 = 0x800, 346 | UVShiftAnim1 = 0x1000, 347 | UVShiftAnim2 = 0x2000, 348 | PatternAnim0 = 0x4000, 349 | PatternAnim1 = 0x8000, 350 | PatternAnim2 = 0x40000, 351 | HasTexture1 = 0x80000, 352 | HasTexture2 = 0x100000, 353 | HasTexture3 = 0x200000, 354 | 355 | } 356 | 357 | public enum MatrixRefType 358 | { 359 | Emitter, 360 | Particle, 361 | } 362 | 363 | public enum GX2TexResFormat 364 | { 365 | INVALID = 0x0, 366 | TCS_R8_G8_B8 = 1, 367 | TCS_R8_G8_B8_A8 = 2, 368 | T_BC1_UNORM = 3, 369 | T_BC1_SRGB = 4, 370 | T_BC2_UNORM = 5, 371 | T_BC2_SRGB = 6, 372 | T_BC3_UNORM = 7, 373 | T_BC3_SRGB = 8, 374 | T_BC4_UNORM = 9, 375 | T_BC4_SNORM = 10, 376 | T_BC5_UNORM = 11, 377 | T_BC5_SNORM = 12, 378 | TC_R8_UNORM = 13, 379 | TC_R8_G8_UNORM = 14, 380 | TCS_R8_G8_B8_A8_UNORM = 15, 381 | TCS_R5_G6_B5_UNORM = 25, 382 | }; 383 | } 384 | -------------------------------------------------------------------------------- /EffectLibrary/FileData/EFFN/NamcoEffectFile.cs: -------------------------------------------------------------------------------- 1 | using ShaderLibrary; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using EffectLibrary.EFT2; 10 | 11 | namespace EffectLibrary 12 | { 13 | public class NamcoEffectFile 14 | { 15 | [StructLayout(LayoutKind.Sequential, Size = 0x10)] 16 | public class Header 17 | { 18 | public Magic Magic = "EFFN"; 19 | 20 | public uint Version = 131072; 21 | public ushort Num_Effects; 22 | public ushort Num_External_Models; 23 | public ushort Multi_Part_Effects; 24 | public ushort Header_Chunk_Align = 1; 25 | } 26 | 27 | [StructLayout(LayoutKind.Sequential)] 28 | public class EffectHeader 29 | { 30 | public ushort Kind; 31 | public ushort Unknown; 32 | public uint EmitterSet_ID; 33 | public uint External_Model_Idx; 34 | public ushort Variant_Start_Idx; 35 | public ushort Variant_Count; 36 | } 37 | 38 | [StructLayout(LayoutKind.Sequential)] 39 | public class EffectVariant 40 | { 41 | public ushort StartFrame; 42 | public ushort EmitterSetID; 43 | } 44 | 45 | [JsonIgnore] 46 | public Header FileHeader = new Header(); 47 | 48 | public List Entries = new List(); 49 | public List EffectVariants = new List(); 50 | public List EffectModels = new List(); 51 | public List EntryNames = new List(); 52 | public List ExternalModelNames = new List(); 53 | public List ExternalBoneNames = new List(); 54 | 55 | [JsonIgnore] 56 | public PtclFile PtclFile; 57 | 58 | public NamcoEffectFile() { } 59 | 60 | public NamcoEffectFile(PtclFile ptcl) { PtclFile = ptcl; } 61 | 62 | public NamcoEffectFile(string filePath) 63 | { 64 | Read(File.OpenRead(filePath)); 65 | } 66 | 67 | public void Save(string filePath) 68 | { 69 | using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) 70 | { 71 | Write(fs); 72 | } 73 | } 74 | 75 | private void Read(Stream stream) 76 | { 77 | var reader = new BinaryReader(stream); 78 | 79 | this.FileHeader = reader.ReadStruct
(); 80 | this.Entries = reader.ReadStructs(FileHeader.Num_Effects); 81 | this.EffectVariants = reader.ReadStructs(FileHeader.Multi_Part_Effects); 82 | this.EffectModels = reader.ReadBytes((int)FileHeader.Num_External_Models).ToList(); 83 | 84 | 85 | for (int i = 0; i < FileHeader.Num_Effects; i++) 86 | this.EntryNames.Add(reader.ReadUtf8Z()); 87 | 88 | for (int i = 0; i < FileHeader.Num_External_Models; i++) 89 | this.ExternalModelNames.Add(reader.ReadUtf8Z()); 90 | 91 | for (int i = 0; i < FileHeader.Multi_Part_Effects; i++) 92 | this.ExternalBoneNames.Add(reader.ReadUtf8Z()); 93 | 94 | var align = GetRequiredChunkAlign(); 95 | 96 | reader.AlignBytes(align); 97 | 98 | var subStream = new SubStream(reader.BaseStream, reader.BaseStream.Position); 99 | PtclFile = new PtclFile(subStream); 100 | } 101 | 102 | private void Write(Stream stream) 103 | { 104 | var writer = new BinaryWriter(stream); 105 | 106 | var align = GetRequiredChunkAlign(); 107 | 108 | FileHeader.Header_Chunk_Align = 1; 109 | if (align == 4096) FileHeader.Header_Chunk_Align = 1; 110 | if (align == 8192) FileHeader.Header_Chunk_Align = 2; 111 | 112 | FileHeader.Num_Effects = (ushort)this.Entries.Count; 113 | FileHeader.Multi_Part_Effects = (ushort)this.EffectVariants.Count; 114 | FileHeader.Num_External_Models = (ushort)this.ExternalModelNames.Count; 115 | 116 | writer.WriteStruct(FileHeader); 117 | writer.WriteStructs(Entries); 118 | writer.WriteStructs(EffectVariants); 119 | writer.Write(EffectModels.ToArray()); 120 | 121 | for (int i = 0; i < this.EntryNames.Count; i++) 122 | writer.WriteZeroTerminatedString(EntryNames[i]); 123 | 124 | for (int i = 0; i < this.ExternalModelNames.Count; i++) 125 | writer.WriteZeroTerminatedString(ExternalModelNames[i]); 126 | 127 | for (int i = 0; i < this.ExternalBoneNames.Count; i++) 128 | writer.WriteZeroTerminatedString(ExternalBoneNames[i]); 129 | 130 | 131 | writer.AlignBytes(align); 132 | 133 | var mem = new MemoryStream(); 134 | PtclFile.Save(mem); 135 | writer.Write(mem.ToArray()); 136 | } 137 | 138 | 139 | private int GetRequiredChunkAlign() 140 | { 141 | int size = 0x10; //header size 142 | size += this.Entries.Count * 0x10; 143 | size += this.EffectVariants.Count * 0x4; 144 | size += this.EffectModels.Count; 145 | size += this.EntryNames.Sum(x => x.Length + 1); 146 | size += this.ExternalModelNames.Sum(x => x.Length + 1); 147 | size += this.ExternalBoneNames.Sum(x => x.Length + 1); 148 | return (size + 0x1000) & ~0xFFF; 149 | } 150 | 151 | #region Json Conversion 152 | 153 | public void Export(string filePath) { 154 | 155 | var list = new List(); 156 | for (int i = 0; i < Entries.Count; i++) 157 | { 158 | var entry = Entries[i]; 159 | 160 | JsonExportEntry json_entry = new JsonExportEntry() 161 | { 162 | EmitterSet_ID = entry.EmitterSet_ID, 163 | Kind = entry.Kind, 164 | Name = this.EntryNames[i], 165 | Unknown = entry.Unknown, 166 | }; 167 | list.Add(json_entry); 168 | 169 | int model_idx = (int)entry.External_Model_Idx - 1; 170 | 171 | if (model_idx != -1 && this.EffectModels.Count > 0) 172 | { 173 | var model_flag = this.EffectModels[(int)model_idx]; 174 | json_entry.ExternalModelFlag = (byte)model_flag; 175 | json_entry.ExternalModelString = this.ExternalModelNames[(int)model_idx]; 176 | } 177 | 178 | int start_idx = (int)entry.Variant_Start_Idx - 1; 179 | 180 | for (int j = 0; j < entry.Variant_Count; j++) 181 | { 182 | var variant = this.EffectVariants[start_idx + j]; 183 | json_entry.Variants.Add(new JsonExportVariant() 184 | { 185 | BoneName = this.ExternalBoneNames[start_idx + j], 186 | EmitterSetID = variant.EmitterSetID, 187 | StartFrame = variant.StartFrame, 188 | }); 189 | } 190 | } 191 | 192 | File.WriteAllText(filePath, JsonConvert.SerializeObject(list, Formatting.Indented)); 193 | } 194 | 195 | public void Import(string filePath) 196 | { 197 | var imported = JsonConvert.DeserializeObject>(File.ReadAllText(filePath)); 198 | 199 | int variant_Start_Idx = 0; 200 | 201 | EntryNames.Clear(); 202 | EffectModels.Clear(); 203 | ExternalModelNames.Clear(); 204 | ExternalBoneNames.Clear(); 205 | 206 | foreach (var entry in imported) 207 | { 208 | var effect_entry = new EffectHeader() 209 | { 210 | EmitterSet_ID = entry.EmitterSet_ID, 211 | External_Model_Idx = 0, 212 | Kind = entry.Kind, 213 | Unknown = entry.Unknown, 214 | Variant_Count = 0, 215 | Variant_Start_Idx = 0, 216 | }; 217 | this.Entries.Add(effect_entry); 218 | EntryNames.Add(entry.Name); 219 | 220 | if (!string.IsNullOrEmpty(entry.ExternalModelString)) 221 | { 222 | effect_entry.External_Model_Idx = (uint)EffectModels.Count + 1; //0 based index 223 | 224 | EffectModels.Add(entry.ExternalModelFlag); 225 | ExternalModelNames.Add(entry.ExternalModelString); 226 | } 227 | 228 | if (entry.Variants.Count > 0) //0 based index 229 | { 230 | effect_entry.Variant_Start_Idx = (ushort)(this.EffectVariants.Count + 1); 231 | effect_entry.Variant_Count = (ushort)entry.Variants.Count; 232 | } 233 | foreach (var variant in entry.Variants) 234 | { 235 | this.EffectVariants.Add(new EffectVariant() 236 | { 237 | EmitterSetID = variant.EmitterSetID, 238 | StartFrame = variant.StartFrame, 239 | }); 240 | ExternalBoneNames.Add(variant.BoneName); 241 | } 242 | 243 | variant_Start_Idx += entry.Variants.Count; 244 | } 245 | } 246 | 247 | class JsonExportEntry //more readable exported option 248 | { 249 | public string Name; 250 | 251 | public ushort Kind; 252 | public ushort Unknown; 253 | public uint EmitterSet_ID; 254 | 255 | public byte ExternalModelFlag; 256 | public byte ExternalModelID; 257 | public string ExternalModelString = ""; 258 | 259 | public List Variants = new List(); 260 | } 261 | 262 | class JsonExportVariant 263 | { 264 | public string BoneName; 265 | 266 | public ushort StartFrame; 267 | public ushort EmitterSetID; 268 | } 269 | 270 | #endregion 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /EffectLibrary/FileData/EFT1/PtclFile.cs: -------------------------------------------------------------------------------- 1 | using Syroot.BinaryData; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EffectLibrary.EFT1 9 | { 10 | public class PtclFile 11 | { 12 | public void Read(BinaryDataReader reader) 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /EffectLibrary/FileData/EFT2/EmitterList.cs: -------------------------------------------------------------------------------- 1 | using BfresLibrary; 2 | using Newtonsoft.Json; 3 | using ShaderLibrary; 4 | using Syroot.BinaryData; 5 | using Syroot.NintenTools.NSW.Bntx; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Reflection.PortableExecutable; 11 | using System.Runtime.InteropServices; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | 15 | namespace EffectLibrary.EFT2 16 | { 17 | public class EmitterList : SectionBase 18 | { 19 | public List EmitterSets = new List(); 20 | 21 | public override string Magic => "ESTA"; 22 | 23 | public EmitterList() 24 | { 25 | Header.Magic = Magic; 26 | } 27 | 28 | public override void Read(BinaryReader reader, PtclFile ptclFile) 29 | { 30 | base.Read(reader, ptclFile); 31 | 32 | reader.SeekBegin(StartPosition + Header.ChildrenOffset); 33 | for (int i = 0; i < Header.ChildrenCount; i++) 34 | { 35 | var emitterSet = new EmitterSet(); 36 | emitterSet.Read(reader, ptclFile); 37 | EmitterSets.Add(emitterSet); 38 | } 39 | } 40 | 41 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 42 | { 43 | Header.ChildrenCount = (ushort)EmitterSets.Count; 44 | 45 | base.Write(writer, ptclFile); 46 | 47 | WriteChildOffset(writer); 48 | WriteList(EmitterSets, writer, ptclFile); 49 | 50 | WriteSectionSize(writer); 51 | 52 | writer.AlignBytes(16); 53 | WriteNextOffset(writer, false); 54 | } 55 | 56 | public EmitterSet GetEmitterSet(string name) 57 | { 58 | return EmitterSets.FirstOrDefault(x => x.Name == name); 59 | } 60 | } 61 | 62 | public class EmitterSet : SectionBase 63 | { 64 | public List Emitters = new List(); 65 | 66 | public string Name { get; set; } 67 | 68 | public override string Magic => "ESET"; 69 | 70 | public uint Unknown1; 71 | public uint Unknown2; 72 | 73 | public uint Unknown3; 74 | public uint Unknown4; 75 | public uint Unknown5; 76 | public uint Unknown6; 77 | 78 | public EmitterSet() 79 | { 80 | Header.Magic = Magic; 81 | 82 | } 83 | 84 | public override void Read(BinaryReader reader, PtclFile ptclFile) 85 | { 86 | base.Read(reader, ptclFile); 87 | 88 | if (Header.Magic != Magic) 89 | throw new Exception(); 90 | 91 | reader.SeekBegin(StartPosition + Header.BinaryOffset); 92 | ReadBinary(reader); 93 | 94 | reader.SeekBegin(StartPosition + Header.ChildrenOffset); 95 | for (int i = 0; i < Header.ChildrenCount; i++) 96 | { 97 | var emitter = new Emitter(this); 98 | emitter.Read(reader, ptclFile); 99 | Emitters.Add(emitter); 100 | 101 | emitter.Data.Order = i; 102 | } 103 | 104 | if (Header.NextSectionOffset != uint.MaxValue) 105 | reader.SeekBegin(StartPosition + Header.NextSectionOffset); 106 | } 107 | 108 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 109 | { 110 | Header.ChildrenCount = (ushort)Emitters.Count; 111 | 112 | base.Write(writer, ptclFile); 113 | 114 | WriteBinaryOffset(writer); 115 | WriteBinary(writer); 116 | 117 | WriteChildOffset(writer); 118 | WriteList(Emitters, writer, ptclFile); 119 | 120 | WriteSectionSize(writer); 121 | } 122 | 123 | private void ReadBinary(BinaryReader reader) 124 | { 125 | reader.ReadBytes(16); //padding 126 | Name = reader.ReadFixedString(64); 127 | reader.ReadUInt32(); //emitter count 128 | reader.ReadUInt32(); //0 129 | reader.ReadUInt32(); //0 130 | reader.ReadUInt32(); //0 131 | if (PtclHeader.Header.VFXVersion >= 0x16) 132 | { 133 | Unknown1 = reader.ReadUInt32(); 134 | Unknown2 = reader.ReadUInt32(); 135 | } 136 | if (PtclHeader.Header.VFXVersion >= 0x24) 137 | { 138 | Unknown3 = reader.ReadUInt32(); 139 | Unknown4 = reader.ReadUInt32(); 140 | Unknown5 = reader.ReadUInt32(); 141 | Unknown6 = reader.ReadUInt32(); 142 | } 143 | } 144 | 145 | private void WriteBinary(BinaryWriter writer) 146 | { 147 | writer.Write(new byte[16]); 148 | 149 | long pos = writer.BaseStream.Position; 150 | 151 | writer.Write(Encoding.UTF8.GetBytes(Name)); 152 | 153 | writer.Seek((int)pos + 64, SeekOrigin.Begin); 154 | 155 | writer.Write(Emitters.Count + Emitters.Sum(x => x.Children.Count)); 156 | writer.Write(0); 157 | writer.Write(0); 158 | writer.Write(0); 159 | 160 | if (PtclHeader.Header.VFXVersion >= 0x16) 161 | { 162 | writer.Write(Unknown1); 163 | writer.Write(Unknown2); 164 | } 165 | if (PtclHeader.Header.VFXVersion >= 0x24) 166 | { 167 | writer.Write(Unknown3); 168 | writer.Write(Unknown4); 169 | writer.Write(Unknown5); 170 | writer.Write(Unknown6); 171 | } 172 | } 173 | } 174 | 175 | 176 | public class Emitter : SectionBase 177 | { 178 | public override string Magic => "EMTR"; 179 | 180 | public byte[] BinaryData; 181 | 182 | public string Name { get; set; } 183 | 184 | public EmitterData Data = new EmitterData(); 185 | 186 | public List Children = new List(); 187 | 188 | public List SubSections = new List(); 189 | 190 | public EmitterSet EmitterSet; 191 | 192 | public Emitter(EmitterSet emitterSet) 193 | { 194 | EmitterSet = emitterSet; 195 | Header.Magic = Magic; 196 | } 197 | 198 | public override void Read(BinaryReader reader, PtclFile ptclFile) 199 | { 200 | base.Read(reader, ptclFile); 201 | 202 | if (Header.Magic != Magic) 203 | throw new Exception(); 204 | 205 | reader.SeekBegin(StartPosition + Header.BinaryOffset); 206 | 207 | //size. 208 | var end = Header.AttrOffset != uint.MaxValue ? Header.AttrOffset : Header.Size; 209 | var size = end - Header.BinaryOffset; 210 | BinaryData = reader.ReadBytes((int)size); 211 | 212 | reader.SeekBegin(StartPosition + Header.BinaryOffset); 213 | ReadBinary(reader); 214 | 215 | reader.SeekBegin(StartPosition + Header.ChildrenOffset); 216 | for (int i = 0; i < Header.ChildrenCount; i++) 217 | { 218 | var sect = new Emitter(EmitterSet); 219 | sect.Read(reader, ptclFile); 220 | Children.Add(sect); 221 | 222 | sect.Data.Order = i; 223 | } 224 | 225 | 226 | if (Header.AttrOffset != uint.MaxValue) 227 | { 228 | reader.SeekBegin(StartPosition + Header.AttrOffset); 229 | //Sub sections 230 | while (true) 231 | { 232 | EmitterSubSection sect = new(); 233 | 234 | // Peek magic 235 | string magic = Encoding.ASCII.GetString(reader.ReadBytes(4)); 236 | reader.BaseStream.Seek(-4, SeekOrigin.Current); 237 | 238 | if (magic.Substring(0, 2) == "EA") // Emitter animation kind 239 | sect = new EmitterAnimation(magic); 240 | 241 | sect.Read(reader, ptclFile); 242 | SubSections.Add(sect); 243 | 244 | //end 245 | if (sect.Header.NextSectionOffset == uint.MaxValue) 246 | break; 247 | } 248 | } 249 | 250 | if (Header.NextSectionOffset != uint.MaxValue) 251 | reader.SeekBegin(StartPosition + Header.NextSectionOffset); 252 | } 253 | 254 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 255 | { 256 | Header.ChildrenCount = (ushort)Children.Count; 257 | 258 | base.Write(writer, ptclFile); 259 | 260 | writer.AlignBytes(256); 261 | WriteBinaryOffset(writer); 262 | 263 | var pos = writer.BaseStream.Position; 264 | writer.Write(BinaryData); 265 | 266 | //sub section 267 | if (SubSections.Count > 0) 268 | { 269 | writer.AlignBytes(4); 270 | WriteSubSectionOffset(writer); 271 | WriteList(SubSections, writer, ptclFile); 272 | } 273 | 274 | var end_pos = writer.BaseStream.Position; 275 | 276 | writer.Seek((int)pos, SeekOrigin.Begin); 277 | WriteBinary(writer); 278 | 279 | writer.Seek((int)end_pos, SeekOrigin.Begin); 280 | 281 | WriteSectionSize(writer); 282 | 283 | if (Children.Count > 0) 284 | { 285 | writer.AlignBytes(4); 286 | 287 | WriteChildOffset(writer); 288 | WriteList(Children, writer, ptclFile); 289 | } 290 | } 291 | 292 | private void ReadBinary(BinaryReader reader) 293 | { 294 | Data = PtclSerialize.Serialize(reader, new EmitterData(), PtclHeader.Header.VFXVersion, reader.BaseStream.Position); 295 | Name = Data.Name; 296 | if (!string.IsNullOrEmpty(Data.Namev40)) 297 | Name = Data.Namev40; 298 | } 299 | 300 | private void VerifyDump() 301 | { 302 | File.WriteAllBytes("og.bin", BinaryData); 303 | File.WriteAllText("test.json", ToJson()); 304 | 305 | var mem = new MemoryStream(); 306 | using (var wr = new BinaryWriter(mem)) 307 | { 308 | PtclSerialize.Deserialize(wr, Data, PtclHeader.Header.VFXVersion); 309 | } 310 | File.WriteAllBytes("saved.bin", mem.ToArray()); 311 | throw new Exception(); 312 | } 313 | 314 | public string ToJson() 315 | { 316 | var jsonsettings = new JsonSerializerSettings 317 | { 318 | NullValueHandling = NullValueHandling.Ignore, 319 | Converters = new List() 320 | { 321 | new Newtonsoft.Json.Converters.StringEnumConverter(), 322 | }, 323 | }; 324 | return JsonConvert.SerializeObject(Data, Formatting.Indented, jsonsettings); 325 | } 326 | 327 | private void WriteBinary(BinaryWriter writer) 328 | { 329 | var pos = writer.BaseStream.Position; 330 | PtclSerialize.Deserialize(writer, Data, PtclHeader.Header.VFXVersion); 331 | } 332 | 333 | public Texture GetTextureBinary(TextureSampler sampler) 334 | { 335 | return PtclHeader.Textures.TryGetTexture(sampler.TextureID); 336 | } 337 | 338 | public Model GetVolumeModelBinary() 339 | { 340 | return PtclHeader.Primitives.TryGetModel(Data.ShapeInfo.PrimitiveIndex); 341 | } 342 | 343 | public Model GetModelBinary() 344 | { 345 | return PtclHeader.Primitives.TryGetModel(Data.ParticleData.PrimitiveID); 346 | } 347 | 348 | public Model GetModelExtraBinary() 349 | { 350 | return PtclHeader.Primitives.TryGetModel(Data.ParticleData.PrimitiveExID); 351 | } 352 | 353 | public BnshFile.ShaderVariation GetShaderBinary() 354 | { 355 | return PtclHeader.Shaders.TryGetShader(Data.ShaderReferences.ShaderIndex); 356 | } 357 | 358 | public BnshFile.ShaderVariation GetUser1ShaderBinary() 359 | { 360 | if (Data.ShaderReferences.ShaderIndex == Data.ShaderReferences.UserShaderIndex1) 361 | return null; 362 | 363 | return PtclHeader.Shaders.TryGetShader(Data.ShaderReferences.UserShaderIndex1); 364 | } 365 | 366 | public BnshFile.ShaderVariation GetUser2ShaderBinary() 367 | { 368 | if (Data.ShaderReferences.UserShaderIndex2 == 0) 369 | return null; 370 | 371 | return PtclHeader.Shaders.TryGetShader(Data.ShaderReferences.UserShaderIndex2); 372 | } 373 | 374 | public BnshFile.ShaderVariation GetComputeShaderBinary() 375 | { 376 | if (Data.ShaderReferences.ComputeShaderIndex == -1) return null; 377 | 378 | return PtclHeader.Shaders.TryGetComputeShader(Data.ShaderReferences.ComputeShaderIndex); 379 | } 380 | } 381 | 382 | public class EmitterSubSection : SectionBase 383 | { 384 | [JsonIgnore] 385 | public byte[] Data; 386 | 387 | public EmitterSubSection() { } 388 | 389 | public EmitterSubSection(string magic) 390 | { 391 | Header.Magic = magic; 392 | } 393 | 394 | public override void Read(BinaryReader reader, PtclFile ptclFile) 395 | { 396 | base.Read(reader, ptclFile); 397 | 398 | reader.SeekBegin(StartPosition + Header.BinaryOffset); 399 | if (!ReadBinary(reader, ptclFile)) // Read entire section if no reading support 400 | Data = reader.ReadBytes((int)(Header.Size - Header.BinaryOffset)); 401 | 402 | //goto next section 403 | if (Header.NextSectionOffset != uint.MaxValue) 404 | reader.SeekBegin(StartPosition + Header.NextSectionOffset); 405 | } 406 | 407 | public virtual bool ReadBinary(BinaryReader reader, PtclFile ptclFile) 408 | { 409 | return false; 410 | } 411 | 412 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 413 | { 414 | base.Write(writer, ptclFile); 415 | 416 | WriteBinaryOffset(writer); 417 | if (!WriteBinary(writer, ptclFile)) // Write entire section if no writing support 418 | writer.Write(Data); 419 | 420 | WriteSectionSize(writer); 421 | } 422 | 423 | public virtual bool WriteBinary(BinaryWriter writer, PtclFile ptclFile) 424 | { 425 | return false; 426 | } 427 | 428 | public virtual void Import(string filePath) 429 | { 430 | this.Data = File.ReadAllBytes(filePath); 431 | } 432 | 433 | public virtual void Export(string filePath) 434 | { 435 | File.WriteAllBytes(filePath, this.Data); 436 | } 437 | } 438 | } 439 | -------------------------------------------------------------------------------- /EffectLibrary/FileData/EFT2/EmitterStructs/Emitter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualBasic; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Runtime.Serialization; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace EffectLibrary.EFT2 11 | { 12 | public class EmitterData 13 | { 14 | public uint Flag; 15 | public uint RandomSeed; 16 | public uint Padding1; 17 | public uint Padding2; 18 | 19 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] 20 | [VersionCheck(VersionCompare.Less, 40)] 21 | public string Name = null; 22 | 23 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 96)] 24 | [VersionCheck(VersionCompare.GreaterOrEqual, 40)] 25 | public string Namev40 = null; 26 | 27 | public EmitterStatic EmitterStatic = new EmitterStatic(); 28 | public EmitterInfo EmitterInfo = new EmitterInfo(); 29 | public EmitterInheritance ChildInheritance = new EmitterInheritance(); 30 | public Emission Emission = new Emission(); 31 | public EmitterShapeInfo ShapeInfo = new EmitterShapeInfo(); 32 | public EmitterRenderState RenderState = new EmitterRenderState(); 33 | public ParticleData ParticleData = new ParticleData(); 34 | 35 | [VersionCheck(VersionCompare.Less, 36)] 36 | public EmitterCombiner Combiner = null; 37 | 38 | [VersionCheck(VersionCompare.Equals, 36)] 39 | public EmitterCombinerV36 CombinerV36 = null; 40 | 41 | [VersionCheck(VersionCompare.Greater, 40)] 42 | public EmitterCombinerV40 CombinerV40 = null; 43 | 44 | public ShaderRefInfo ShaderReferences = new ShaderRefInfo(); 45 | 46 | public ActionInfo Action = new ActionInfo(); 47 | 48 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 49 | [VersionCheck(VersionCompare.Greater, 40)] 50 | public string DepthMode = null; 51 | 52 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 52)] 53 | [VersionCheck(VersionCompare.Greater, 40)] 54 | public string PassInfo = null; 55 | 56 | public ParticleVelocityInfo ParticleVelocity = new ParticleVelocityInfo(); 57 | 58 | [VersionCheck(VersionCompare.GreaterOrEqual, 36)] 59 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] 60 | public float[] UnknownV36 = null; 61 | 62 | public ParticleColor ParticleColor = new ParticleColor(); 63 | public ParticleScale ParticleScale = new ParticleScale(); 64 | public ParticleFlucInfo ParticleFluctuation = new ParticleFlucInfo(); 65 | 66 | public TextureSampler Sampler0; 67 | public TextureSampler Sampler1; 68 | public TextureSampler Sampler2; 69 | 70 | [VersionCheck(VersionCompare.Greater, 40)] 71 | public TextureSampler Sampler3; 72 | [VersionCheck(VersionCompare.Greater, 40)] 73 | public TextureSampler Sampler4; 74 | [VersionCheck(VersionCompare.Greater, 40)] 75 | public TextureSampler Sampler5; 76 | 77 | public TextureAnim TextureAnim0; 78 | public TextureAnim TextureAnim1; 79 | public TextureAnim TextureAnim2; 80 | 81 | [VersionCheck(VersionCompare.Greater, 40)] 82 | public TextureAnim TextureAnim3; 83 | [VersionCheck(VersionCompare.Greater, 40)] 84 | public TextureAnim TextureAnim4; 85 | [VersionCheck(VersionCompare.Greater, 40)] 86 | public TextureAnim TextureAnim5; 87 | 88 | [VersionCheck(VersionCompare.GreaterOrEqual, 22)] 89 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x40)] 90 | public byte[] Reserved = null; 91 | 92 | [IgnoreDataMember] 93 | public int Order; 94 | 95 | public List GetSamplers() 96 | { 97 | List samplers = new List(); 98 | samplers.Add(Sampler0); 99 | samplers.Add(Sampler1); 100 | samplers.Add(Sampler2); 101 | 102 | if (Sampler3 != null) //v40 > with 6 samplers total 103 | { 104 | samplers.Add(Sampler3); 105 | samplers.Add(Sampler4); 106 | samplers.Add(Sampler5); 107 | } 108 | return samplers; 109 | } 110 | } 111 | 112 | public class EmitterStatic 113 | { 114 | public uint Flags1; 115 | public uint Flags2; 116 | public uint Flags3; 117 | public uint Flags4; 118 | 119 | public uint NumColor0Keys; 120 | public uint NumAlpha0Keys; 121 | public uint NumColor1Keys; 122 | public uint NumAlpha1Keys; 123 | public uint NumScaleKeys; 124 | public uint NumParamKeys; 125 | 126 | public uint Unknown1; 127 | public uint Unknown2; 128 | 129 | [VersionCheck(VersionCompare.Greater, 50)] 130 | public uint NumAnim2Keys; 131 | [VersionCheck(VersionCompare.Greater, 50)] 132 | public uint NumAnim3Keys; 133 | [VersionCheck(VersionCompare.Greater, 50)] 134 | public uint NumAnim4Keys; 135 | [VersionCheck(VersionCompare.Greater, 50)] 136 | public uint NumAnim5Keys; 137 | 138 | public float Color0LoopRate; 139 | public float Alpha0LoopRate; 140 | public float Color1LoopRate; 141 | public float Alpha1LoopRate; 142 | public float ScaleLoopRate; 143 | 144 | public float Color0LoopRandom; 145 | public float Alpha0LoopRandom; 146 | public float Color1LoopRandom; 147 | public float Alpha1LoopRandom; 148 | public float ScaleLoopRandom; 149 | 150 | public float Unknown3; 151 | public float Unknown4; 152 | 153 | public float GravityDirX; 154 | public float GravityDirY; 155 | public float GravityDirZ; 156 | 157 | public float GravityScale; 158 | 159 | public float AirRes; 160 | 161 | public float val_0x74; 162 | public float val_0x78; 163 | public float val_0x82; 164 | 165 | public float CenterX; 166 | public float CenterY; 167 | 168 | public float Offset; 169 | public float Padding; 170 | 171 | public float AmplitudeX; 172 | public float AmplitudeY; 173 | 174 | public float CycleX; 175 | public float CycleY; 176 | 177 | public float PhaseRndX; 178 | public float PhaseRndY; 179 | 180 | public float PhaseInitX; 181 | public float PhaseInitY; 182 | 183 | public float Coefficient0; 184 | public float Coefficient1; 185 | 186 | public float val_0xB8; 187 | public float val_0xBC; 188 | 189 | public TexPatAnim TexPatternAnim0; 190 | public TexPatAnim TexPatternAnim1; 191 | public TexPatAnim TexPatternAnim2; 192 | 193 | [VersionCheck(VersionCompare.Greater, 40)] 194 | public TexPatAnim TexPatternAnim3; 195 | [VersionCheck(VersionCompare.Greater, 40)] 196 | public TexPatAnim TexPatternAnim4; 197 | [VersionCheck(VersionCompare.Greater, 40)] 198 | public TexPatAnim TexPatternAnim5; 199 | 200 | public TexScrollAnim TexScrollAnim0; 201 | public TexScrollAnim TexScrollAnim1; 202 | public TexScrollAnim TexScrollAnim2; 203 | 204 | [VersionCheck(VersionCompare.Greater, 40)] 205 | public TexScrollAnim TexScrollAnim3; 206 | [VersionCheck(VersionCompare.Greater, 40)] 207 | public TexScrollAnim TexScrollAnim4; 208 | [VersionCheck(VersionCompare.Greater, 40)] 209 | public TexScrollAnim TexScrollAnim5; 210 | 211 | public float ColorScale; 212 | public float val_0x364; 213 | public float val_0x368; 214 | public float val_0x36A; 215 | 216 | public AnimationKeyTable Color0; 217 | public AnimationKeyTable Alpha0; 218 | public AnimationKeyTable Color1; 219 | public AnimationKeyTable Alpha1; 220 | 221 | public float SoftEdgeParam1; 222 | public float SoftEdgeParam2; 223 | public float FresnelAlphaParam1; 224 | public float FresnelAlphaParam2; 225 | public float NearDistAlphaParam1; 226 | public float NearDistAlphaParam2; 227 | public float FarDistAlphaParam1; 228 | public float FarDistAlphaParam2; 229 | 230 | public float DecalParam1; 231 | public float DecalParam2; 232 | 233 | public float AlphaThreshold; 234 | public float Padding2; 235 | 236 | public float AddVelToScale; 237 | public float SoftPartcileDist; 238 | public float SoftParticleVolume; 239 | public float Padding3; 240 | 241 | public AnimationKeyTable ScaleAnim; 242 | public AnimationKeyTable ParamAnim; 243 | 244 | [VersionCheck(VersionCompare.Greater, 50)] 245 | public AnimationKeyTable Anim1Keys; 246 | [VersionCheck(VersionCompare.Greater, 50)] 247 | public AnimationKeyTable Anim2Keys; 248 | [VersionCheck(VersionCompare.Greater, 50)] 249 | public AnimationKeyTable Anim3Keys; 250 | [VersionCheck(VersionCompare.Greater, 50)] 251 | public AnimationKeyTable Anim4Keys; 252 | 253 | [VersionCheck(VersionCompare.Greater, 40)] 254 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 255 | public float[] Unknown6; 256 | 257 | public float RotateInitX; 258 | public float RotateInitY; 259 | public float RotateInitZ; 260 | public float RotateInitEmpty; 261 | 262 | public float RotateInitRandX; 263 | public float RotateInitRandY; 264 | public float RotateInitRandZ; 265 | public float RotateInitRandEmpty; 266 | 267 | public float RotateAddX; 268 | public float RotateAddY; 269 | public float RotateAddZ; 270 | public float RotateRegist; 271 | 272 | public float RotateAddRandX; 273 | public float RotateAddRandY; 274 | public float RotateAddRandZ; 275 | public float Padding4; 276 | 277 | public float ScaleLimitDistNear; 278 | public float ScaleLimitDistFar; 279 | 280 | public float Padding5; 281 | public float Padding6; 282 | 283 | [VersionCheck(VersionCompare.Greater, 40)] 284 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 285 | public float[] Unknown7; 286 | } 287 | 288 | public class TexPatAnim 289 | { 290 | public float Num; 291 | public float Frequency; 292 | public float NumRandom; 293 | public float Pad; 294 | 295 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] 296 | public int[] Table = new int[32]; 297 | } 298 | 299 | public class TexScrollAnim 300 | { 301 | public float ScrollAddX; 302 | public float ScrollAddY; 303 | 304 | public float ScrollX; 305 | public float ScrollY; 306 | 307 | public float ScrollRandomX; 308 | public float ScrollRandomY; 309 | 310 | public float ScaleAddX; 311 | public float ScaleAddY; 312 | 313 | public float ScaleX; 314 | public float ScaleY; 315 | 316 | public float ScaleRandomX; 317 | public float ScaleRandomY; 318 | 319 | public float RotationAdd; 320 | public float Rotation; 321 | public float RotationRandom; 322 | public float RotationType; 323 | 324 | public float UVScaleX; 325 | public float UVScaleY; 326 | 327 | public float UVDivX; 328 | public float UVDivY; 329 | } 330 | 331 | [StructLayout(LayoutKind.Sequential, Size = 0x10)] 332 | public class AnimationKeyTable 333 | { 334 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] 335 | public AnimationKey[] Keys = new AnimationKey[8]; 336 | } 337 | 338 | [StructLayout(LayoutKind.Sequential, Size = 0x10)] 339 | public class AnimationKey 340 | { 341 | public float X; 342 | public float Y; 343 | public float Z; 344 | public float Time; //ratio 0.0 -> 1.0 345 | } 346 | 347 | 348 | public class EmitterInfo 349 | { 350 | public byte IsParticleDraw; 351 | public byte SortType; 352 | public byte CalcType; 353 | public byte FollowType; 354 | public byte IsFadeEmit; 355 | public byte IsFadeAlphaFade; 356 | public byte IsScaleFade; 357 | public byte RandomSeedType; 358 | public byte IsUpdateMatrixByEmit; 359 | public byte TestAlways; 360 | public byte InterpolateEmissionAmount; 361 | public byte IsAlphaFadeIn; 362 | public byte IsScaleFadeIn; 363 | public byte padding1; 364 | public byte padding2; 365 | public byte padding3; 366 | 367 | public uint RandomSeed; 368 | public uint DrawPath; //render pass 369 | public int AlphaFadeTime; 370 | public int FadeInTime; 371 | public float TransX; 372 | public float TransY; 373 | public float TransZ; 374 | public float TransRandX; 375 | public float TransRandY; 376 | public float TransRandZ; 377 | public float RotateX; 378 | public float RotateY; 379 | public float RotateZ; 380 | public float RotateRandX; 381 | public float RotateRandY; 382 | public float RotateRandZ; 383 | public float ScaleX; 384 | public float ScaleY; 385 | public float ScaleZ; 386 | 387 | public float Color0R; 388 | public float Color0G; 389 | public float Color0B; 390 | public float Color0A; 391 | 392 | public float Color1R; 393 | public float Color1G; 394 | public float Color1B; 395 | public float Color1A; 396 | 397 | public float EmissionRangeNear; 398 | public float EmissionRangeFar; 399 | public float EmissionRatioFar; 400 | } 401 | 402 | 403 | public class EmitterInheritance 404 | { 405 | public byte Velocity; 406 | public byte Scale; 407 | public byte Rotate; 408 | public byte ColorScale; 409 | public byte Color0; 410 | public byte Color1; 411 | public byte Alpha0; 412 | public byte Alpha1; 413 | public byte DrawPath; 414 | public byte PreDraw; 415 | public byte Alpha0EachFrame; 416 | public byte Alpha1EachFrame; 417 | public byte EnableEmitterParticle; 418 | public byte padding1; 419 | public byte padding2; 420 | public byte padding3; 421 | 422 | [VersionCheck(VersionCompare.Greater, 40)] 423 | public ulong UnknownV40; 424 | 425 | public float VelocityRate; 426 | public float ScaleRate; 427 | } 428 | 429 | 430 | [StructLayout(LayoutKind.Sequential, Size = 0x10)] 431 | public class Emission 432 | { 433 | public bool isOneTime; 434 | public bool IsWorldGravity; 435 | public bool IsEmitDistEnabled; 436 | public bool IsWorldOrientedVelocity; 437 | public uint Start; 438 | public uint Timing; 439 | public uint Duration; 440 | public float Rate; 441 | public float RateRandom; 442 | public int Interval; 443 | public float IntervalRandom; 444 | public float PositionRandom; 445 | public float GravityScale; 446 | public float GravityDirX; 447 | public float GravityDirY; 448 | public float GravityDirZ; 449 | public float EmitterDistUnit; 450 | public float EmitterDistMin; 451 | public float EmitterDistMax; 452 | public float EmitterDistMarg; 453 | public int EmitterDistParticlesMax; 454 | } 455 | 456 | public class EmitterShapeInfo 457 | { 458 | public byte VolumeType; 459 | public byte SweepStartRandom; 460 | public byte ArcType; 461 | public byte IsVolumeLatitudeEnabled; 462 | public byte VolumeTblIndex; 463 | public byte VolumeTblIndex64; 464 | public byte VolumeLatitudeDir; 465 | public byte IsGpuEmitter; 466 | public float SweepLongitude; 467 | public float SweepLatitude; 468 | public float SweepStart; 469 | public float VolumeSurfacePosRand; 470 | public float CaliberRatio; 471 | public float LineCenter; 472 | public float LineLength; 473 | public float VolumeRadiusX; 474 | public float VolumeRadiusY; 475 | public float VolumeRadiusZ; 476 | public float VolumeFormScaleX; 477 | public float VolumeFormScaleY; 478 | public float VolumeFormScaleZ; 479 | public int PrimEmitType; 480 | public ulong PrimitiveIndex; 481 | public int NumDivideCircle; 482 | public int NumDivideCircleRandom; 483 | public int NumDivideLine; 484 | public int NumDivideLineRandom; 485 | 486 | [VersionCheck(VersionCompare.Less, 40)] 487 | public byte IsOnAnotherBinaryVolumePrimitive; 488 | [VersionCheck(VersionCompare.Less, 40)] 489 | public byte padding1; 490 | [VersionCheck(VersionCompare.Less, 40)] 491 | public byte padding2; 492 | [VersionCheck(VersionCompare.Less, 40)] 493 | public byte padding3; 494 | [VersionCheck(VersionCompare.Less, 40)] 495 | public uint padding4; 496 | } 497 | 498 | public class EmitterRenderState 499 | { 500 | public bool IsBlendEnable; 501 | public bool IsDepthTest; 502 | public byte DepthFunc; 503 | public bool IsDepthMask; 504 | 505 | public bool IsAlphaTest; 506 | public byte AlphaFunc; 507 | public byte BlendType; 508 | public byte DisplaySide; 509 | 510 | public float AlphaThreshold; 511 | public uint padding; 512 | } 513 | 514 | [StructLayout(LayoutKind.Sequential, Size = 0x10)] 515 | public class ParticleData 516 | { 517 | public bool InfiniteLife; //Always display 518 | public bool IsTriming; 519 | public byte BillboardType; 520 | public byte RotType; 521 | public byte OffsetType; 522 | public bool RotRevRandX; 523 | public bool RotRevRandY; 524 | public bool RotRevRandZ; 525 | public bool IsRotateX; 526 | public bool IsRotateY; 527 | public byte IsRotateZ; 528 | public byte PrimitiveScaleType; 529 | public byte IsTextureCommonRandom; 530 | public byte ConnectPtclScaleAndZOffset; 531 | public byte EnableAvoidZFighting; 532 | public byte val_0xF; 533 | public int Life; 534 | public int LifeRandom; 535 | public float MomentumRandom; 536 | public uint PrimitiveVertexInfoFlags; 537 | public ulong PrimitiveID; 538 | public ulong PrimitiveExID; 539 | public bool LoopColor0; 540 | public bool LoopAlpha0; 541 | public bool LoopColor1; 542 | public bool LoopAlpha1; 543 | public bool ScaleLoop; 544 | public bool LoopRandomColor0; 545 | public bool LoopRandomAlpha0; 546 | public bool LoopRandomColor1; 547 | public bool LoopRandomAlpha1; 548 | public bool ScaleLoopRandom; 549 | public byte PrimFlag1; 550 | public byte PrimFlag2; 551 | 552 | [VersionCheck(VersionCompare.Less, 50)] 553 | public int Color0LoopRate; 554 | [VersionCheck(VersionCompare.Less, 50)] 555 | public int Alpha0LoopRate; 556 | [VersionCheck(VersionCompare.Less, 50)] 557 | public int Color1LoopRate; 558 | [VersionCheck(VersionCompare.Less, 50)] 559 | public int Alpha1LoopRate; 560 | [VersionCheck(VersionCompare.Less, 50)] 561 | public int ScaleLoopRate; 562 | 563 | [VersionCheck(VersionCompare.GreaterOrEqual, 50)] 564 | public short Color0LoopRate16; 565 | [VersionCheck(VersionCompare.GreaterOrEqual, 50)] 566 | public short Alpha0LoopRate16; 567 | [VersionCheck(VersionCompare.GreaterOrEqual, 50)] 568 | public short Color1LoopRate16; 569 | [VersionCheck(VersionCompare.GreaterOrEqual, 50)] 570 | public short Alpha1LoopRate16; 571 | [VersionCheck(VersionCompare.GreaterOrEqual, 50)] 572 | public short ScaleLoopRate16; 573 | 574 | } 575 | 576 | public class EmitterCombinerV36 577 | { 578 | public byte ColorCombinerProcess; 579 | public byte AlphaCombinerProcess; 580 | public byte Texture1ColorBlend; 581 | public byte Texture2ColorBlend; 582 | 583 | public byte PrimitiveColorBlend; 584 | public byte Texture1AlphaBlend; 585 | public byte Texture2AlphaBlend; 586 | public byte PrimitiveAlphaBlend; 587 | } 588 | 589 | public class EmitterCombinerV40 590 | { 591 | public byte ColorCombinerProcess; 592 | public byte AlphaCombinerProcess; 593 | public byte Texture1ColorBlend; 594 | public byte Texture2ColorBlend; 595 | 596 | public byte PrimitiveColorBlend; 597 | public byte Texture1AlphaBlend; 598 | public byte Texture2AlphaBlend; 599 | public byte PrimitiveAlphaBlend; 600 | 601 | public byte TexColor0InputType; 602 | public byte TexColor1InputType; 603 | public byte TexColor2InputType; 604 | public byte TexAlpha0InputType; 605 | 606 | public byte TexAlpha1InputType; 607 | public byte TexAlpha2InputType; 608 | public byte PrimitiveColorInputType; 609 | public byte PrimitiveAlphaInputType; 610 | 611 | //Likely combiner color/alpha modes for added textures 3, 4, 5 612 | [VersionCheck(VersionCompare.GreaterOrEqual, 50)] 613 | public short Padding; 614 | 615 | [VersionCheck(VersionCompare.GreaterOrEqual, 50)] 616 | public uint Padding2; 617 | 618 | [VersionCheck(VersionCompare.GreaterOrEqual, 50)] 619 | public uint Padding3; 620 | } 621 | 622 | public class EmitterCombiner 623 | { 624 | public byte ColorCombinerProcess; 625 | public byte AlphaCombinerProcess; 626 | public byte Texture1ColorBlend; 627 | public byte Texture2ColorBlend; 628 | 629 | public byte PrimitiveColorBlend; 630 | public byte Texture1AlphaBlend; 631 | public byte Texture2AlphaBlend; 632 | public byte PrimitiveAlphaBlend; 633 | 634 | public byte TexColor0InputType; 635 | public byte TexColor1InputType; 636 | public byte TexColor2InputType; 637 | public byte TexAlpha0InputType; 638 | 639 | public byte TexAlpha1InputType; 640 | public byte TexAlpha2InputType; 641 | public byte PrimitiveColorInputType; 642 | public byte PrimitiveAlphaInputType; 643 | 644 | public byte ShaderType; 645 | public byte ApplyAlpha; 646 | public byte IsDistortionByCameraDistance; 647 | public byte padding1; 648 | 649 | public uint padding2; 650 | } 651 | 652 | public class ShaderRefInfo 653 | { 654 | public byte Type; 655 | public byte val_0x2; 656 | public byte val_0x3; 657 | public byte val_0x4; 658 | 659 | 660 | public int ShaderIndex; 661 | public int ComputeShaderIndex; 662 | public int UserShaderIndex1; 663 | public int UserShaderIndex2; 664 | public int CustomShaderIndex; 665 | 666 | [VersionCheck(VersionCompare.Less, 50)] 667 | public ulong CustomShaderFlag; 668 | 669 | [VersionCheck(VersionCompare.Less, 50)] 670 | public ulong CustomShaderSwitch; 671 | 672 | [VersionCheck(VersionCompare.Less, 22)] 673 | public ulong Unknown1; 674 | 675 | public int ExtraShaderIndex2; 676 | 677 | public int val_0x34; 678 | 679 | [VersionCheck(VersionCompare.Greater, 50)] 680 | public ulong Unknown2; 681 | 682 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 683 | public byte[] UserShaderDefine1 = new byte[16]; 684 | 685 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] 686 | public byte[] UserShaderDefine2 = new byte[16]; 687 | 688 | } 689 | 690 | public class ActionInfo 691 | { 692 | public uint ActionIndex; 693 | 694 | [VersionCheck(VersionCompare.Greater, 40)] 695 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] 696 | public uint[] Unknown; //maybe in action section 697 | 698 | } 699 | 700 | public class ParticleVelocityInfo 701 | { 702 | public float AllDirection; 703 | public float DesignatedDirScale; 704 | public float DesignatedDirX; 705 | public float DesignatedDirY; 706 | public float DesignatedDirZ; 707 | public float DiffusionDirAngle; 708 | public float XZDiffusion; 709 | public float DiffusionX; 710 | public float DiffusionY; 711 | public float DiffusionZ; 712 | 713 | public float VelRandom; 714 | public float EmVelInherit; 715 | } 716 | 717 | [StructLayout(LayoutKind.Sequential, Size = 0x10)] 718 | public class ParticleColor 719 | { 720 | public byte IsSoftParticle; 721 | public byte IsFresnelAlpha; 722 | public byte IsNearDistAlpha; 723 | public byte IsFarDistAlpha; 724 | public byte IsDecal; 725 | public byte val_0x5; 726 | public byte val_0x6; 727 | public byte val_0x7; 728 | 729 | public ColorType Color0Type; 730 | public ColorType Color1Type; 731 | public ColorType Alpha0Type; 732 | public ColorType Alpha1Type; 733 | 734 | public float Color0R; 735 | public float Color0G; 736 | public float Color0B; 737 | public float Alpha0; 738 | public float Color1R; 739 | public float Color1G; 740 | public float Color1B; 741 | public float Alpha1; 742 | } 743 | 744 | [StructLayout(LayoutKind.Sequential, Size = 0x10)] 745 | public class ParticleScale 746 | { 747 | public float ScaleX; 748 | public float ScaleY; 749 | public float ScaleZ; 750 | public float ScaleRandomX; 751 | public float ScaleRandomY; 752 | public float ScaleRandomZ; 753 | 754 | public byte EnableScalingByCameraDistNear; 755 | public byte EnableScalingByCameraDistFar; 756 | public byte EnableAddScaleY; 757 | public byte EnableLinkFovyToScaleValue; 758 | 759 | public float ScaleMin; 760 | public float ScaleMax; 761 | } 762 | 763 | [StructLayout(LayoutKind.Sequential, Size = 0x10)] 764 | public class ParticleFlucInfo 765 | { 766 | public byte IsApplyAlpha; 767 | public byte IsApplayScale; 768 | public byte IsApplayScaleY; 769 | public byte IsWaveType; 770 | 771 | public byte IsPhaseRandomX; 772 | public byte IsPhaseRandomY; 773 | public byte padding1; 774 | public byte padding2; 775 | 776 | public uint padding3; 777 | } 778 | 779 | 780 | public class TextureSampler 781 | { 782 | public ulong TextureID; 783 | 784 | public WrapMode WrapU = WrapMode.Mirror; 785 | public WrapMode WrapV = WrapMode.Mirror; 786 | public byte Filter = 0; 787 | public byte IsSphereMap; 788 | 789 | public float MaxLOD = 15.0f; 790 | public float LODBias = 0.0f; 791 | 792 | public byte MipLevelLimit; 793 | public byte IsDensityFixedU; 794 | public byte IsDensityFixedV; 795 | public byte IsSquareRgb; 796 | 797 | [VersionCheck(VersionCompare.Less, 50)] 798 | public byte IsOnAnotherBinary; 799 | [VersionCheck(VersionCompare.Less, 50)] 800 | public byte padding1; 801 | [VersionCheck(VersionCompare.Less, 50)] 802 | public byte padding2; 803 | [VersionCheck(VersionCompare.Less, 50)] 804 | public byte padding3; 805 | 806 | [VersionCheck(VersionCompare.Less, 50)] 807 | public uint padding4; 808 | } 809 | 810 | public class TextureAnim 811 | { 812 | public byte PatternAnimType; 813 | public bool IsScroll; 814 | public bool IsRotate; 815 | public bool IsScale; 816 | 817 | public byte Repeat; 818 | public byte InvRandU; 819 | public byte InvRandV; 820 | public byte IsPatAnimLoopRandom; 821 | 822 | public byte UvChannel; 823 | public byte IsCrossfade; 824 | public byte padding1; 825 | public byte padding2; 826 | 827 | public uint padding3; 828 | } 829 | } 830 | -------------------------------------------------------------------------------- /EffectLibrary/FileData/EFT2/EmitterStructs/EmitterAnimation.cs: -------------------------------------------------------------------------------- 1 | using EffectLibrary.EFT2; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace EffectLibrary.EFT2 10 | { 11 | public class EmitterAnimation : EmitterSubSection 12 | { 13 | public bool Enable; // Toggles usage 14 | public bool Loop; 15 | public bool RandomizeStartFrame; // Randomizes starting frame 16 | public byte Reserved; 17 | 18 | public uint LoopCount; // Amount of times to keep looping. 19 | 20 | public List KeyFrames = new List(); 21 | 22 | public EmitterAnimation() { } 23 | 24 | public EmitterAnimation(string magic) { this.Header.Magic = magic; } 25 | 26 | public override bool ReadBinary(BinaryReader reader, PtclFile ptclFile) 27 | { 28 | Enable = reader.ReadBoolean(); 29 | Loop = reader.ReadBoolean(); 30 | RandomizeStartFrame = reader.ReadBoolean(); 31 | Reserved = reader.ReadByte(); 32 | uint numKeys = reader.ReadUInt32(); 33 | LoopCount = reader.ReadUInt32(); 34 | for (int i = 0; i < numKeys; i++) 35 | KeyFrames.Add(new KeyFrame() 36 | { 37 | X = reader.ReadSingle(), 38 | Y = reader.ReadSingle(), 39 | Z = reader.ReadSingle(), 40 | Time = reader.ReadSingle(), 41 | }); 42 | return true; 43 | } 44 | 45 | public override bool WriteBinary(BinaryWriter writer, PtclFile ptclFile) 46 | { 47 | // Check if raw data is loaded 48 | // This is so .bin emitters from older builds can write back 49 | if (this.Data?.Length > 0) 50 | return false; 51 | 52 | writer.Write(Enable); 53 | writer.Write(Loop); 54 | writer.Write(RandomizeStartFrame); 55 | writer.Write(Reserved); 56 | writer.Write(KeyFrames.Count); 57 | writer.Write(LoopCount); 58 | for (int i = 0; i < KeyFrames.Count; i++) 59 | { 60 | writer.Write(KeyFrames[i].X); 61 | writer.Write(KeyFrames[i].Y); 62 | writer.Write(KeyFrames[i].Z); 63 | writer.Write(KeyFrames[i].Time); 64 | } 65 | return true; 66 | } 67 | 68 | public override void Import(string filePath) 69 | { 70 | if (filePath.EndsWith(".bin")) 71 | base.Import(filePath); 72 | else if ((filePath.EndsWith(".json"))) 73 | { 74 | var anim = JsonConvert.DeserializeObject(File.ReadAllText(filePath)); 75 | this.Loop = anim.Loop; 76 | this.Enable = anim.Enable; 77 | this.RandomizeStartFrame = anim.RandomizeStartFrame; 78 | this.Reserved = anim.Reserved; 79 | this.LoopCount = anim.LoopCount; 80 | this.KeyFrames = anim.KeyFrames; } 81 | else 82 | throw new Exception($"Unknown file format given! {Path.GetExtension(filePath)}"); 83 | } 84 | 85 | public override void Export(string filePath) 86 | { 87 | filePath = filePath.Replace(".bin", ".json"); 88 | 89 | File.WriteAllText(filePath, JsonConvert.SerializeObject(this, Formatting.Indented)); 90 | } 91 | 92 | public class KeyFrame 93 | { 94 | public float X; 95 | public float Y; 96 | public float Z; 97 | public float Time; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /EffectLibrary/FileData/EFT2/Primitives.cs: -------------------------------------------------------------------------------- 1 | using BfresLibrary; 2 | using Newtonsoft.Json.Linq; 3 | using Syroot.NintenTools.NSW.Bntx; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Numerics; 8 | using System.Reflection; 9 | using System.Reflection.PortableExecutable; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace EffectLibrary.EFT2 14 | { 15 | public class PrimitiveList : SectionBase 16 | { 17 | public override string Magic => "PRMA"; 18 | 19 | public List Primtitives = new List(); 20 | 21 | public override void Read(BinaryReader reader, PtclFile ptclFile) 22 | { 23 | base.Read(reader, ptclFile); 24 | 25 | if (this.Header.ChildrenCount > 0) 26 | { 27 | reader.SeekBegin(StartPosition + this.Header.ChildrenOffset); 28 | for (int i = 0; i < this.Header.ChildrenCount; i++) 29 | { 30 | var prim = new Primtitive(); 31 | prim.Read(reader, ptclFile); 32 | Primtitives.Add(prim); 33 | } 34 | } 35 | } 36 | 37 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 38 | { 39 | this.Header.NextSectionOffset = 32; 40 | this.Header.AttrOffset = uint.MaxValue; 41 | this.Header.BinaryOffset = uint.MaxValue; 42 | this.Header.ChildrenCount = (ushort)Primtitives.Count; 43 | 44 | base.Write(writer, ptclFile); 45 | 46 | long start_pos = writer.BaseStream.Position; 47 | 48 | if (Primtitives.Count > 0) 49 | { 50 | WriteChildOffset(writer); 51 | WriteBinaryOffset(writer); 52 | WriteList(Primtitives, writer, ptclFile); 53 | } 54 | 55 | long end_pos = writer.BaseStream.Position; 56 | 57 | 58 | this.WriteSectionSize(writer, end_pos - start_pos); 59 | this.WriteNextOffset(writer, false); 60 | } 61 | } 62 | 63 | public class Primtitive : SectionBase 64 | { 65 | public override string Magic => "PRIM"; 66 | 67 | public ulong PrimitiveID; 68 | 69 | public Attribute Positions = new Attribute(); 70 | public Attribute Normals = new Attribute(); 71 | public Attribute Tangents = new Attribute(); 72 | public Attribute Colors = new Attribute(); 73 | public Attribute TexCoords0 = new Attribute(); 74 | public Attribute TexCoords1 = new Attribute(); 75 | 76 | public List Indices = new List(); 77 | 78 | private byte[] Raw; 79 | 80 | public override void Read(BinaryReader reader, PtclFile header) 81 | { 82 | base.Read(reader, header); 83 | 84 | SeekFromHeader(reader, this.Header.BinaryOffset); 85 | Raw = reader.ReadBytes((int)Header.Size); 86 | 87 | // ReadBinary(reader); 88 | } 89 | 90 | public override void Write(BinaryWriter writer, PtclFile header) 91 | { 92 | base.Write(writer, header); 93 | 94 | WriteBinaryOffset(writer); 95 | 96 | long pos = writer.BaseStream.Position; 97 | writer.Write(Raw); 98 | // WriterBinary(writer); 99 | 100 | var size = writer.BaseStream.Position - pos; 101 | 102 | this.WriteSectionSize(writer, (int)size); 103 | } 104 | 105 | private void ReadBinary(BinaryReader reader) 106 | { 107 | long pos = reader.BaseStream.Position; 108 | 109 | PrimitiveID = reader.ReadUInt64(); 110 | int num_positions = reader.ReadInt32(); 111 | int num_positions_elements = reader.ReadInt32(); 112 | int num_normals = reader.ReadInt32(); 113 | int num_normals_elements = reader.ReadInt32(); 114 | int num_tangents = reader.ReadInt32(); 115 | int num_tangents_elements = reader.ReadInt32(); 116 | int num_colors = reader.ReadInt32(); 117 | int num_num_colors_elements = reader.ReadInt32(); 118 | int num_texCoords0 = reader.ReadInt32(); 119 | int num_texCoords0_elements = reader.ReadInt32(); 120 | int num_texCoords1 = reader.ReadInt32(); 121 | int num_texCoords1_elements = reader.ReadInt32(); 122 | int num_indices = reader.ReadInt32(); 123 | uint position_offset = reader.ReadUInt32(); 124 | uint normal_offset = reader.ReadUInt32(); 125 | uint tangent_offset = reader.ReadUInt32(); 126 | uint colors_offset = reader.ReadUInt32(); 127 | uint texCoords_offset = reader.ReadUInt32(); 128 | uint index_buffer_offset = reader.ReadUInt32(); 129 | 130 | Attribute ReadAttribute(int count, int num_elements, uint offset) 131 | { 132 | Attribute att = new Attribute(); 133 | att.Buffer = new float[count * 4]; 134 | att.Count = count; 135 | att.ElementCount = num_elements; 136 | 137 | reader.SeekBegin(pos + offset); 138 | for (int i = 0; i < count * 4; i++) 139 | att.Buffer[i] = reader.ReadSingle(); 140 | 141 | return att; 142 | } 143 | 144 | this.Positions = ReadAttribute(num_positions, num_positions_elements, position_offset); 145 | this.Normals = ReadAttribute(num_normals, num_normals_elements, normal_offset); 146 | this.Tangents = ReadAttribute(num_tangents, num_tangents_elements, tangent_offset); 147 | this.Colors = ReadAttribute(num_colors, num_num_colors_elements, colors_offset); 148 | 149 | var texCoord_size = (uint)(num_texCoords0 * 16); 150 | 151 | this.TexCoords0 = ReadAttribute(num_texCoords0, num_texCoords0_elements, texCoords_offset); 152 | this.TexCoords1 = ReadAttribute(num_texCoords1, num_texCoords1_elements, texCoords_offset + texCoord_size); 153 | 154 | reader.SeekBegin(pos + index_buffer_offset); 155 | Indices = reader.ReadInt32s((int)num_indices).ToList(); 156 | } 157 | 158 | private void WriterBinary(BinaryWriter writer) 159 | { 160 | var pos = writer.BaseStream.Position; 161 | 162 | writer.Write(PrimitiveID); 163 | writer.Write(this.Positions.Count); 164 | writer.Write(this.Positions.GetElementCount()); 165 | writer.Write(this.Normals.Count); 166 | writer.Write(this.Normals.GetElementCount()); 167 | writer.Write(this.Tangents.Count); 168 | writer.Write(this.Tangents.GetElementCount()); 169 | writer.Write(this.Colors.Count); 170 | writer.Write(this.Colors.GetElementCount()); 171 | writer.Write(this.TexCoords0.Count); 172 | writer.Write(this.TexCoords0.GetElementCount()); 173 | writer.Write(this.TexCoords1.Count); 174 | writer.Write(this.TexCoords1.GetElementCount()); 175 | writer.Write(this.Indices.Count); 176 | 177 | long ofs_pos = writer.BaseStream.Position; 178 | 179 | writer.Write(0); 180 | writer.Write(0); 181 | writer.Write(0); 182 | writer.Write(0); 183 | writer.Write(0); 184 | writer.Write(0); 185 | 186 | if (this.PtclHeader.Header.VFXVersion >= 21) 187 | writer.Write(0); 188 | 189 | void WriteAttribute(Attribute attr, int index) 190 | { 191 | if (attr.Buffer.Length == 0) 192 | return; 193 | 194 | writer.AlignBytes(0x40, 0xCC); 195 | writer.WriteOffset(ofs_pos + index * sizeof(uint), pos); 196 | writer.Write(attr.Buffer); 197 | } 198 | 199 | WriteAttribute(this.Positions, 0); 200 | WriteAttribute(this.Normals, 1); 201 | WriteAttribute(this.Tangents, 2); 202 | WriteAttribute(this.Colors, 3); 203 | WriteAttribute(this.TexCoords0, 4); 204 | //data directly after 205 | writer.Write(this.TexCoords1.Buffer); 206 | 207 | //indices 208 | writer.AlignBytes(0x40, 0xCC); 209 | writer.WriteOffset(ofs_pos + 5 * sizeof(uint), pos); 210 | writer.Write(this.Indices.ToArray()); 211 | } 212 | 213 | public class Attribute 214 | { 215 | public int Count; 216 | public int ElementCount; 217 | public float[] Buffer = new float[0]; 218 | 219 | public int GetElementCount() => ElementCount; 220 | } 221 | } 222 | 223 | public class PrimitiveInfo : SectionBase 224 | { 225 | public override string Magic => "G3PR"; 226 | 227 | public PrimitiveDescTable PrimDescTable = new PrimitiveDescTable(); 228 | 229 | public ResFile ResFile; 230 | 231 | public byte[] BinaryData; 232 | 233 | public override void Read(BinaryReader reader, PtclFile ptclFile) 234 | { 235 | base.Read(reader, ptclFile); 236 | 237 | if (this.Header.ChildrenCount != 1) 238 | throw new Exception(); 239 | 240 | //Descriptor 241 | SeekFromHeader(reader, this.Header.ChildrenOffset); 242 | PrimDescTable = new PrimitiveDescTable(); 243 | PrimDescTable.Read(reader, ptclFile); 244 | 245 | //section contains BFRES 246 | if (this.Header.BinaryOffset != uint.MaxValue) 247 | { 248 | SeekFromHeader(reader, this.Header.BinaryOffset); 249 | LoadBinary(reader); 250 | } 251 | } 252 | 253 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 254 | { 255 | // SaveBinary(); 256 | 257 | this.Header.Size = (uint)BinaryData.Length; 258 | 259 | //Descriptor 260 | this.Header.ChildrenOffset = 32; //descriptors as children 261 | this.Header.ChildrenCount = 1; 262 | 263 | base.Write(writer, ptclFile); 264 | 265 | WriteChildOffset(writer); 266 | PrimDescTable.Write(writer, ptclFile); 267 | 268 | 269 | if (BinaryData.Length > 0) 270 | { 271 | //binary data next with alignment 272 | writer.AlignBytes(4096); 273 | WriteBinaryOffset(writer); 274 | writer.Write(BinaryData); 275 | } 276 | 277 | this.WriteNextOffset(writer, false); 278 | } 279 | 280 | public void LoadBinary(BinaryReader reader) 281 | { 282 | BinaryData = reader.ReadBytes((int)this.Header.Size); 283 | ResFile = new ResFile(new MemoryStream(BinaryData)); 284 | } 285 | 286 | public void SaveBinary() 287 | { 288 | var mem = new MemoryStream(); 289 | ResFile.Save(mem); 290 | BinaryData = mem.ToArray(); 291 | } 292 | 293 | public byte[] GetBinaryData() 294 | { 295 | if (ResFile == null) return new byte[0]; 296 | 297 | var mem = new MemoryStream(); 298 | ResFile.Save(mem); 299 | return mem.ToArray(); 300 | } 301 | 302 | public void AddPrimitive(ulong id, Model model) 303 | { 304 | //model is empty, skip 305 | var descPrimIndex = PrimDescTable.Descriptors.FindIndex(x => x.ID == id); 306 | //ID already added, skip 307 | if (descPrimIndex != -1) 308 | return; 309 | 310 | //compute indices 311 | sbyte pos_idx = -1; 312 | sbyte nrm_idx = -1; 313 | sbyte tan_idx = -1; 314 | sbyte col_idx = -1; 315 | sbyte uv0_idx = -1; 316 | sbyte uv1_idx = -1; 317 | 318 | if (model.Shapes.Count > 0) 319 | { 320 | //Note all shapes seem to use the same attributes 321 | var attr = model.VertexBuffers[0].Attributes; 322 | 323 | if (attr.ContainsKey("_p0")) pos_idx = (sbyte)attr.IndexOf("_p0"); 324 | if (attr.ContainsKey("_n0")) nrm_idx = (sbyte)attr.IndexOf("_n0"); 325 | if (attr.ContainsKey("_t0")) tan_idx = (sbyte)attr.IndexOf("_t0"); 326 | if (attr.ContainsKey("_c0")) col_idx = (sbyte)attr.IndexOf("_c0"); 327 | if (attr.ContainsKey("_u0")) uv0_idx = (sbyte)attr.IndexOf("_u0"); 328 | if (attr.ContainsKey("_u1")) uv1_idx = (sbyte)attr.IndexOf("_u1"); 329 | } 330 | 331 | PrimDescTable.Descriptors.Add(new PrimitiveDescTable.Descriptor() 332 | { 333 | ID = id, 334 | PositionIndex = pos_idx, 335 | NormalIndex = nrm_idx, 336 | TangentIndex = tan_idx, 337 | ColorIndex = col_idx, 338 | TexCoord0Index = uv0_idx, 339 | TexCoord1Index = uv1_idx, 340 | }); 341 | 342 | if (!ResFile.Models.ContainsKey(model.Name)) 343 | ResFile.Models.Add(model.Name, model); 344 | } 345 | 346 | public Model TryGetModel(ulong id) 347 | { 348 | if (PrimDescTable == null) 349 | throw new Exception("Failed to find primitive descriptor table!"); 350 | 351 | var descPrimIndex = PrimDescTable.Descriptors.FindIndex(x => x.ID == id); 352 | if (descPrimIndex != -1) 353 | return ResFile.Models[descPrimIndex]; 354 | 355 | return null; 356 | } 357 | } 358 | 359 | public class PrimitiveDescTable : SectionBase 360 | { 361 | public List Descriptors = new List(); 362 | 363 | public override void Read(BinaryReader reader, PtclFile ptclFile) 364 | { 365 | base.Read(reader, ptclFile); 366 | 367 | reader.SeekBegin(StartPosition + this.Header.BinaryOffset); 368 | ReadBinary(reader); 369 | } 370 | 371 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 372 | { 373 | var binaryData = WriteBinary().ToArray(); 374 | 375 | this.Header.Size = (uint)binaryData.Length; 376 | this.Header.NextSectionOffset = (uint)binaryData.Length + 48; 377 | 378 | base.Write(writer, ptclFile); 379 | 380 | WriteBinaryOffset(writer); 381 | writer.Write(binaryData); 382 | 383 | writer.AlignBytes(16); 384 | this.WriteNextOffset(writer, false); 385 | } 386 | 387 | private void ReadBinary(BinaryReader reader) 388 | { 389 | var end_pos = StartPosition + this.Header.BinaryOffset + this.Header.Size; 390 | while (end_pos > reader.BaseStream.Position) 391 | { 392 | long pos = reader.BaseStream.Position; 393 | 394 | ulong id = reader.ReadUInt64(); 395 | uint next_offset = reader.ReadUInt32(); //to next descriptor 396 | reader.ReadUInt32(); //always 8 397 | var indices = reader.ReadSbytes(6); 398 | ushort padding = reader.ReadUInt16(); 399 | 400 | Descriptors.Add(new Descriptor() 401 | { 402 | PositionIndex = indices[0], 403 | NormalIndex = indices[1], 404 | TangentIndex = indices[2], 405 | ColorIndex = indices[3], 406 | TexCoord0Index = indices[4], 407 | TexCoord1Index = indices[5], 408 | Padding = padding, 409 | ID = id, 410 | }); 411 | 412 | if (next_offset == 0) 413 | break; 414 | 415 | reader.SeekBegin(pos + next_offset); 416 | } 417 | } 418 | 419 | private MemoryStream WriteBinary() 420 | { 421 | var mem = new MemoryStream(); 422 | using (var writer = new BinaryWriter(mem)) 423 | { 424 | for (int i = 0; i < Descriptors.Count; i++) 425 | { 426 | writer.Write(Descriptors[i].ID); 427 | writer.Write(i == Descriptors.Count - 1 ? 0 : 24); // next offset 428 | writer.Write(8); // always 8? 429 | writer.Write(Descriptors[i].PositionIndex); 430 | writer.Write(Descriptors[i].NormalIndex); 431 | writer.Write(Descriptors[i].TangentIndex); 432 | writer.Write(Descriptors[i].ColorIndex); 433 | writer.Write(Descriptors[i].TexCoord0Index); 434 | writer.Write(Descriptors[i].TexCoord1Index); 435 | writer.Write(Descriptors[i].Padding); 436 | } 437 | } 438 | return mem; 439 | } 440 | 441 | public class Descriptor 442 | { 443 | public ulong ID; 444 | public sbyte PositionIndex; 445 | public sbyte NormalIndex; 446 | public sbyte TangentIndex; 447 | public sbyte ColorIndex; 448 | public sbyte TexCoord0Index; 449 | public sbyte TexCoord1Index; 450 | public ushort Padding; 451 | } 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /EffectLibrary/FileData/EFT2/PtclFile.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Globalization; 3 | using System.IO; 4 | using System.Reflection.PortableExecutable; 5 | using System.Text; 6 | using System.Xml.Linq; 7 | 8 | namespace EffectLibrary.EFT2 9 | { 10 | public class PtclFile 11 | { 12 | public BinaryHeader Header; 13 | 14 | public string Name; 15 | 16 | public EmitterList EmitterList = new EmitterList(); 17 | public TextureInfo Textures = new TextureInfo(); 18 | public ShaderInfo Shaders = new ShaderInfo(); 19 | public PrimitiveInfo Primitives = new PrimitiveInfo(); 20 | public PrimitiveList PrimitiveList = new PrimitiveList(); 21 | public SectionDefault TRMA = new SectionDefault(); 22 | 23 | //EFTB 24 | public TextureArrayGX2 TexturesGX2 = new TextureArrayGX2(); 25 | 26 | public PtclFile() 27 | { 28 | Header = new BinaryHeader(); 29 | } 30 | 31 | public PtclFile(string filePath) 32 | { 33 | Read(File.OpenRead(filePath)); 34 | } 35 | 36 | public PtclFile(Stream stream) 37 | { 38 | Read(stream); 39 | } 40 | 41 | public void Save(string filePath) 42 | { 43 | using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) 44 | { 45 | Write(fs); 46 | } 47 | } 48 | 49 | public void Save(Stream stream) 50 | { 51 | Write(stream); 52 | } 53 | 54 | private void Read(Stream stream) 55 | { 56 | var reader = stream.AsBinaryReader(); 57 | stream.Read(Utils.AsSpan(ref Header)); 58 | Name = reader.ReadFixedString(32); 59 | 60 | reader.SeekBegin(Header.BlockOffset); 61 | 62 | //Go through all sections 63 | while (reader.BaseStream.Position < Header.FileSize) 64 | { 65 | long pos = reader.BaseStream.Position; 66 | 67 | ReadSection(reader); 68 | 69 | reader.SeekBegin(pos + 12); 70 | uint nextSectionOffset = reader.ReadUInt32(); 71 | 72 | if (nextSectionOffset != uint.MaxValue) 73 | reader.SeekBegin(pos + nextSectionOffset); 74 | else 75 | break; 76 | } 77 | } 78 | 79 | private void Write(Stream stream) 80 | { 81 | var writer = stream.AsBinaryWriter(); 82 | stream.Write(Utils.AsSpan(ref Header)); 83 | 84 | //Name (32 bytes total, used in BOTW/TOTK) 85 | writer.WriteZeroTerminatedString(Name); 86 | 87 | writer.Seek(64, SeekOrigin.Begin); 88 | 89 | //write each section 90 | EmitterList?.Write(writer, this); 91 | Textures?.Write(writer, this); 92 | PrimitiveList?.Write(writer, this); 93 | // TRMA?.Write(writer, this); 94 | Primitives?.Write(writer, this); 95 | Shaders?.Write(writer, this); 96 | 97 | //set file size 98 | using (writer.BaseStream.TemporarySeek(28, SeekOrigin.Begin)) 99 | { 100 | writer.Write((uint)writer.BaseStream.Length); 101 | } 102 | } 103 | 104 | private SectionBase ReadSection(BinaryReader reader) 105 | { 106 | long pos = reader.BaseStream.Position; 107 | 108 | string magic = Encoding.ASCII.GetString(reader.ReadBytes(4)); 109 | reader.BaseStream.Seek(-4, SeekOrigin.Current); 110 | 111 | if (!SectionTypes.ContainsKey(magic)) 112 | throw new Exception($"Unknown section {magic}"); 113 | 114 | var section = (SectionBase)Activator.CreateInstance(SectionTypes[magic]); 115 | section.Read(reader, this); 116 | 117 | //Apply each section 118 | switch (magic) 119 | { 120 | case "ESTA": EmitterList = (EmitterList)section; break; 121 | case "GRTF": Textures = (TextureInfo)section; break; 122 | case "PRMA": PrimitiveList = (PrimitiveList)section; break; 123 | case "G3PR": Primitives = (PrimitiveInfo)section; break; 124 | case "GRSN": Shaders = (ShaderInfo)section; break; 125 | case "TRMA": TRMA = (SectionDefault)section; break; 126 | case "EFTB": TexturesGX2 = (TextureArrayGX2)section; break; 127 | default: 128 | throw new Exception($"Section {magic} not supported!"); 129 | } 130 | 131 | //go to next section 132 | if (section.Header.NextSectionOffset != uint.MaxValue) 133 | reader.SeekBegin(pos + section.Header.NextSectionOffset); 134 | 135 | return section; 136 | } 137 | 138 | static Dictionary SectionTypes = new Dictionary() 139 | { 140 | { "ESTA", typeof(EmitterList) }, 141 | { "ESET", typeof(EmitterSet) }, 142 | { "EMTR", typeof(Emitter) }, 143 | 144 | { "GRTF", typeof(TextureInfo) }, 145 | { "GTNT", typeof(TextureDescTable) }, 146 | 147 | { "TEXA", typeof(TextureArrayGX2) }, //Used by EFTB 148 | 149 | { "PRMA", typeof(PrimitiveList) }, 150 | 151 | { "G3PR", typeof(PrimitiveInfo) }, 152 | { "G3NT", typeof(PrimitiveDescTable) }, 153 | 154 | { "GRSN", typeof(ShaderInfo) }, 155 | { "GRSC", typeof(ComputeShader) }, 156 | 157 | { "TRMA", typeof(SectionDefault) } 158 | }; 159 | } 160 | 161 | public class SectionBase 162 | { 163 | [JsonIgnore] 164 | public virtual string Magic { get; } 165 | 166 | internal long StartPosition; 167 | internal SectionHeader Header = new SectionHeader(); 168 | internal PtclFile PtclHeader; 169 | 170 | public SectionBase() 171 | { 172 | this.Header.NextSectionOffset = uint.MaxValue; 173 | this.Header.AttrOffset = uint.MaxValue; 174 | this.Header.BinaryOffset = uint.MaxValue; 175 | this.Header.ChildrenOffset = uint.MaxValue; 176 | } 177 | 178 | public virtual void Read(BinaryReader reader, PtclFile ptclFile) 179 | { 180 | StartPosition = reader.BaseStream.Position; 181 | 182 | PtclHeader = ptclFile; 183 | Header = new SectionHeader(); 184 | reader.BaseStream.Read(Utils.AsSpan(ref Header)); 185 | } 186 | 187 | public virtual void Write(BinaryWriter writer, PtclFile ptclFile) 188 | { 189 | this.PtclHeader = ptclFile; 190 | 191 | Header.ChildrenOffset = uint.MaxValue; 192 | Header.NextSectionOffset = uint.MaxValue; 193 | 194 | StartPosition = writer.BaseStream.Position; 195 | writer.BaseStream.Write(Utils.AsSpan(ref Header)); 196 | } 197 | 198 | public void SeekFromHeader(BinaryReader reader, long pos) 199 | { 200 | reader.SeekBegin(StartPosition + pos); 201 | } 202 | 203 | public void WriteNextOffset(BinaryWriter writer, bool isLastElement) 204 | { 205 | writer.AlignBytes(4); 206 | 207 | var offset = writer.BaseStream.Position - StartPosition; 208 | using (writer.BaseStream.TemporarySeek(StartPosition + 12, SeekOrigin.Begin)) 209 | { 210 | if (isLastElement) 211 | writer.Write(uint.MaxValue); 212 | else 213 | writer.Write((uint)offset); 214 | } 215 | } 216 | 217 | public static void WriteList(IEnumerable sections, BinaryWriter writer, PtclFile header) 218 | { 219 | var list = sections.ToList(); 220 | for (int i = 0; i < list.Count; i++) 221 | { 222 | list[i].Write(writer, header); 223 | list[i].WriteNextOffset(writer, i == list.Count - 1); 224 | } 225 | } 226 | 227 | public void WriteChildOffset(BinaryWriter writer) { 228 | writer.WriteOffset(StartPosition + 8, StartPosition); 229 | } 230 | 231 | public void WriteBinaryOffset(BinaryWriter writer) { 232 | writer.WriteOffset(StartPosition + 20, StartPosition); 233 | } 234 | 235 | public void WriteSubSectionOffset(BinaryWriter writer) { 236 | writer.WriteOffset(StartPosition + 16, StartPosition); 237 | } 238 | 239 | public void WriteSectionSize(BinaryWriter writer) 240 | { 241 | var size = writer.BaseStream.Position - StartPosition; 242 | using (writer.BaseStream.TemporarySeek(StartPosition + 4, SeekOrigin.Begin)) 243 | { 244 | writer.Write((uint)size); 245 | } 246 | } 247 | 248 | public void WriteSectionSize(BinaryWriter writer, long size) 249 | { 250 | using (writer.BaseStream.TemporarySeek(StartPosition + 4, SeekOrigin.Begin)) 251 | { 252 | writer.Write((uint)size); 253 | } 254 | } 255 | } 256 | 257 | //Default instance to use when a section is unsupported 258 | public class SectionDefault : SectionBase 259 | { 260 | public byte[] Data; 261 | 262 | public SectionDefault() { } 263 | 264 | public SectionDefault(string magic) 265 | { 266 | this.Header.Magic = magic; 267 | } 268 | 269 | public override void Read(BinaryReader reader, PtclFile ptclFile) 270 | { 271 | base.Read(reader, ptclFile); 272 | 273 | if (this.Header.BinaryOffset != uint.MaxValue) 274 | { 275 | reader.SeekBegin(this.StartPosition + this.Header.BinaryOffset); 276 | //read entire section 277 | Data = reader.ReadBytes((int)(this.Header.Size)); 278 | } 279 | 280 | //goto next section 281 | if (Header.NextSectionOffset != uint.MaxValue) 282 | reader.SeekBegin(StartPosition + Header.NextSectionOffset); 283 | } 284 | 285 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 286 | { 287 | base.Write(writer, ptclFile); 288 | 289 | if (this.Data?.Length > 0) 290 | { 291 | this.Header.Size = (uint)Data.Length; 292 | WriteBinaryOffset(writer); 293 | writer.Write(Data); 294 | } 295 | } 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /EffectLibrary/FileData/EFT2/Shaders.cs: -------------------------------------------------------------------------------- 1 | using ShaderLibrary; 2 | using System.Reflection.PortableExecutable; 3 | using System.Text; 4 | 5 | namespace EffectLibrary.EFT2 6 | { 7 | public class ShaderInfo : SectionBase 8 | { 9 | public override string Magic => "GRSN"; 10 | 11 | public ComputeShader ComputeShader = new ComputeShader(); 12 | 13 | public byte[] BinaryData; 14 | 15 | public BnshFile BnshFile = new BnshFile(); 16 | public BfshaFile BfshaFile = new BfshaFile(); 17 | 18 | public BnshFile.ShaderVariation TryGetShader(int index) 19 | { 20 | if (BnshFile.Variations.Count > index && index != -1) 21 | return BnshFile.Variations[index]; 22 | 23 | return null; 24 | } 25 | 26 | public BnshFile.ShaderVariation TryGetComputeShader(int index) { 27 | return ComputeShader.TryGetShader(index); 28 | } 29 | 30 | public override void Read(BinaryReader reader, PtclFile ptclFile) 31 | { 32 | base.Read(reader, ptclFile); 33 | 34 | //section contains BNSH 35 | if (this.Header.Size > 0) 36 | { 37 | //quick peek at header magic 38 | SeekFromHeader(reader, this.Header.BinaryOffset); 39 | string magic = Encoding.ASCII.GetString(reader.ReadBytes(4)); 40 | 41 | SeekFromHeader(reader, this.Header.BinaryOffset); 42 | BinaryData = reader.ReadBytes((int)this.Header.Size); 43 | 44 | switch (magic) 45 | { 46 | case "BNSH": 47 | BnshFile = new BnshFile(new MemoryStream(BinaryData)); 48 | break; 49 | case "FSHA": 50 | BfshaFile = new BfshaFile(new MemoryStream(BinaryData)); 51 | BnshFile = BfshaFile.ShaderModels[0].BnshFile; 52 | break; 53 | default: 54 | throw new Exception($"Unsupported shader format with magic {magic}!"); 55 | } 56 | } 57 | 58 | //compute shader 59 | if (this.Header.ChildrenOffset != uint.MaxValue) 60 | { 61 | SeekFromHeader(reader, this.Header.ChildrenOffset); 62 | ComputeShader.Read(reader, ptclFile); 63 | } 64 | } 65 | 66 | public void SaveBinary() 67 | { 68 | if (BfshaFile != null) //uses bfsha (custom shaders) 69 | { 70 | var mem = new MemoryStream(); 71 | BnshFile.Save(mem); 72 | BinaryData = mem.ToArray(); 73 | 74 | } 75 | else if (BnshFile != null) //else uses bnsh 76 | { 77 | var mem = new MemoryStream(); 78 | BnshFile.Save(mem); 79 | BinaryData = mem.ToArray(); 80 | } 81 | } 82 | 83 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 84 | { 85 | SaveBinary(); 86 | this.Header.Size = (uint)BinaryData.Length; 87 | 88 | //Compute shader 89 | this.Header.ChildrenOffset = uint.MaxValue; 90 | this.Header.ChildrenCount = 0; 91 | 92 | base.Write(writer, ptclFile); 93 | 94 | if (ComputeShader != null) 95 | { 96 | WriteChildOffset(writer); 97 | ComputeShader.Write(writer, ptclFile); 98 | } 99 | 100 | if (BinaryData?.Length > 0) 101 | { 102 | //binary data next with alignment 103 | writer.AlignBytes(4096); 104 | 105 | WriteBinaryOffset(writer); 106 | writer.Write(BinaryData); 107 | } 108 | 109 | if (ComputeShader != null) 110 | ComputeShader.WriteData(writer); 111 | } 112 | } 113 | 114 | public class ComputeShader : SectionBase 115 | { 116 | public override string Magic => "GRSC"; 117 | 118 | public byte[] BinaryData; 119 | 120 | public BnshFile BnshFile = new BnshFile(); 121 | public BfshaFile BfshaFile; 122 | 123 | public ComputeShader() 124 | { 125 | this.Header.Magic = this.Magic; 126 | this.Header.ChildrenOffset = uint.MaxValue; 127 | this.Header.NextSectionOffset = uint.MaxValue; 128 | this.Header.AttrOffset = uint.MaxValue; 129 | this.Header.ChildrenCount = 0; 130 | } 131 | 132 | public BnshFile.ShaderVariation TryGetShader(int index) 133 | { 134 | if (BnshFile.Variations.Count > index && index != -1) 135 | return BnshFile.Variations[index]; 136 | 137 | return null; 138 | } 139 | 140 | public void SaveBinary() 141 | { 142 | if (BfshaFile != null) //uses bfsha (custom shaders) 143 | { 144 | var mem = new MemoryStream(); 145 | BnshFile.Save(mem); 146 | BinaryData = mem.ToArray(); 147 | 148 | } 149 | else if (BnshFile != null) //else uses bnsh 150 | { 151 | var mem = new MemoryStream(); 152 | BnshFile.Save(mem); 153 | BinaryData = mem.ToArray(); 154 | } 155 | } 156 | 157 | public override void Read(BinaryReader reader, PtclFile ptclFile) 158 | { 159 | base.Read(reader, ptclFile); 160 | 161 | //section contains BNSH 162 | //quick peek at header magic 163 | SeekFromHeader(reader, this.Header.BinaryOffset); 164 | string magic = Encoding.ASCII.GetString(reader.ReadBytes(4)); 165 | 166 | SeekFromHeader(reader, this.Header.BinaryOffset); 167 | BinaryData = reader.ReadBytes((int)this.Header.Size); 168 | 169 | switch (magic) 170 | { 171 | case "BNSH": 172 | BnshFile = new BnshFile(new MemoryStream(BinaryData)); 173 | break; 174 | case "FSHA": 175 | // File.WriteAllBytes("og.bfsha", BinaryData); 176 | BfshaFile = new BfshaFile(new MemoryStream(BinaryData)); 177 | BnshFile = BfshaFile.ShaderModels[0].BnshFile; 178 | // BfshaFile.Save("new.bfsha"); 179 | break; 180 | default: 181 | throw new Exception($"Unsupported shader format with magic {magic}!"); 182 | } 183 | } 184 | 185 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 186 | { 187 | SaveBinary(); 188 | this.Header.Size = (uint)BinaryData.Length; 189 | 190 | base.Write(writer, ptclFile); 191 | } 192 | 193 | public void WriteData(BinaryWriter writer) 194 | { 195 | //binary data next with alignment 196 | writer.AlignBytes(4096); 197 | 198 | WriteBinaryOffset(writer); 199 | writer.Write(BinaryData); 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /EffectLibrary/FileData/EFT2/Structs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EffectLibrary.EFT2 9 | { 10 | [StructLayout(LayoutKind.Sequential, Size = 0x10)] 11 | public struct BinaryHeader //A header shared between bntx and other formats 12 | { 13 | public ulong Magic; //VFXB + padding 14 | 15 | public ushort GraphicsAPIVersion; 16 | public ushort VFXVersion; 17 | 18 | public ushort ByteOrder; 19 | public byte Alignment; 20 | public byte TargetAddressSize; 21 | public uint NameOffset; // 22 | public ushort Flag; 23 | public ushort BlockOffset; 24 | public uint RelocationTableOffset; //0 25 | public uint FileSize; 26 | } 27 | 28 | [StructLayout(LayoutKind.Sequential, Size = 0x10)] 29 | public struct SectionHeader //A header shared between bntx and other formats 30 | { 31 | public Magic Magic; //VFXB + padding 32 | public uint Size; 33 | public uint ChildrenOffset; 34 | public uint NextSectionOffset; 35 | public uint AttrOffset; //Offsets to another section in emitter data 36 | public uint BinaryOffset; 37 | public uint Padding; 38 | public ushort ChildrenCount; 39 | public ushort Unknown; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /EffectLibrary/FileData/EFT2/Textures.cs: -------------------------------------------------------------------------------- 1 | using Syroot.NintenTools.NSW.Bntx; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using static System.Net.Mime.MediaTypeNames; 8 | 9 | namespace EffectLibrary.EFT2 10 | { 11 | 12 | public class TextureInfo : SectionBase 13 | { 14 | public override string Magic => "GRTF"; 15 | 16 | public TextureDescTable TexDescTable = new TextureDescTable(); 17 | 18 | public byte[] BinaryData; 19 | 20 | public BntxFile BntxFile; 21 | 22 | public override void Read(BinaryReader reader, PtclFile ptclFile) 23 | { 24 | base.Read(reader, ptclFile); 25 | 26 | if (this.Header.ChildrenCount != 1) 27 | throw new Exception(); 28 | 29 | //Descriptor 30 | reader.SeekBegin(StartPosition + this.Header.ChildrenOffset); 31 | TexDescTable = new TextureDescTable(); 32 | TexDescTable.Read(reader, ptclFile); 33 | 34 | //section contains BNTX 35 | if (this.Header.Size > 0) 36 | { 37 | reader.SeekBegin(StartPosition + this.Header.BinaryOffset); 38 | BinaryData = reader.ReadBytes((int)this.Header.Size); 39 | BntxFile = new BntxFile(new MemoryStream(BinaryData)); 40 | } 41 | } 42 | 43 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 44 | { 45 | /* if (BntxFile != null) 46 | { 47 | OrderTextures(); 48 | 49 | var mem = new MemoryStream(); 50 | BntxFile.Save(mem); 51 | this.BinaryData = mem.ToArray(); 52 | 53 | this.Header.Size = (uint)BinaryData.Length; 54 | }*/ 55 | 56 | //Descriptor 57 | this.Header.ChildrenOffset = 32; //descriptors as children 58 | this.Header.ChildrenCount = 1; 59 | 60 | base.Write(writer, ptclFile); 61 | 62 | WriteChildOffset(writer); 63 | TexDescTable.Write(writer, ptclFile); 64 | 65 | if (BinaryData?.Length > 0) 66 | { 67 | //binary data next with alignment 68 | writer.AlignBytes(4096); 69 | WriteBinaryOffset(writer); 70 | writer.Write(BinaryData); 71 | } 72 | 73 | writer.AlignBytes(16); 74 | WriteNextOffset(writer, false); 75 | } 76 | 77 | public Texture TryGetTexture(ulong id) 78 | { 79 | if (TexDescTable == null) 80 | throw new Exception("Failed to find texture descriptor table!"); 81 | 82 | var descPrimIndex = TexDescTable.Descriptors.FindIndex(x => x.ID == id); 83 | if (descPrimIndex != -1) 84 | { 85 | var desc = TexDescTable.Descriptors[descPrimIndex]; 86 | var idx = BntxFile.TextureDict.IndexOf(desc.Name); 87 | if (idx == -1) 88 | throw new Exception($"Failed to find texture {desc.Name} in BNTX!"); 89 | 90 | return BntxFile.Textures[idx]; 91 | } 92 | 93 | return null; 94 | } 95 | 96 | public void AddTexture(ulong id, Texture texture) 97 | { 98 | var descPrimIndex = TexDescTable.Descriptors.FindIndex(x => x.ID == id && x.Name == texture.Name); 99 | //ID already added, skip 100 | if (descPrimIndex != -1) 101 | return; 102 | 103 | TexDescTable.Descriptors.Add(new TextureDescTable.Descriptor() 104 | { 105 | ID = id, 106 | Name = texture.Name, 107 | }); 108 | 109 | var idx = BntxFile.TextureDict.IndexOf(texture.Name); 110 | if (idx != -1) 111 | return; 112 | 113 | BntxFile.Textures.Add(texture); 114 | BntxFile.TextureDict.Add(texture.Name); 115 | } 116 | 117 | public void OrderTextures() 118 | { 119 | if (BntxFile == null) 120 | return; 121 | //order textures by descriptor 122 | var textures = BntxFile.Textures.ToList(); 123 | string name = BntxFile.Name; 124 | 125 | this.TexDescTable.Descriptors = this.TexDescTable.Descriptors.OrderBy(x => x.Name).ToList(); 126 | 127 | BntxFile.Textures.Clear(); 128 | BntxFile.TextureDict.Clear(); 129 | 130 | foreach (var desc in this.TexDescTable.Descriptors) 131 | { 132 | var tex = textures.FirstOrDefault(x => x.Name == desc.Name); 133 | if (tex == null) 134 | throw new Exception($"Failed to find texture {desc.Name} in BNTX!"); 135 | 136 | BntxFile.Textures.Add(tex); 137 | BntxFile.TextureDict.Add(tex.Name); 138 | } 139 | } 140 | } 141 | 142 | public class TextureDescTable : SectionBase 143 | { 144 | public List Descriptors = new List(); 145 | 146 | public override void Read(BinaryReader reader, PtclFile ptclFile) 147 | { 148 | base.Read(reader, ptclFile); 149 | 150 | reader.SeekBegin(StartPosition + this.Header.BinaryOffset); 151 | ReadBinary(reader); 152 | } 153 | 154 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 155 | { 156 | var binaryData = WriteBinary().ToArray(); 157 | 158 | this.Header.Size = (uint)binaryData.Length; 159 | 160 | base.Write(writer, ptclFile); 161 | 162 | WriteBinaryOffset(writer); 163 | writer.Write(binaryData); 164 | 165 | writer.AlignBytes(16); 166 | this.WriteNextOffset(writer, false); 167 | } 168 | 169 | private void ReadBinary(BinaryReader reader) 170 | { 171 | var end_pos = StartPosition + this.Header.BinaryOffset + this.Header.Size; 172 | while (end_pos > reader.BaseStream.Position) 173 | { 174 | long pos = reader.BaseStream.Position; 175 | 176 | ulong id = reader.ReadUInt64(); 177 | uint next_offset = reader.ReadUInt32(); //to next descriptor 178 | 179 | //name 180 | int len = reader.ReadInt32(); 181 | string name = reader.ReadFixedString(len); 182 | 183 | Descriptors.Add(new Descriptor() 184 | { 185 | Name = name, 186 | ID = id, 187 | }); 188 | 189 | if (next_offset == 0) 190 | break; 191 | 192 | reader.SeekBegin(pos + next_offset); 193 | } 194 | } 195 | 196 | private MemoryStream WriteBinary() 197 | { 198 | var mem = new MemoryStream(); 199 | using (var writer = new BinaryWriter(mem)) 200 | { 201 | for (int i = 0; i < Descriptors.Count; i++) 202 | { 203 | long pos = writer.BaseStream.Position; 204 | 205 | writer.Write(Descriptors[i].ID); 206 | writer.Write(0); // next offset 207 | writer.Write(Descriptors[i].Name.Length + 1); 208 | writer.Write(Encoding.UTF8.GetBytes(Descriptors[i].Name)); 209 | //Add 2 extra bytes to offset the alignment/padding 210 | writer.Write((short)0); 211 | 212 | writer.AlignBytes(8); 213 | 214 | if (i < Descriptors.Count - 1) //write next offset 215 | writer.WriteOffset(pos + 8, pos); 216 | } 217 | } 218 | return mem; 219 | } 220 | 221 | public class Descriptor 222 | { 223 | public ulong ID; 224 | public string Name; 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /EffectLibrary/FileData/EFTB/TextureArrayGX2.cs: -------------------------------------------------------------------------------- 1 | using BfresLibrary.WiiU; 2 | using Syroot.NintenTools.NSW.Bntx; 3 | using Syroot.NintenTools.NSW.Bntx.GFX; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Numerics; 8 | using System.Runtime.InteropServices; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace EffectLibrary.EFT2 13 | { 14 | public class TextureArrayGX2 : SectionBase 15 | { 16 | public override string Magic => "TEXA"; //Texture array 17 | 18 | public List Textures = new List(); 19 | 20 | public override void Read(BinaryReader reader, PtclFile ptclFile) 21 | { 22 | base.Read(reader, ptclFile); 23 | 24 | reader.SeekBegin(StartPosition + this.Header.ChildrenOffset); 25 | 26 | for (int i = 0; i < this.Header.ChildrenCount; i++) 27 | { 28 | TextureGX2 tex = new TextureGX2(); 29 | tex.Read(reader, ptclFile); 30 | Textures.Add(tex); 31 | } 32 | } 33 | 34 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 35 | { 36 | 37 | } 38 | } 39 | 40 | public class TextureGX2 : SectionBase 41 | { 42 | public override string Magic => "TEXR"; //Texture Resource 43 | 44 | public GX2Binary GX2Bin = new GX2Binary(); 45 | 46 | public ushort Width; 47 | public ushort Height; 48 | 49 | public uint val0x4; 50 | public uint val0x10; 51 | public uint val0x18; 52 | public uint val0x1C; 53 | public uint val0x20; 54 | 55 | public uint TileMode; 56 | public uint MipCount; 57 | public uint CompSel; 58 | public GX2SurfaceFormat SurfFormat; 59 | public byte[] data; 60 | public uint TextureID; 61 | public byte[] Padding = new byte[7]; 62 | 63 | public override void Read(BinaryReader reader, PtclFile ptclFile) 64 | { 65 | base.Read(reader, ptclFile); 66 | 67 | reader.SeekBegin(StartPosition + this.Header.BinaryOffset); 68 | //texture header (48 bytes) 69 | ReadBinary(reader); 70 | 71 | //raw data block (GX2B) 72 | reader.SeekBegin(StartPosition + this.Header.ChildrenOffset); 73 | GX2Bin.Read(reader, ptclFile); 74 | } 75 | 76 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 77 | { 78 | var binaryData = WriteBinary().ToArray(); 79 | 80 | this.Header.Size = (uint)binaryData.Length; 81 | 82 | base.Write(writer, ptclFile); 83 | 84 | WriteBinaryOffset(writer); 85 | writer.Write(binaryData); 86 | 87 | writer.AlignBytes(16); 88 | this.WriteNextOffset(writer, false); 89 | } 90 | 91 | private void ReadBinary(BinaryReader reader) 92 | { 93 | Width = reader.ReadUInt16(); 94 | Height = reader.ReadUInt16(); 95 | val0x4 = reader.ReadUInt32(); 96 | CompSel = reader.ReadUInt32(); 97 | MipCount = reader.ReadUInt32(); 98 | val0x10 = reader.ReadUInt32(); 99 | TileMode = reader.ReadUInt32(); 100 | val0x18 = reader.ReadUInt32(); 101 | val0x1C = reader.ReadUInt32(); 102 | val0x20 = reader.ReadUInt32(); 103 | TextureID = reader.ReadUInt32(); 104 | SurfFormat = (GX2SurfaceFormat)reader.ReadByte(); 105 | Padding = reader.ReadBytes(7); 106 | } 107 | 108 | private MemoryStream WriteBinary() 109 | { 110 | var mem = new MemoryStream(); 111 | using (var writer = new BinaryWriter(mem)) 112 | { 113 | writer.Write((ushort)Width); 114 | writer.Write((ushort)Height); 115 | writer.Write(val0x4); 116 | writer.Write(CompSel); 117 | writer.Write(MipCount); 118 | writer.Write(val0x10); 119 | writer.Write(TileMode); 120 | writer.Write(val0x18); 121 | writer.Write(val0x1C); 122 | writer.Write(val0x20); 123 | writer.Write(TextureID); 124 | writer.Write((byte)SurfFormat); 125 | writer.Write(Padding); 126 | } 127 | return mem; 128 | } 129 | 130 | public class GX2Binary : SectionBase 131 | { 132 | public override string Magic => "GX2B"; //GX2 Raw image data 133 | 134 | public byte[] Data; 135 | 136 | public override void Read(BinaryReader reader, PtclFile ptclFile) 137 | { 138 | base.Read(reader, ptclFile); 139 | 140 | reader.SeekBegin(StartPosition + this.Header.BinaryOffset); 141 | Data = reader.ReadBytes((int)this.Header.Size); 142 | } 143 | 144 | public override void Write(BinaryWriter writer, PtclFile ptclFile) 145 | { 146 | this.Header.Size = (uint)Data.Length; 147 | 148 | base.Write(writer, ptclFile); 149 | 150 | WriteBinaryOffset(writer); 151 | writer.Write(Data); 152 | 153 | writer.AlignBytes(16); 154 | this.WriteNextOffset(writer, false); 155 | } 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /EffectLibrary/LIb/BfresLibrary.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KillzXGaming/EffectLibrary/d5491138d645221ec92189eeff3ded8c43fd0a1a/EffectLibrary/LIb/BfresLibrary.dll -------------------------------------------------------------------------------- /EffectLibrary/LIb/ShaderLibrary.deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtimeTarget": { 3 | "name": ".NETCoreApp,Version=v6.0", 4 | "signature": "" 5 | }, 6 | "compilationOptions": {}, 7 | "targets": { 8 | ".NETCoreApp,Version=v6.0": { 9 | "ShaderLibrary/1.0.0": { 10 | "runtime": { 11 | "ShaderLibrary.dll": {} 12 | } 13 | } 14 | } 15 | }, 16 | "libraries": { 17 | "ShaderLibrary/1.0.0": { 18 | "type": "project", 19 | "serviceable": false, 20 | "sha512": "" 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /EffectLibrary/LIb/ShaderLibrary.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KillzXGaming/EffectLibrary/d5491138d645221ec92189eeff3ded8c43fd0a1a/EffectLibrary/LIb/ShaderLibrary.dll -------------------------------------------------------------------------------- /EffectLibrary/LIb/Syroot.BinaryData.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KillzXGaming/EffectLibrary/d5491138d645221ec92189eeff3ded8c43fd0a1a/EffectLibrary/LIb/Syroot.BinaryData.dll -------------------------------------------------------------------------------- /EffectLibrary/LIb/Syroot.Maths.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KillzXGaming/EffectLibrary/d5491138d645221ec92189eeff3ded8c43fd0a1a/EffectLibrary/LIb/Syroot.Maths.dll -------------------------------------------------------------------------------- /EffectLibrary/LIb/Syroot.NintenTools.NSW.Bntx.deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtimeTarget": { 3 | "name": ".NETStandard,Version=v2.1/", 4 | "signature": "" 5 | }, 6 | "compilationOptions": {}, 7 | "targets": { 8 | ".NETStandard,Version=v2.1": {}, 9 | ".NETStandard,Version=v2.1/": { 10 | "Syroot.NintenTools.NSW.Bntx/1.0.0": { 11 | "dependencies": { 12 | "Syroot.BinaryData": "2.0.1.0", 13 | "Syroot.Maths": "1.5.3.0" 14 | }, 15 | "runtime": { 16 | "Syroot.NintenTools.NSW.Bntx.dll": {} 17 | } 18 | }, 19 | "Syroot.BinaryData/2.0.1.0": { 20 | "runtime": { 21 | "Syroot.BinaryData.dll": { 22 | "assemblyVersion": "2.0.1.0", 23 | "fileVersion": "2.0.1.0" 24 | } 25 | } 26 | }, 27 | "Syroot.Maths/1.5.3.0": { 28 | "runtime": { 29 | "Syroot.Maths.dll": { 30 | "assemblyVersion": "1.5.3.0", 31 | "fileVersion": "1.5.3.0" 32 | } 33 | } 34 | } 35 | } 36 | }, 37 | "libraries": { 38 | "Syroot.NintenTools.NSW.Bntx/1.0.0": { 39 | "type": "project", 40 | "serviceable": false, 41 | "sha512": "" 42 | }, 43 | "Syroot.BinaryData/2.0.1.0": { 44 | "type": "reference", 45 | "serviceable": false, 46 | "sha512": "" 47 | }, 48 | "Syroot.Maths/1.5.3.0": { 49 | "type": "reference", 50 | "serviceable": false, 51 | "sha512": "" 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /EffectLibrary/LIb/Syroot.NintenTools.NSW.Bntx.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KillzXGaming/EffectLibrary/d5491138d645221ec92189eeff3ded8c43fd0a1a/EffectLibrary/LIb/Syroot.NintenTools.NSW.Bntx.dll -------------------------------------------------------------------------------- /EffectLibrary/Shared/Enums.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EffectLibrary 8 | { 9 | public enum ColorType : byte 10 | { 11 | Constant, 12 | Random, 13 | Animated8Key, 14 | } 15 | 16 | public enum GX2SurfaceFormat : byte 17 | { 18 | INVALID = 0x0, 19 | TCS_R8_G8_B8_A8 = 2, 20 | T_BC1_UNORM = 3, 21 | T_BC1_SRGB = 4, 22 | T_BC2_UNORM = 5, 23 | T_BC2_SRGB = 6, 24 | T_BC3_UNORM = 7, 25 | T_BC3_SRGB = 8, 26 | T_BC4_UNORM = 9, 27 | T_BC4_SNORM = 10, 28 | T_BC5_UNORM = 11, 29 | T_BC5_SNORM = 12, 30 | TC_R8_UNORM = 13, 31 | TC_R8_G8_UNORM = 14, 32 | TCS_R8_G8_B8_A8_UNORM = 15, 33 | TCS_R5_G6_B5_UNORM = 25, 34 | }; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /EffectLibrary/Shared/PtclSerialize.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualBasic.FileIO; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.Drawing; 7 | using System.Linq; 8 | using System.Reflection; 9 | using System.Runtime.InteropServices; 10 | using System.Runtime.Serialization; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace EffectLibrary 15 | { 16 | public class PtclSerialize 17 | { 18 | public static T Serialize(BinaryReader reader, T obj, int version, long start_pos) 19 | { 20 | foreach (var field in obj.GetType().GetFields()) 21 | { 22 | var versionCheck = field.GetCustomAttribute(); 23 | if (!IsUsed(versionCheck, version) || field.GetCustomAttribute() != null) 24 | continue; 25 | 26 | if (field.FieldType == typeof(string)) //fixed string 27 | { 28 | var attribute = field.GetCustomAttribute(); 29 | string value = Encoding.UTF8.GetString(reader.ReadBytes(attribute.SizeConst)).Replace("\0", ""); 30 | field.SetValue(obj, value); 31 | } 32 | else if (typeof(IEnumerable).IsAssignableFrom(field.FieldType)) 33 | LoadEnumerable(reader, field, obj, version, start_pos); 34 | else if (IsNonPrimitiveClass(field.FieldType)) 35 | { 36 | var instance = Activator.CreateInstance(field.FieldType); 37 | PtclSerialize.Serialize(reader, instance, version, start_pos); 38 | field.SetValue(obj, instance); 39 | } 40 | else 41 | field.SetValue(obj, ReadPrimitive(reader, field.FieldType, obj)); 42 | } 43 | return obj; 44 | } 45 | 46 | static bool IsUsed(VersionCheck versionCheck, int currentVersion) 47 | { 48 | if (versionCheck == null) return true; 49 | 50 | return versionCheck.IsValid(currentVersion); 51 | } 52 | 53 | public static void Deserialize(BinaryWriter writer, T obj, int version) 54 | { 55 | foreach (var field in obj.GetType().GetFields()) 56 | { 57 | var versionCheck = field.GetCustomAttribute(); 58 | if (!IsUsed(versionCheck, version) || field.GetCustomAttribute() != null) 59 | continue; 60 | 61 | var value = field.GetValue(obj); 62 | if (field.FieldType == typeof(string)) //fixed string 63 | { 64 | var attribute = field.GetCustomAttribute(); 65 | writer.Write(Encoding.UTF8.GetBytes((string)value)); 66 | writer.Write(new byte[attribute.SizeConst - ((string)value).Length]); 67 | } 68 | else if (typeof(IEnumerable).IsAssignableFrom(field.FieldType)) 69 | SaveEnumerable(writer, field, obj, version); 70 | else if (field.FieldType.IsClass) 71 | PtclSerialize.Deserialize(writer, value, version); 72 | else 73 | WritePrimitive(writer, field.FieldType, value); 74 | } 75 | } 76 | 77 | static void WritePrimitive(BinaryWriter writer, Type type, object obj) 78 | { 79 | if (type.IsEnum) 80 | type = Enum.GetUnderlyingType(type); 81 | 82 | if (type== typeof(uint)) writer.Write((uint)obj); 83 | else if (type== typeof(int)) writer.Write((int)obj); 84 | else if (type== typeof(float)) writer.Write((float)obj); 85 | else if (type== typeof(ulong)) writer.Write((ulong)obj); 86 | else if (type == typeof(sbyte)) writer.Write((sbyte)obj); 87 | else if (type == typeof(byte)) writer.Write((byte)obj); 88 | else if (type == typeof(bool)) writer.Write((bool)obj); 89 | else if (type == typeof(ushort)) writer.Write((ushort)obj); 90 | else if (type == typeof(short)) writer.Write((short)obj); 91 | else if (type== typeof(long)) writer.Write((long)obj); 92 | else if (type== typeof(decimal)) writer.Write((decimal)obj); 93 | else if (type== typeof(double)) writer.Write((double)obj); 94 | else if (type== typeof(char[])) writer.Write((char[])obj); 95 | else if (type== typeof(uint[])) writer.Write((uint[])obj); 96 | else if (type== typeof(int[])) writer.Write((int[])obj); 97 | else 98 | throw new Exception($"Unsupported type {type}"); 99 | } 100 | 101 | static object ReadPrimitive(BinaryReader reader, Type type, object obj) 102 | { 103 | if (type.IsEnum) 104 | type = Enum.GetUnderlyingType(type); 105 | 106 | if (type == typeof(uint)) return reader.ReadUInt32(); 107 | else if (type== typeof(int)) return reader.ReadInt32(); 108 | else if (type== typeof(float)) return reader.ReadSingle(); 109 | else if (type== typeof(byte)) return reader.ReadByte(); 110 | else if (type== typeof(sbyte)) return reader.ReadSByte(); 111 | else if (type == typeof(ushort)) return reader.ReadUInt16(); 112 | else if (type == typeof(short)) return reader.ReadInt16(); 113 | else if (type== typeof(bool)) return reader.ReadBoolean(); 114 | else if (type== typeof(ulong)) return reader.ReadUInt64(); 115 | else if (type== typeof(long)) return reader.ReadInt64(); 116 | else if (type== typeof(decimal)) return reader.ReadDecimal(); 117 | else if (type== typeof(double)) return reader.ReadDouble(); 118 | else 119 | throw new Exception($"Unsupported type {type}"); 120 | } 121 | 122 | static void LoadEnumerable(BinaryReader reader, FieldInfo field, object obj, int version, long start_pos) 123 | { 124 | Type elementType = GetEnumerableElementType(field.FieldType); 125 | if (elementType == null) 126 | throw new Exception($"Field {field.Name} is not an enumerable type."); 127 | 128 | var value = field.GetValue(obj); 129 | var attribute = field.GetCustomAttribute(); 130 | if (attribute != null && attribute.Value != UnmanagedType.ByValArray) 131 | throw new Exception($"Field {field.Name} must have a MarshalAs attribute of type ByValArray."); 132 | 133 | var size = attribute.SizeConst; 134 | 135 | Array array = Array.CreateInstance(elementType, size); 136 | for (int i = 0; i < size; i++) 137 | { 138 | object instance; 139 | if (IsNonPrimitiveClass(elementType)) 140 | { 141 | instance = Activator.CreateInstance(elementType); 142 | PtclSerialize.Serialize(reader, instance, version, start_pos); 143 | } 144 | else 145 | { 146 | // Read primitive value 147 | instance = ReadPrimitive(reader, elementType, obj); 148 | } 149 | // Set the value in the array 150 | array.SetValue(instance, i); 151 | } 152 | field.SetValue(obj, array); 153 | } 154 | 155 | static void SaveEnumerable(BinaryWriter writer, FieldInfo field, object obj, int version) 156 | { 157 | Type elementType = GetEnumerableElementType(field.FieldType); 158 | if (elementType == null) 159 | throw new Exception($"Field {field.Name} is not an enumerable type."); 160 | 161 | var value = (Array)field.GetValue(obj); 162 | var attribute = field.GetCustomAttribute(); 163 | if (attribute != null && attribute.Value != UnmanagedType.ByValArray) 164 | throw new Exception($"Field {field.Name} must have a MarshalAs attribute of type ByValArray."); 165 | 166 | var size = attribute.SizeConst; 167 | for (int i = 0; i < size; i++) 168 | { 169 | if (IsNonPrimitiveClass(elementType)) 170 | PtclSerialize.Deserialize(writer, value.GetValue(i), version); 171 | else // Read primitive value 172 | WritePrimitive(writer, elementType, value.GetValue(i)); 173 | } 174 | } 175 | 176 | static Type GetEnumerableElementType(Type enumerableType) 177 | { 178 | // Check if the type implements IEnumerable 179 | if (enumerableType.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(enumerableType.GetGenericTypeDefinition())) 180 | { 181 | // Return the generic type argument 182 | return enumerableType.GetGenericArguments()[0]; 183 | } 184 | 185 | // Check if the type implements IEnumerable and try to find the generic IEnumerable interface 186 | var iEnumerableType = enumerableType.GetInterface(typeof(IEnumerable<>).FullName); 187 | if (iEnumerableType != null) 188 | { 189 | return iEnumerableType.GetGenericArguments()[0]; 190 | } 191 | 192 | // If it is not generic, return null (or handle it accordingly) 193 | return null; 194 | } 195 | 196 | static bool IsNonPrimitiveClass(Type type) 197 | { 198 | // Check if the type is a class but not a primitive type, value type, or string 199 | return type.IsClass && !type.IsPrimitive && !type.IsValueType 200 | && type != typeof(string); 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /EffectLibrary/Shared/Structs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace EffectLibrary 9 | { 10 | 11 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 12 | public struct Magic 13 | { 14 | int value; 15 | public static implicit operator string(Magic magic) => Encoding.ASCII.GetString(BitConverter.GetBytes(magic.value)); 16 | public static implicit operator Magic(string s) => new Magic { value = BitConverter.ToInt32(Encoding.ASCII.GetBytes(s), 0) }; 17 | 18 | public override string ToString() 19 | { 20 | return Encoding.ASCII.GetString(BitConverter.GetBytes(value)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /EffectLibrary/Shared/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Drawing; 4 | using System.Numerics; 5 | using System.Runtime.CompilerServices; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | 9 | namespace EffectLibrary 10 | { 11 | internal readonly ref struct TemporarySeekHandle 12 | { 13 | private readonly Stream Stream; 14 | private readonly long RetPos; 15 | 16 | public TemporarySeekHandle(Stream stream, long retpos) 17 | { 18 | this.Stream = stream; 19 | this.RetPos = retpos; 20 | } 21 | 22 | public readonly void Dispose() 23 | { 24 | Stream.Seek(RetPos, SeekOrigin.Begin); 25 | } 26 | } 27 | 28 | internal static class Utils 29 | { 30 | public static void WriteOffset(this BinaryWriter writer, long offset, long start) 31 | { 32 | long pos = writer.BaseStream.Position - start; 33 | using (writer.BaseStream.TemporarySeek(offset, SeekOrigin.Begin)) 34 | { 35 | writer.Write((uint)pos); 36 | } 37 | } 38 | 39 | public static void WriteZeroTerminatedString(this BinaryWriter writer, string text) 40 | { 41 | writer.Write(Encoding.UTF8.GetBytes(text)); 42 | writer.Write((byte)0); 43 | } 44 | 45 | public static string ReadFixedString(this BinaryReader reader, int size) 46 | { 47 | return Encoding.UTF8.GetString(reader.ReadBytes(size)).Replace("\0", ""); 48 | } 49 | 50 | public static Span AsSpan(ref T val) where T : unmanaged 51 | { 52 | Span valSpan = MemoryMarshal.CreateSpan(ref val, 1); 53 | return MemoryMarshal.Cast(valSpan); 54 | } 55 | 56 | public static void WriteStructs(this BinaryWriter writer, IEnumerable val) 57 | { 58 | var list = val.ToList(); 59 | for (int i = 0; i < list.Count; i++) 60 | writer.WriteStruct(list[i]); 61 | } 62 | 63 | public static void WriteStruct(this BinaryWriter writer, T val) 64 | { 65 | writer.Write(StructToBytes(val)); 66 | } 67 | 68 | static byte[] StructToBytes(T val) 69 | { 70 | IntPtr ptr = IntPtr.Zero; 71 | var size = Marshal.SizeOf(typeof(T)); 72 | var buffer = new byte[size]; 73 | 74 | try 75 | { 76 | 77 | ptr = Marshal.AllocHGlobal(size); 78 | Marshal.StructureToPtr(val, ptr, true); 79 | Marshal.Copy(ptr, buffer, 0, size); 80 | } 81 | finally 82 | { 83 | Marshal.FreeHGlobal(ptr); 84 | } 85 | return buffer; 86 | } 87 | 88 | public static List ReadStructs(this BinaryReader reader, int num) 89 | { 90 | T[] list = new T[num]; 91 | for (int i = 0; i < num; i++) 92 | list[i] = reader.ReadStruct(); 93 | 94 | return list.ToList(); 95 | } 96 | 97 | public static T ReadStruct(this BinaryReader reader) 98 | { 99 | int size = Marshal.SizeOf(); 100 | var byteArray = reader.ReadBytes(size); 101 | 102 | IntPtr ptr = Marshal.AllocHGlobal(size); 103 | Marshal.Copy(byteArray, 0, ptr, size); 104 | 105 | T result = Marshal.PtrToStructure(ptr); 106 | 107 | Marshal.FreeHGlobal(ptr); 108 | 109 | return result; 110 | } 111 | 112 | public static Vector2 ReadVector2(this BinaryReader reader) 113 | { 114 | return new Vector2(reader.ReadSingle(), reader.ReadSingle()); 115 | } 116 | 117 | public static Vector3 ReadVector3(this BinaryReader reader) 118 | { 119 | return new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); 120 | } 121 | 122 | public static Vector4 ReadVector4(this BinaryReader reader) 123 | { 124 | return new Vector4(reader.ReadSingle(), reader.ReadSingle(), 125 | reader.ReadSingle(), reader.ReadSingle()); 126 | } 127 | 128 | public static void Write(this BinaryWriter writer, float[] values) 129 | { 130 | for (int i = 0; i < values.Length; i++) 131 | writer.Write(values[i]); 132 | } 133 | 134 | public static void Write(this BinaryWriter writer, uint[] values) 135 | { 136 | for (int i = 0; i < values.Length; i++) 137 | writer.Write(values[i]); 138 | } 139 | 140 | public static void Write(this BinaryWriter writer, int[] values) 141 | { 142 | for (int i = 0; i < values.Length; i++) 143 | writer.Write(values[i]); 144 | } 145 | 146 | public static void Write(this BinaryWriter writer, bool[] values) 147 | { 148 | for (int i = 0; i < values.Length; i++) 149 | writer.Write(values[i]); 150 | } 151 | 152 | public static void AlignBytes(this BinaryReader reader, int align) 153 | { 154 | var startPos = reader.BaseStream.Position; 155 | long position = reader.BaseStream.Seek((int)(-reader.BaseStream.Position % align + align) % align, SeekOrigin.Current); 156 | 157 | reader.SeekBegin((int)startPos); 158 | while (reader.BaseStream.Position != position) 159 | { 160 | reader.ReadByte(); 161 | } 162 | } 163 | 164 | public static void AlignBytes(this BinaryWriter writer, int align, byte pad_val = 0) 165 | { 166 | var startPos = writer.BaseStream.Position; 167 | long position = writer.Seek((int)(-writer.BaseStream.Position % align + align) % align, SeekOrigin.Current); 168 | 169 | writer.Seek((int)startPos, System.IO.SeekOrigin.Begin); 170 | while (writer.BaseStream.Position != position) 171 | { 172 | writer.Write((byte)pad_val); 173 | } 174 | } 175 | 176 | public static sbyte[] ReadSbytes(this BinaryReader reader, int count) 177 | { 178 | sbyte[] values = new sbyte[count]; 179 | for (int i = 0; i < count; i++) 180 | values[i] = reader.ReadSByte(); 181 | return values; 182 | } 183 | 184 | public static bool[] ReadBooleans(this BinaryReader reader, int count) 185 | { 186 | bool[] values = new bool[count]; 187 | for (int i = 0; i < count; i++) 188 | values[i] = reader.ReadBoolean(); 189 | return values; 190 | } 191 | 192 | public static float[] ReadSingles(this BinaryReader reader, int count) 193 | { 194 | float[] values = new float[count]; 195 | for (int i = 0; i < count; i++) 196 | values[i] = reader.ReadSingle(); 197 | return values; 198 | } 199 | 200 | public static ushort[] ReadUInt16s(this BinaryReader reader, int count) 201 | { 202 | ushort[] values = new ushort[count]; 203 | for (int i = 0; i < count; i++) 204 | values[i] = reader.ReadUInt16(); 205 | return values; 206 | } 207 | 208 | public static int[] ReadInt32s(this BinaryReader reader, int count) 209 | { 210 | int[] values = new int[count]; 211 | for (int i = 0; i < count; i++) 212 | values[i] = reader.ReadInt32(); 213 | return values; 214 | } 215 | 216 | public static uint[] ReadUInt32s(this BinaryReader reader, int count) 217 | { 218 | uint[] values = new uint[count]; 219 | for (int i = 0; i < count; i++) 220 | values[i] = reader.ReadUInt32(); 221 | return values; 222 | } 223 | 224 | public static long[] ReadInt64s(this BinaryReader reader, int count) 225 | { 226 | long[] values = new long[count]; 227 | for (int i = 0; i < count; i++) 228 | values[i] = reader.ReadInt64(); 229 | return values; 230 | } 231 | 232 | public static ulong[] ReadUInt64s(this BinaryReader reader, int count) 233 | { 234 | ulong[] values = new ulong[count]; 235 | for (int i = 0; i < count; i++) 236 | values[i] = reader.ReadUInt64(); 237 | return values; 238 | } 239 | 240 | public static T ReadCustom(this BinaryReader reader, Func value, ulong offset) 241 | { 242 | if (offset == 0) return default(T); 243 | 244 | long pos = reader.BaseStream.Position; 245 | 246 | reader.SeekBegin((long)offset); 247 | 248 | var result = value.Invoke(); 249 | 250 | reader.SeekBegin((long)pos); 251 | 252 | return result; 253 | } 254 | 255 | public static void SeekBegin(this BinaryReader reader, long offset) 256 | { 257 | reader.BaseStream.Seek(offset, SeekOrigin.Begin); 258 | } 259 | 260 | public static void SeekBegin(this BinaryReader reader, ulong offset) 261 | { 262 | reader.BaseStream.Seek((long)offset, SeekOrigin.Begin); 263 | } 264 | 265 | public static ushort ReadUInt16BigEndian(this BinaryReader reader) 266 | { 267 | byte[] bytes = reader.ReadBytes(2); 268 | Array.Reverse(bytes); //Reverse bytes 269 | return BitConverter.ToUInt16(bytes, 0); 270 | } 271 | 272 | public static bool[] ReadBooleanBits(this BinaryReader reader, int count) 273 | { 274 | bool[] booleans = new bool[count]; 275 | 276 | int idx = 0; 277 | var bitFlags = reader.ReadInt64s(1 + count / 64); 278 | for (int i = 0; i < count; i++) 279 | { 280 | if (i != 0 && i % 64 == 0) 281 | idx++; 282 | 283 | booleans[i] = (bitFlags[idx] & ((long)1 << i)) != 0; 284 | } 285 | return booleans; 286 | } 287 | 288 | public static List ReadStringOffsets(this BinaryReader reader, int count) 289 | { 290 | string[] strings = new string[count]; 291 | for (int i = 0; i < count; i++) 292 | { 293 | var offset = reader.ReadUInt64(); 294 | strings[i] = reader.ReadStringOffset(offset); 295 | } 296 | return strings.ToList(); 297 | } 298 | 299 | public static string ReadStringOffset(this BinaryReader reader, ulong offset) 300 | { 301 | long pos = reader.BaseStream.Position; 302 | 303 | reader.SeekBegin(offset); 304 | 305 | ushort size = reader.ReadUInt16(); 306 | string value = reader.ReadUtf8Z(); 307 | reader.BaseStream.Seek(pos, SeekOrigin.Begin); 308 | 309 | return value; 310 | } 311 | 312 | public static uint ReadUInt24(this BinaryReader reader) 313 | { 314 | /* Read out 3 bytes into a sizeof(uint) buffer. */ 315 | Span bytes = stackalloc byte[4]; 316 | reader.BaseStream.Read(bytes[..^1]); 317 | 318 | bytes[3] = 0; 319 | /* Convert buffer into uint. */ 320 | uint v = BitConverter.ToUInt32(bytes); 321 | 322 | return v; 323 | } 324 | public static void WriteUInt24(this BinaryWriter writer, uint value) 325 | { 326 | /* Build a byte array from the value. */ 327 | Span bytes = new byte[3] 328 | { 329 | (byte)(value & 0xFF), 330 | (byte)(value >> 8 & 0xFF), 331 | (byte)(value >> 16 & 0xFF), 332 | }; 333 | 334 | /* Write array. */ 335 | writer.BaseStream.Write(bytes); 336 | } 337 | public static T[] ReadArray(this Stream stream, uint count) where T : struct 338 | { 339 | /* Read data. */ 340 | T[] data = new T[count]; 341 | 342 | /* Read into casted span. */ 343 | stream.Read(MemoryMarshal.Cast(data)); 344 | 345 | return data; 346 | } 347 | 348 | public static void WriteArray(this Stream stream, ReadOnlySpan array) where T : struct 349 | { 350 | stream.Write(MemoryMarshal.Cast(array)); 351 | } 352 | 353 | public static TemporarySeekHandle TemporarySeek(this Stream stream) 354 | { 355 | return stream.TemporarySeek(0, SeekOrigin.Begin); 356 | } 357 | 358 | public static TemporarySeekHandle TemporarySeek(this Stream stream, long offset, SeekOrigin origin) 359 | { 360 | long ret = stream.Position; 361 | stream.Seek(offset, origin); 362 | return new TemporarySeekHandle(stream, ret); 363 | } 364 | 365 | public static int BinarySearch(IList arr, K v) where T : IComparable 366 | { 367 | var start = 0; 368 | var end = arr.Count - 1; 369 | 370 | while (start <= end) 371 | { 372 | var mid = (start + end) / 2; 373 | var entry = arr[mid]; 374 | var cmp = entry.CompareTo(v); 375 | 376 | if (cmp == 0) 377 | return mid; 378 | if (cmp > 0) 379 | end = mid - 1; 380 | else /* if (cmp < 0) */ 381 | start = mid + 1; 382 | } 383 | 384 | return ~start; 385 | } 386 | public static BinaryReader AsBinaryReader(this Stream stream) 387 | { 388 | return new BinaryReader(stream); 389 | } 390 | public static BinaryWriter AsBinaryWriter(this Stream stream) 391 | { 392 | return new BinaryWriter(stream); 393 | } 394 | 395 | public static string ReadUtf8(this BinaryReader reader, int size) 396 | { 397 | return Encoding.UTF8.GetString(reader.ReadBytes(size), 0, size); 398 | } 399 | 400 | public static string ReadUtf8Z(this BinaryReader reader, int maxLength = int.MaxValue) 401 | { 402 | long start = reader.BaseStream.Position; 403 | int size = 0; 404 | 405 | // Read until we hit the end of the stream (-1) or a zero 406 | while (reader.BaseStream.ReadByte() - 1 > 0 && size < maxLength) 407 | { 408 | size++; 409 | } 410 | 411 | reader.BaseStream.Position = start; 412 | string text = reader.ReadUtf8(size); 413 | reader.BaseStream.Position++; // Skip the null byte 414 | return text; 415 | } 416 | 417 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 418 | public static uint AlignUp(uint num, uint align) 419 | { 420 | return (num + (align - 1)) & ~(align - 1); 421 | } 422 | 423 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 424 | public static int AlignUp(int num, int align) 425 | { 426 | return (num + (align - 1)) & ~(align - 1); 427 | } 428 | 429 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 430 | public static ulong AlignUp(ulong num, ulong align) 431 | { 432 | return (num + (align - 1)) & ~(align - 1); 433 | } 434 | 435 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 436 | public static long AlignUp(long num, long align) 437 | { 438 | return (num + (align - 1)) & ~(align - 1); 439 | } 440 | 441 | public static float ReadHalfFloat(this BinaryReader binaryReader) 442 | { 443 | return (float)binaryReader.ReadHalf(); 444 | } 445 | } 446 | } -------------------------------------------------------------------------------- /EffectLibrary/Shared/VersionCheck.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace EffectLibrary 8 | { 9 | public class VersionCheck : Attribute 10 | { 11 | public int Version; 12 | public VersionCompare Compare; 13 | 14 | public int Version2; 15 | public VersionCompare Compare2; 16 | 17 | public VersionCheck(VersionCompare compare, int version) 18 | { 19 | Compare = compare; 20 | Version = version; 21 | } 22 | 23 | public VersionCheck(VersionCompare compare, int version, VersionCompare compare2, int version2) 24 | { 25 | Compare = compare; 26 | Version = version; 27 | Compare2 = compare2; 28 | Version2 = version2; 29 | } 30 | 31 | public bool IsValid(int v) 32 | { 33 | if (Version2 != 0) 34 | { 35 | return CompareCheck(this.Compare, this.Version, v) && 36 | CompareCheck(this.Compare2, this.Version2, v); 37 | } 38 | return CompareCheck(this.Compare, this.Version, v); 39 | } 40 | 41 | private bool CompareCheck(VersionCompare c, int dst, int src) 42 | { 43 | switch (c) 44 | { 45 | case VersionCompare.Greater: return src > dst; 46 | case VersionCompare.GreaterOrEqual: return src >= dst; 47 | case VersionCompare.Less: return src < dst; 48 | case VersionCompare.Equals: return src == dst; 49 | } 50 | return true; 51 | } 52 | } 53 | 54 | public enum VersionCompare 55 | { 56 | Less, 57 | LessOrEqual, 58 | GreaterOrEqual, 59 | Greater, 60 | Equals, 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /EffectLibrary/Tools/PtclFileCreator.cs: -------------------------------------------------------------------------------- 1 | using BfresLibrary; 2 | using ShaderLibrary; 3 | using Newtonsoft.Json; 4 | using Syroot.NintenTools.NSW.Bntx; 5 | using Syroot.NintenTools.NSW.Bntx.GFX; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.IO; 12 | using EffectLibrary.EFT2; 13 | using System.Reflection.PortableExecutable; 14 | 15 | namespace EffectLibrary.Tools 16 | { 17 | public class PtclFileCreator 18 | { 19 | public static PtclFile FromFolder(PtclFile ptcl, string folder) 20 | { 21 | string header_info = Path.Combine(folder, "PtclHeader.txt"); 22 | if (File.Exists(header_info)) 23 | { 24 | var info = JsonConvert.DeserializeObject(File.ReadAllText(header_info)); 25 | ptcl.Header = info.Header; 26 | ptcl.Name = info.Name; 27 | } 28 | 29 | var compute_shaders = ptcl.Shaders.ComputeShader.BnshFile.Variations.ToArray(); 30 | 31 | ptcl.EmitterList.EmitterSets.Clear(); 32 | ptcl.Shaders.BnshFile.Variations.Clear(); 33 | ptcl.Textures.BntxFile.Textures.Clear(); 34 | ptcl.Textures.BntxFile.TextureDict.Clear(); 35 | ptcl.Textures.TexDescTable.Descriptors.Clear(); 36 | ptcl.Primitives.ResFile?.Models.Clear(); 37 | ptcl.Primitives.PrimDescTable.Descriptors.Clear(); 38 | ptcl.Shaders.ComputeShader.BnshFile.Variations.Clear(); 39 | 40 | 41 | var emitter_folders = Directory.GetDirectories(folder); 42 | 43 | //order load 44 | { 45 | string path = Path.Combine(folder, "EmitterSetInfo.txt"); 46 | if (File.Exists(path)) 47 | { 48 | var info = JsonConvert.DeserializeObject(File.ReadAllText(path)); 49 | emitter_folders = emitter_folders.OrderBy( 50 | x => info.Order.IndexOf(new DirectoryInfo(x).Name)).ToArray(); 51 | } 52 | } 53 | 54 | //each entry is an emitter set 55 | foreach (var dir in emitter_folders) 56 | { 57 | EmitterSet emitterSet = new EmitterSet(); 58 | emitterSet.Name = new DirectoryInfo(dir).Name; 59 | ptcl.EmitterList.EmitterSets.Add(emitterSet); 60 | 61 | var folders = Directory.GetDirectories(dir); 62 | 63 | //order load 64 | { 65 | string path = Path.Combine(dir, "EmitterOrder.txt"); 66 | if (File.Exists(path)) 67 | { 68 | var info = JsonConvert.DeserializeObject(File.ReadAllText(path)); 69 | folders = folders.OrderBy( 70 | x => info.Order.IndexOf(new DirectoryInfo(x).Name)).ToArray(); 71 | } 72 | } 73 | 74 | foreach (var d in folders) 75 | emitterSet.Emitters.Add(LoadEmitter(ptcl, emitterSet, d)); 76 | 77 | emitterSet.Emitters = emitterSet.Emitters.OrderBy(x => x.Data.Order).ToList(); 78 | } 79 | 80 | //ptcl files can have a compute shader present but no references, add it anyways to be accurate 81 | if (compute_shaders.Length == 1 && ptcl.Shaders.ComputeShader.BnshFile.Variations.Count == 0) 82 | { 83 | ptcl.Shaders.ComputeShader.BnshFile.Variations.AddRange(compute_shaders); 84 | } 85 | 86 | return ptcl; 87 | } 88 | 89 | private static Emitter LoadEmitter(PtclFile ptcl, EmitterSet emitterSet, string dir) 90 | { 91 | Emitter emitter = new Emitter(emitterSet); 92 | emitter.Name = new DirectoryInfo(dir).Name; 93 | 94 | var jsonsettings = new JsonSerializerSettings 95 | { 96 | NullValueHandling = NullValueHandling.Ignore, 97 | Converters = new List() 98 | { 99 | new Newtonsoft.Json.Converters.StringEnumConverter(), 100 | }, 101 | }; 102 | emitter.Data = JsonConvert.DeserializeObject(File.ReadAllText(Path.Combine(dir, "EmitterData.json")), jsonsettings); 103 | 104 | foreach (var f in Directory.GetFiles(dir)) 105 | { 106 | if (!f.EndsWith(".bin") && !f.EndsWith(".json")) 107 | continue; 108 | 109 | if (f.Contains("EmitterData.bin")) 110 | { 111 | emitter.BinaryData = File.ReadAllBytes(Path.Combine(dir, "EmitterData.bin")); 112 | } 113 | else if (f.Contains("EmitterData.json")) 114 | { 115 | 116 | } 117 | else 118 | { 119 | string magic = Path.GetFileNameWithoutExtension(f); 120 | 121 | var sect = new EmitterSubSection(magic); 122 | if (magic.StartsWith("EA")) // Emitter anim 123 | sect = new EmitterAnimation(magic); 124 | 125 | sect.Import(f); 126 | emitter.SubSections.Add(sect); 127 | } 128 | } 129 | 130 | var section_order = new string[] { "FCOV", "CSDP", "CUDP", "CADP", "EAA0", "EAA1", "EATR", }.ToList(); 131 | 132 | emitter.SubSections = emitter.SubSections.OrderBy(x => section_order.IndexOf(x.Header.Magic)).ToList(); 133 | 134 | foreach (var f in Directory.GetFiles(dir)) 135 | { 136 | if (!f.EndsWith(".bntx")) 137 | continue; 138 | 139 | ulong id = ulong.Parse(Path.GetFileNameWithoutExtension(f)); 140 | 141 | BntxFile bntx = new BntxFile(f); 142 | 143 | ptcl.Textures.AddTexture(id, bntx.Textures[0]); 144 | } 145 | 146 | foreach (var f in Directory.GetFiles(dir)) 147 | { 148 | if (!f.EndsWith(".bfres")) 149 | continue; 150 | 151 | ulong id = ulong.Parse(Path.GetFileNameWithoutExtension(f)); 152 | 153 | ResFile resFile = new ResFile(f); 154 | 155 | ptcl.Primitives.AddPrimitive(id, resFile.Models[0]); 156 | } 157 | 158 | emitter.Data.ShaderReferences.ShaderIndex = ptcl.Shaders.BnshFile.Variations.Count; 159 | 160 | Console.WriteLine($"{emitterSet.Name} {emitter.Name} {emitter.Data.ShaderReferences.ShaderIndex}"); 161 | 162 | string shader_path = Path.Combine(dir, "Shader.bnsh"); 163 | string user_shader1_path = Path.Combine(dir, "UserShader1.bnsh"); 164 | string user_shader2_path = Path.Combine(dir, "UserShader2.bnsh"); 165 | string compute_shader_path = Path.Combine(dir, "ComputeShader.bnsh"); 166 | 167 | if (File.Exists(shader_path)) 168 | { 169 | var shader = new BnshFile(shader_path); 170 | ptcl.Shaders.BnshFile.Variations.Add(shader.Variations[0]); 171 | } 172 | else 173 | throw new Exception($"No shader present for emitter!"); 174 | 175 | if (File.Exists(user_shader1_path)) 176 | { 177 | emitter.Data.ShaderReferences.UserShaderIndex1 = ptcl.Shaders.BnshFile.Variations.Count; 178 | 179 | var shader = new BnshFile(user_shader1_path); 180 | ptcl.Shaders.BnshFile.Variations.Add(shader.Variations[0]); 181 | } 182 | 183 | if (File.Exists(user_shader2_path)) 184 | { 185 | emitter.Data.ShaderReferences.UserShaderIndex2 = ptcl.Shaders.BnshFile.Variations.Count; 186 | 187 | var shader = new BnshFile(user_shader2_path); 188 | ptcl.Shaders.BnshFile.Variations.Add(shader.Variations[0]); 189 | } 190 | 191 | if (File.Exists(compute_shader_path)) 192 | { 193 | emitter.Data.ShaderReferences.ComputeShaderIndex = ptcl.Shaders.ComputeShader.BnshFile.Variations.Count; 194 | 195 | var shader = new BnshFile(compute_shader_path); 196 | ptcl.Shaders.ComputeShader.BnshFile.Variations.Add(shader.Variations[0]); 197 | } 198 | 199 | //children 200 | foreach (var d in Directory.GetDirectories(dir)) 201 | emitter.Children.Add(LoadEmitter(ptcl, emitterSet, d)); 202 | 203 | emitter.Children = emitter.Children.OrderBy(x => x.Data.Order).ToList(); 204 | 205 | return emitter; 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /EffectLibrary/Tools/PtclFileDumper.cs: -------------------------------------------------------------------------------- 1 | using BfresLibrary; 2 | using EffectLibrary.EFT2; 3 | using Newtonsoft.Json; 4 | using Syroot.NintenTools.NSW.Bntx; 5 | using Syroot.NintenTools.NSW.Bntx.GFX; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace EffectLibrary.Tools 13 | { 14 | public class PtclFileDumper 15 | { 16 | public static void DumpAll(PtclFile ptcl, string folder) 17 | { 18 | if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); 19 | 20 | string ptcl_base = Path.Combine(folder, "Base.ptcl"); 21 | ptcl.Save(ptcl_base); 22 | 23 | HeaderInfo header = new HeaderInfo() { Header = ptcl.Header, Name = ptcl.Name, }; 24 | File.WriteAllText(Path.Combine(folder, "PtclHeader.txt"), JsonConvert.SerializeObject(header, Formatting.Indented)); 25 | 26 | foreach (var emitterSet in ptcl.EmitterList.EmitterSets) 27 | { 28 | DumpEmitterSet(emitterSet, folder); 29 | } 30 | 31 | EmitterSetInfo info = new EmitterSetInfo(); 32 | foreach (var emitterSet in ptcl.EmitterList.EmitterSets) 33 | info.Order.Add(emitterSet.Name); 34 | 35 | File.WriteAllText(Path.Combine(folder, "EmitterSetInfo.txt"), JsonConvert.SerializeObject(info, Formatting.Indented)); 36 | } 37 | 38 | public static void DumpEmitterSet(EmitterSet emitterSet, string folder) 39 | { 40 | string dir = Path.Combine(folder, emitterSet.Name); 41 | 42 | foreach (var emitter in emitterSet.Emitters) 43 | DumpEmitter(emitter, dir); 44 | 45 | EmitterSetInfo info = new EmitterSetInfo(); 46 | foreach (var emitter in emitterSet.Emitters) 47 | info.Order.Add(emitter.Name); 48 | 49 | File.WriteAllText(Path.Combine(dir, "EmitterOrder.txt"), JsonConvert.SerializeObject(info, Formatting.Indented)); 50 | } 51 | 52 | public static void DumpEmitter(Emitter emitter, string folder) 53 | { 54 | string dir = Path.Combine(folder, emitter.Name); 55 | 56 | if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); 57 | 58 | //Dump textures, shaders and models 59 | var bntx = emitter.PtclHeader.Textures.BntxFile; 60 | var bnsh = emitter.PtclHeader.Shaders.BnshFile; 61 | var bfres = emitter.PtclHeader.Primitives.ResFile; 62 | 63 | var model = emitter.GetModelBinary(); 64 | var model_volume = emitter.GetVolumeModelBinary(); 65 | var model_extra = emitter.GetModelExtraBinary(); 66 | 67 | var shader = emitter.GetShaderBinary(); 68 | var shader_compute = emitter.GetComputeShaderBinary(); 69 | var shader_user1 = emitter.GetUser1ShaderBinary(); 70 | var shader_user2 = emitter.GetUser2ShaderBinary(); 71 | 72 | if (shader != null) shader.Export(Path.Combine(dir, $"Shader.bnsh")); 73 | if (shader_compute != null) shader_compute.Export(Path.Combine(dir, $"ComputeShader.bnsh")); 74 | if (shader_user1 != null) shader_user1.Export(Path.Combine(dir, $"UserShader1.bnsh")); 75 | if (shader_user2 != null) shader_user2.Export(Path.Combine(dir, $"UserShader2.bnsh")); 76 | 77 | void DumpModel(string filePath) 78 | { 79 | ResFile resFile = new ResFile() 80 | { 81 | IsPlatformSwitch = true, 82 | VersionMajor = 0, VersionMajor2 = 5, 83 | VersionMinor = 0, VersionMinor2 = 3, 84 | Alignment = 0xC, 85 | Name = model.Name, 86 | ByteOrder = Syroot.BinaryData.ByteOrder.LittleEndian, 87 | }; 88 | resFile.Models.Add(model.Name, model); 89 | resFile.Save(filePath); 90 | } 91 | 92 | if (model != null) 93 | DumpModel(Path.Combine(dir, $"{emitter.Data.ParticleData.PrimitiveID}.bfres")); 94 | if (model_volume != null) 95 | DumpModel(Path.Combine(dir, $"{emitter.Data.ShapeInfo.PrimitiveIndex}.bfres")); 96 | if (model_extra != null) 97 | DumpModel(Path.Combine(dir, $"{emitter.Data.ParticleData.PrimitiveExID}.bfres")); 98 | 99 | void DumpTexures(string filePath, Texture tex) 100 | { 101 | var bntx = new BntxFile(); 102 | bntx.Target = new char[] { 'N', 'X', ' ', ' ' }; 103 | bntx.Name = tex.Name; 104 | bntx.Alignment = 0xC; 105 | bntx.TargetAddressSize = 0x40; 106 | bntx.VersionMajor = 0; 107 | bntx.VersionMajor2 = 4; 108 | bntx.VersionMinor = 0; 109 | bntx.VersionMinor2 = 0; 110 | bntx.Textures = new List(); 111 | bntx.TextureDict = new Syroot.NintenTools.NSW.Bntx.ResDict(); 112 | bntx.RelocationTable = new RelocationTable(); 113 | bntx.Flag = 0; 114 | 115 | bntx.TextureDict = new Syroot.NintenTools.NSW.Bntx.ResDict(); 116 | bntx.Textures = new List(); 117 | 118 | bntx.Textures.Add(tex); 119 | bntx.TextureDict.Add(tex.Name); 120 | bntx.Save(filePath); 121 | } 122 | 123 | int idx = 0; 124 | foreach (var sampler in emitter.Data.GetSamplers()) 125 | { 126 | var texture = emitter.GetTextureBinary(sampler); 127 | if (texture == null) 128 | continue; 129 | 130 | DumpTexures(Path.Combine(dir, $"{sampler.TextureID}.bntx"), texture); 131 | idx++; 132 | } 133 | 134 | File.WriteAllBytes(Path.Combine(dir, "EmitterData.bin"), emitter.BinaryData); 135 | 136 | var jsonsettings = new JsonSerializerSettings 137 | { 138 | NullValueHandling = NullValueHandling.Ignore, 139 | Converters = new List() 140 | { 141 | new Newtonsoft.Json.Converters.StringEnumConverter(), 142 | }, 143 | }; 144 | string json = JsonConvert.SerializeObject(emitter.Data, Formatting.Indented, jsonsettings); 145 | 146 | File.WriteAllText(Path.Combine(dir, "EmitterData.json"), json); 147 | 148 | foreach (var sub in emitter.SubSections) 149 | sub.Export(Path.Combine(dir, $"{sub.Header.Magic}.bin")); 150 | 151 | foreach (var child in emitter.Children) 152 | DumpEmitter(child, dir); 153 | } 154 | 155 | public class HeaderInfo 156 | { 157 | public BinaryHeader Header; 158 | public string Name; 159 | } 160 | 161 | public class EmitterSetInfo 162 | { 163 | public List Order = new List(); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 KillzXGaming 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EffectLibrary 2 | A library for loading and saving particle effect files 3 | 4 | Credits 5 | 6 | [eff_lib]( https://github.com/ultimate-research/eff_lib/tree/main) for helping me figure out the eff header 7 | --------------------------------------------------------------------------------