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