diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..f30d000
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,8 @@
+root = true
+
+[*.cs]
+indent_style = space
+indent_size = 4
+insert_final_newline = true
+trim_trailing_whitespace = true
+end_of_line = lf
diff --git a/.gitignore b/.gitignore
index ae4e793..bcf8194 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ LanternExtractor/bin/
*.DS_Store
packages/
.vs/
+.vscode/
diff --git a/LanternExtractor/App.config b/LanternExtractor/App.config
deleted file mode 100644
index 88fa402..0000000
--- a/LanternExtractor/App.config
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/LanternExtractor/ClientData/musictracks.txt b/LanternExtractor/ClientData/musictracks.txt
deleted file mode 100644
index 196c1c3..0000000
--- a/LanternExtractor/ClientData/musictracks.txt
+++ /dev/null
@@ -1,595 +0,0 @@
-# Lantern Music Tracks
-
-#airplane
-akanon-2
-airplane_0
-entrance_fanfare-1
-arpeggiated_runs
-airplane_1
-heroism-1
-
-#akanon
-akanon
-gfaydark-1
-gfaydark-2
-felwithe_2
-felwithe_0
-felwithe_1
-arena
-gypsies
-
-#befallen
-eerie_0
-eerie_1
-eerie_2
-eerie_3
-eerie_4
-eerie_5
-eerie_6
-eerie_7
-eerie_8
-eerie_9
-eerie_10
-eerie_11
-
-#blackburrow
-eerie_0
-eerie_1
-eerie_2
-eerie_3
-eerie_4
-eerie_5
-eerie_6
-eerie_7
-eerie_8
-eerie_9
-eerie_10
-eerie_11
-
-#butcher
-entrance_fanfare
-entrance_fanfare_1
-
-#cauldron
-entrance_fanfare
-entrance_fanfare_1
-entrance_fanfare_1-1
-entrance_fanfare-1
-sea
-
-#cobaltscar
-cobaltscar
-underwater
-
-#crushbone
-gfaydark-4
-airplane_0
-entrance_fanfare-1
-eerie_3
-arpeggiated_runs
-neriak_0-1
-
-#crystal
-erudsxing_0
-gfaydark-1
-cobaltscar
-gfaydark
-
-#damage
-damage_0
-damage_1
-damage_2
-damage_3
-damage_4
-
-#damage1
-damage_0
-damage_1
-damage_2
-damage_3
-damage_4
-
-#damage2
-damage_0
-damage_1
-damage_2
-damage_3
-damage_4
-
-#eastkarana
-cascade
-eastkarana
-
-#eerie
-eerie_0
-eerie_1
-eerie_2
-eerie_3
-eerie_4
-eerie_5
-eerie_6
-eerie_7
-eerie_8
-eerie_9
-eerie_10
-eerie_11
-
-#erudnext
-felwithe_0
-cascade
-karana_river
-entrance_fanfare_1-1
-bard
-freportn
-karana_river-1
-brass_harmonies
-
-#eudnint
-cascade
-karana_river
-entrance_fanfare_1-1
-bard
-freportn
-karana_river-1
-brass_harmonies
-felwithe_1
-
-#erudsxing
-erudsxing_0
-erudsxing_1
-
-#fearplane
-eerie_8
-eerie_9
-eerie_0
-eerie_1
-eerie_2
-eerie_3
-eerie_4
-eerie_5
-eerie_7
-
-#felwithea
-felwithe_0
-felwithe_1
-felwithe_2
-
-#freporte
-freeport
-lionsmane
-fishsale
-cottage
-gypsies
-death_variant
-eerie_4
-eerie_5
-
-#freportn
-harprun
-gypsies-1
-gypsies
-eqtheme
-arena
-brass_harmonies
-akanon-1
-lionsmane
-fishsale
-bard
-entrance_fanfare_1-1
-cottage
-freportn
-nightchords
-string_fanfare
-brass_fanfare
-
-#freportw
-heroism
-hogcaller
-militia
-arena
-entrance_fanfare_1-1
-eastkarana
-karana_river-1
-
-#frozenshadow
-eerie_7
-eerie_0
-lavastorm-1
-templeoflife
-qeynos_0-1
-gfaydark
-
-#gfaydark
-entrance_fanfare
-entrance_fanfare_1
-entrance_fanfare_1-1
-entrance_fanfare-1
-gfaydark
-eerie_9
-
-#gl
-attack_0
-attack_1
-attack_2
-death
-airplane_0-1
-gl_5
-gl_6
-crouching
-underwater
-gl_9
-gl_10
-gl_11
-gl_12
-bard_intro
-bard_main
-gl_15
-gl_16
-gl_17
-gl_18
-gl_19
-sea
-merchant
-gfaydark-5
-guildmaster
-
-#grobb
-eerie_0
-eerie_1
-eerie_2
-eerie_3
-eerie_4
-eerie_5
-eerie_6
-eerie_7
-eerie_8
-eerie_9
-eerie_10
-eerie_11
-
-#guktop
-eerie_0
-eerie_1
-eerie_5
-eerie_7
-arpeggiated_runs
-neriak_0-1
-eerie_3
-maidensfancy
-hogcaller
-entrance_fanfare_1
-
-#halas
-felwithe_0
-felwithe_1
-felwithe_2
-arena
-hogcaller
-maidensfancy
-lionsmane
-fishsale
-cottage
-eerie_4
-
-#hateplane
-felwithe_0
-felwithe_2
-felwithe_1
-arpeggiated_runs
-bard-2
-neriak_0-1
-
-#innothule
-entrance_fanfare
-entrance_fanfare_1
-eerie_3
-eerie_2
-
-#kael
-kael_0
-kael_1
-attack_1
-arena
-
-#kaladima
-entrance_fanfare_1
-arena
-nightchords
-cascade
-hogcaller
-lionsmane
-freportn
-heroism
-brass_harmonies-1
-
-#lavastorm
-lavastorm
-eerie_1
-
-#lfaydark
-cascade
-gfaydark-2
-entrance_fanfare_1-1
-gypsies-1
-eerie_5
-militia
-
-#mistmoore
-cascade
-eastkarana
-eerie_1
-entrance_fanfare
-felwithe_1
-felwithe_2
-felwithe_0
-neriak_0-1
-karana_river
-bard-2
-brass_harmonies
-string_fanfare
-freportn-1
-nightchords
-gfaydark-3
-heroism-1
-
-#najena
-eerie_0
-eerie_1
-eerie_2
-eerie_3
-eerie_4
-eerie_5
-eerie_6
-eerie_7
-eerie_8
-eerie_9
-eerie_10
-eerie_11
-
-#nektulos
-lavastorm-1
-
-#neriaka
-eerie_0
-eerie_1
-eerie_2
-eerie_3
-eerie_4
-eerie_5
-eerie_6
-eerie_7
-eerie_8
-eerie_9
-eerie_10
-eerie_11
-neriak_0
-neriak_1
-neriak_2
-arpeggiated_runs
-fishsale
-maidensfancy
-
-#neriakb
-eerie_0
-eerie_1
-eerie_2
-eerie_3
-eerie_4
-eerie_5
-eerie_6
-eerie_7
-eerie_8
-eerie_9
-eerie_10
-eerie_11
-neriak_0
-neriak_1
-neriak_2
-arpeggiated_runs
-fishsale
-maidensfancy
-
-#neriakc
-eerie_0
-eerie_1
-eerie_2
-eerie_3
-eerie_4
-eerie_5
-eerie_6
-eerie_7
-eerie_8
-eerie_9
-eerie_10
-eerie_11
-neriak_0
-neriak_1
-neriak_2
-arpeggiated_runs
-fishsale
-maidensfancy
-
-#northkarana
-cascade
-eastkarana
-
-#nro
-nro
-entrance_fanfare_1
-freeport
-gypsies
-eerie_7
-
-#opener
-opener_0
-
-#opener2
-eastkarana
-character_select
-
-#opener3
-opener_3
-character_select
-
-#opener4
-eqtheme
-character_select
-
-#paw
-eerie_0
-eerie_1
-crouching
-
-#pickchar
-character_select-1
-
-#qcat
-eerie_0
-eerie_1
-crouching
-
-#qey2hh1
-cottage
-maidensfancy
-karana_river
-
-#qeynos
-arena
-qeynos_gates
-eerie_0
-eerie_1
-templeoflife
-bard
-eqtheme
-gypsies
-lionsmane
-fishsale
-cottage
-freeport
-qeynos_0
-
-#qeynos2
-arena
-qeynos_gates
-eerie_0
-eerie_1
-templeoflife
-bard-1
-
-#qeytoqrg
-arena
-qeytoqrg
-eerie_0
-eerie_1
-templeoflife
-cottage
-maidensfancy
-entrance_fanfare
-
-#qrg
-arena
-qeytoqrg
-templeoflife
-qeynos_gates_brass
-
-#rathemtn
-cascade
-eastkarana
-gypsies-2
-entrance_fanfare_1
-entrance_fanfare_1-1
-eerie_2
-eerie_3
-
-#rivervale
-rivervale
-
-#runnyeye
-eerie_0
-eerie_1
-crouching
-
-#skyshrine
-gl_12
-bard_intro
-skyshrine
-beethoven6
-nightchords
-sea
-gfaydark
-gfaydark-2
-
-#soldungb
-lavastorm
-eerie_10
-eerie_11
-eerie_0
-eerie_1
-eerie_2
-eerie_3
-eerie_6-1
-
-#southkarana
-cascade
-eastkarana
-eerie_2
-entrance_fanfare
-maidensfancy
-akanon-1
-
-#steamfont
-gfaydark-4
-entrance_fanfare
-entrance_fanfare_1-1
-akanon-1
-lavastorm
-eerie_3
-eerie_5
-
-#templeveeshan
-templeveeshan
-gfaydark-5
-attack_1
-attack_0
-
-#thurgadina
-templeveeshan
-attack_1
-attack_0
-thurgadina_0
-templeoflife
-thurgadina_1
-
-#thurgadinb
-thurgadina_1
-thurgadinb
-
-#tox
-entrance_fanfare_1
-karana_river
-gypsies-1
-cascade
-karana_river-1
-
-#unrest
-eastkarana
-eerie_2
-eerie_1
-entrance_fanfare
-felwithe_1
-felwithe_2
-neriak_0-1
-karana_river
-brass_harmonies
-string_fanfare
-freportn-1
-gfaydark-3
-heroism-1
-
-#velketor
-velketor_0
-velketor_1
-
-#wakening
-wakening
-gfaydark
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Archive/ArchiveBase.cs b/LanternExtractor/EQ/Archive/ArchiveBase.cs
new file mode 100644
index 0000000..145ea74
--- /dev/null
+++ b/LanternExtractor/EQ/Archive/ArchiveBase.cs
@@ -0,0 +1,68 @@
+using System.Collections.Generic;
+using System.IO;
+using LanternExtractor.Infrastructure;
+using LanternExtractor.Infrastructure.Logger;
+
+namespace LanternExtractor.EQ.Archive
+{
+ public abstract class ArchiveBase
+ {
+ public string FilePath { get; }
+ public string FileName { get; }
+ protected List Files = new List();
+ protected Dictionary FileNameReference = new Dictionary();
+ protected ILogger Logger;
+ public bool IsWldArchive { get; set; }
+ public Dictionary FilenameChanges = new Dictionary();
+
+ protected ArchiveBase(string filePath, ILogger logger)
+ {
+ FilePath = filePath;
+ FileName = Path.GetFileName(filePath);
+ Logger = logger;
+ }
+
+ public abstract bool Initialize();
+
+ public ArchiveFile GetFile(string fileName)
+ {
+ return !FileNameReference.ContainsKey(fileName) ? null : FileNameReference[fileName];
+ }
+
+ public ArchiveFile GetFile(int index)
+ {
+ if (index < 0 || index >= Files.Count)
+ {
+ return null;
+ }
+
+ return Files[index];
+ }
+
+ public ArchiveFile[] GetAllFiles()
+ {
+ return Files.ToArray();
+ }
+
+ public void WriteAllFiles(string folder)
+ {
+ foreach (var file in Files)
+ {
+ FileWriter.WriteBytesToDisk(file.Bytes, folder, file.Name);
+ }
+ }
+
+ public void RenameFile(string originalName, string newName)
+ {
+ if (!FileNameReference.ContainsKey(originalName))
+ {
+ return;
+ }
+
+ var file = FileNameReference[originalName];
+ FileNameReference.Remove(originalName);
+ file.Name = newName;
+ FileNameReference[newName] = file;
+ }
+ }
+}
diff --git a/LanternExtractor/EQ/Archive/ArchiveFactory.cs b/LanternExtractor/EQ/Archive/ArchiveFactory.cs
new file mode 100644
index 0000000..9bf0b9f
--- /dev/null
+++ b/LanternExtractor/EQ/Archive/ArchiveFactory.cs
@@ -0,0 +1,75 @@
+using System;
+using System.IO;
+using LanternExtractor.Infrastructure.Logger;
+
+namespace LanternExtractor.EQ.Archive
+{
+ public class ArchiveFactory
+ {
+ private const uint T3dMagic = 0xffff3d02;
+ private const uint PfsMagic = 0x20534650;
+
+ public static ArchiveBase GetArchive(string filePath, ILogger logger)
+ {
+ if (!File.Exists(filePath))
+ {
+ // Skip detection and let the archive Initialize fail.
+ return new NullArchive(filePath, logger);
+ }
+
+ var archiveType = GetArchiveTypeFromMagic(filePath);
+ if (archiveType == ArchiveType.Unknown)
+ {
+ archiveType = GetArchiveTypeFromFilename(filePath);
+ }
+
+ switch (archiveType)
+ {
+ case ArchiveType.Pfs:
+ return new PfsArchive(filePath, logger);
+ case ArchiveType.T3d:
+ return new T3dArchive(filePath, logger);
+ default:
+ throw new ArgumentException("Unknown archive type", "filePath");
+ }
+ }
+
+ private static ArchiveType GetArchiveTypeFromMagic(string filePath)
+ {
+ uint data;
+ using (BinaryReader br = new BinaryReader(File.OpenRead(filePath)))
+ {
+ data = br.ReadUInt32();
+ if (data == T3dMagic)
+ {
+ return ArchiveType.T3d;
+ }
+
+ data = br.ReadUInt32();
+ if (data == PfsMagic)
+ {
+ return ArchiveType.Pfs;
+ }
+ }
+
+ return ArchiveType.Unknown;
+ }
+
+ private static ArchiveType GetArchiveTypeFromFilename(string filePath)
+ {
+ string archiveExt = Path.GetExtension(filePath)?.ToLower();
+
+ switch (archiveExt)
+ {
+ case LanternStrings.T3dFormatExtension:
+ return ArchiveType.T3d;
+ case LanternStrings.S3dFormatExtension:
+ case LanternStrings.PfsFormatExtension:
+ case LanternStrings.PakFormatExtension:
+ return ArchiveType.Pfs;
+ }
+
+ return ArchiveType.Unknown;
+ }
+ }
+}
diff --git a/LanternExtractor/EQ/Pfs/PfsFile.cs b/LanternExtractor/EQ/Archive/ArchiveFile.cs
similarity index 72%
rename from LanternExtractor/EQ/Pfs/PfsFile.cs
rename to LanternExtractor/EQ/Archive/ArchiveFile.cs
index 3cbe3fd..528ded7 100644
--- a/LanternExtractor/EQ/Pfs/PfsFile.cs
+++ b/LanternExtractor/EQ/Archive/ArchiveFile.cs
@@ -1,15 +1,10 @@
-namespace LanternExtractor.EQ.Pfs
+namespace LanternExtractor.EQ.Archive
{
///
/// This class represents a single file in the archive
///
- public class PfsFile
+ public abstract class ArchiveFile
{
- ///
- /// The CRC of the PFSFile
- ///
- public uint Crc { get; }
-
///
/// The inflated size of the file in bytes
///
@@ -29,13 +24,12 @@ public class PfsFile
/// The name of the file
///
public string Name { get; set; }
-
- public PfsFile(uint crc, uint size, uint offset, byte[] bytes)
+
+ public ArchiveFile(uint size, uint offset, byte[] bytes)
{
- Crc = crc;
Size = size;
Offset = offset;
Bytes = bytes;
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Archive/ArchiveType.cs b/LanternExtractor/EQ/Archive/ArchiveType.cs
new file mode 100644
index 0000000..b6bfe40
--- /dev/null
+++ b/LanternExtractor/EQ/Archive/ArchiveType.cs
@@ -0,0 +1,9 @@
+namespace LanternExtractor.EQ.Archive
+{
+ public enum ArchiveType
+ {
+ Unknown = 0,
+ Pfs = 1,
+ T3d = 2
+ }
+}
diff --git a/LanternExtractor/EQ/Archive/NullArchive.cs b/LanternExtractor/EQ/Archive/NullArchive.cs
new file mode 100644
index 0000000..d7a9e34
--- /dev/null
+++ b/LanternExtractor/EQ/Archive/NullArchive.cs
@@ -0,0 +1,16 @@
+using LanternExtractor.Infrastructure.Logger;
+
+namespace LanternExtractor.EQ.Archive
+{
+ public class NullArchive : ArchiveBase
+ {
+ public NullArchive(string filePath, ILogger logger) : base(filePath, logger)
+ {
+ }
+
+ public override bool Initialize()
+ {
+ return false;
+ }
+ }
+}
diff --git a/LanternExtractor/EQ/Pfs/PfsArchive.cs b/LanternExtractor/EQ/Archive/PfsArchive.cs
similarity index 64%
rename from LanternExtractor/EQ/Pfs/PfsArchive.cs
rename to LanternExtractor/EQ/Archive/PfsArchive.cs
index fda1b24..3881792 100644
--- a/LanternExtractor/EQ/Pfs/PfsArchive.cs
+++ b/LanternExtractor/EQ/Archive/PfsArchive.cs
@@ -2,41 +2,26 @@
using System.Collections.Generic;
using System.IO;
using Ionic.Zlib;
-using LanternExtractor.Infrastructure;
using LanternExtractor.Infrastructure.Logger;
-namespace LanternExtractor.EQ.Pfs
+namespace LanternExtractor.EQ.Archive
{
///
/// Loads and can extract files in the PFS archive
///
- public class PfsArchive
+ public class PfsArchive : ArchiveBase
{
- public string FilePath { get; }
- public string FileName { get; }
-
- private List _files = new List();
- private Dictionary _fileNameReference = new Dictionary();
- private ILogger _logger;
-
- public bool IsWldArchive { get; set; }
-
- public Dictionary FilenameChanges = new Dictionary();
-
- public PfsArchive(string filePath, ILogger logger)
+ public PfsArchive(string filePath, ILogger logger) : base(filePath, logger)
{
- FilePath = filePath;
- FileName = Path.GetFileName(filePath);
- _logger = logger;
}
- public bool Initialize()
+ public override bool Initialize()
{
- _logger.LogInfo("PfsArchive: Started initialization of archive: " + FileName);
+ Logger.LogInfo("PfsArchive: Started initialization of archive: " + FileName);
if (!File.Exists(FilePath))
{
- _logger.LogError("PfsArchive: File does not exist at: " + FilePath);
+ Logger.LogError("PfsArchive: File does not exist at: " + FilePath);
return false;
}
@@ -44,6 +29,8 @@ public bool Initialize()
{
var reader = new BinaryReader(fileStream);
int directoryOffset = reader.ReadInt32();
+ var pfsMagic = reader.ReadUInt32();
+ var pfsVersion = reader.ReadInt32();
reader.BaseStream.Position = directoryOffset;
int fileCount = reader.ReadInt32();
@@ -57,7 +44,7 @@ public bool Initialize()
if (offset > reader.BaseStream.Length)
{
- _logger.LogError("PfsArchive: Corrupted PFS length detected!");
+ Logger.LogError("PfsArchive: Corrupted PFS length detected!");
return false;
}
@@ -75,16 +62,16 @@ public bool Initialize()
if (deflatedLength >= reader.BaseStream.Length)
{
- _logger.LogError("PfsArchive: Corrupted file length detected!");
+ Logger.LogError("PfsArchive: Corrupted file length detected!");
return false;
}
byte[] compressedBytes = reader.ReadBytes((int)deflatedLength);
byte[] inflatedBytes;
- if (!InflateBlock(compressedBytes, (int)inflatedLength, out inflatedBytes, _logger))
+ if (!InflateBlock(compressedBytes, (int)inflatedLength, out inflatedBytes, Logger))
{
- _logger.LogError("PfsArchive: Error occured inflating data");
+ Logger.LogError("PfsArchive: Error occured inflating data");
return false;
}
@@ -92,7 +79,9 @@ public bool Initialize()
inflatedSize += inflatedLength;
}
- if (crc == 0x61580AC9)
+ // EQZip saved archives use 0xFFFFFFFFU for filenames
+ // https://github.com/Shendare/EQZip/blob/b181ec7658ea9880984d58271cbab924ab8dd702/EQArchive.cs#L517
+ if (crc == 0x61580AC9 || (crc == 0xFFFFFFFFU && fileNames.Count == 0))
{
var dictionaryStream = new MemoryStream(fileBytes);
var dictionary = new BinaryReader(dictionaryStream);
@@ -110,27 +99,42 @@ public bool Initialize()
continue;
}
- _files.Add(new PfsFile(crc, size, offset, fileBytes));
+ Files.Add(new PfsFile(crc, size, offset, fileBytes));
reader.BaseStream.Position = cachedOffset;
}
// Sort files by offset so we can assign names
- _files.Sort((x, y) => x.Offset.CompareTo(y.Offset));
+ Files.Sort((x, y) => x.Offset.CompareTo(y.Offset));
// Assign file names
- for (int i = 0; i < _files.Count; ++i)
+ for (int i = 0; i < Files.Count; ++i)
{
- _files[i].Name = fileNames[i];
- _fileNameReference[fileNames[i]] = _files[i];
-
- if (!IsWldArchive && fileNames[i].EndsWith(".wld"))
+ switch(pfsVersion)
{
- IsWldArchive = true;
+ case 0x10000:
+ // PFS version 1 files do not appear to contain the filenames
+ if (Files[i] is PfsFile pfsFile)
+ {
+ pfsFile.Name = $"{pfsFile.Crc:X8}.bin";
+ }
+ break;
+ case 0x20000:
+ Files[i].Name = fileNames[i];
+ FileNameReference[fileNames[i]] = Files[i];
+
+ if (!IsWldArchive && fileNames[i].EndsWith(LanternStrings.WldFormatExtension))
+ {
+ IsWldArchive = true;
+ }
+ break;
+ default:
+ Logger.LogError("PfsArchive: Unexpected pfs version: " + FileName);
+ break;
}
}
- _logger.LogInfo("PfsArchive: Finished initialization of archive: " + FileName);
+ Logger.LogInfo("PfsArchive: Finished initialization of archive: " + FileName);
}
return true;
@@ -186,45 +190,5 @@ private static bool InflateBlock(byte[] deflatedBytes, int inflatedSize, out byt
}
}
- public PfsFile GetFile(string fileName)
- {
- return !_fileNameReference.ContainsKey(fileName) ? null : _fileNameReference[fileName];
- }
-
- public PfsFile GetFile(int index)
- {
- if (index < 0 || index >= _files.Count)
- {
- return null;
- }
-
- return _files[index];
- }
-
- public PfsFile[] GetAllFiles()
- {
- return _files.ToArray();
- }
-
- public void WriteAllFiles(string folder)
- {
- foreach (var file in _files)
- {
- FileWriter.WriteBytesToDisk(file.Bytes, folder, file.Name);
- }
- }
-
- public void RenameFile(string originalName, string newName)
- {
- if (!_fileNameReference.ContainsKey(originalName))
- {
- return;
- }
-
- var file = _fileNameReference[originalName];
- _fileNameReference.Remove(originalName);
- file.Name = newName;
- _fileNameReference[newName] = file;
- }
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Archive/PfsFile.cs b/LanternExtractor/EQ/Archive/PfsFile.cs
new file mode 100644
index 0000000..714a626
--- /dev/null
+++ b/LanternExtractor/EQ/Archive/PfsFile.cs
@@ -0,0 +1,18 @@
+namespace LanternExtractor.EQ.Archive
+{
+ ///
+ /// This class represents a single file in the archive
+ ///
+ public class PfsFile : ArchiveFile
+ {
+ ///
+ /// The CRC of the PFSFile
+ ///
+ public uint Crc { get; }
+
+ public PfsFile(uint crc, uint size, uint offset, byte[] bytes) : base(size, offset, bytes)
+ {
+ Crc = crc;
+ }
+ }
+}
diff --git a/LanternExtractor/EQ/Archive/T3dArchive.cs b/LanternExtractor/EQ/Archive/T3dArchive.cs
new file mode 100644
index 0000000..e2143cf
--- /dev/null
+++ b/LanternExtractor/EQ/Archive/T3dArchive.cs
@@ -0,0 +1,90 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using LanternExtractor.Infrastructure;
+using LanternExtractor.Infrastructure.Logger;
+
+namespace LanternExtractor.EQ.Archive
+{
+ ///
+ /// Loads and can extract files in the T3D archive
+ ///
+ public class T3dArchive : ArchiveBase
+ {
+ private static readonly byte[] T3dMagic = new byte[] {0x02, 0x3D, 0xFF, 0xFF};
+ private static readonly byte[] T3dVersion = new byte[] {0x00, 0x57, 0x01, 0x00};
+
+ public T3dArchive(string filePath, ILogger logger) : base(filePath, logger)
+ {
+ }
+
+ public override bool Initialize()
+ {
+ Logger.LogInfo("T3dArchive: Started initialization of archive: " + FileName);
+
+ if (!File.Exists(FilePath))
+ {
+ Logger.LogError("T3dArchive: File does not exist at: " + FilePath);
+ return false;
+ }
+
+ using (var fileStream = new FileStream(FilePath, FileMode.Open, FileAccess.Read))
+ {
+ var reader = new BinaryReader(fileStream);
+
+ var magic = reader.ReadBytes(4);
+ if (!magic.SequenceEqual(T3dMagic))
+ {
+ Logger.LogError("T3dArchive: Incorrect file magic");
+ return false;
+ }
+
+ var version = reader.ReadBytes(4);
+ if (!version.SequenceEqual(T3dVersion))
+ {
+ Logger.LogError("T3dArchive: Incorrect file version");
+ return false;
+ }
+
+ var fileCount = reader.ReadUInt32();
+ var filenamesLength = reader.ReadUInt32();
+
+ var offsetPairs = new List<(uint FileOffset, uint FileNameOffset)>();
+ for (int i = 0; i < fileCount; i++)
+ {
+ var fileNameBaseOffset = (uint) reader.BaseStream.Position;
+ offsetPairs.Add((reader.ReadUInt32(), reader.ReadUInt32() + fileNameBaseOffset));
+ }
+
+ var totalFilesize = reader.ReadUInt64();
+
+ for (int i = 0; i < fileCount - 1; i++)
+ {
+ var fileOffset = offsetPairs[i].FileOffset;
+ var fileNameOffset = offsetPairs[i].FileNameOffset;
+ var nextFileOffset = i == fileCount - 2 ? totalFilesize : offsetPairs[i + 1].FileOffset;
+ uint fileSize = (uint) (nextFileOffset - fileOffset);
+ var fileBytes = new byte[fileSize];
+
+ reader.BaseStream.Position = fileOffset;
+ reader.Read(fileBytes);
+
+ var file = new T3dFile(fileSize, fileOffset, fileBytes);
+
+ reader.BaseStream.Position = fileNameOffset;
+ file.Name = reader.ReadNullTerminatedString().ToLower();
+
+ if (!IsWldArchive && file.Name.EndsWith(LanternStrings.WldFormatExtension))
+ {
+ IsWldArchive = true;
+ }
+
+ Files.Add(file);
+ FileNameReference[file.Name] = file;
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/LanternExtractor/EQ/Archive/T3dFile.cs b/LanternExtractor/EQ/Archive/T3dFile.cs
new file mode 100644
index 0000000..21cf0bd
--- /dev/null
+++ b/LanternExtractor/EQ/Archive/T3dFile.cs
@@ -0,0 +1,12 @@
+namespace LanternExtractor.EQ.Archive
+{
+ ///
+ /// This class represents a single file in the archive
+ ///
+ public class T3dFile : ArchiveFile
+ {
+ public T3dFile(uint size, uint offset, byte[] bytes) : base(size, offset, bytes)
+ {
+ }
+ }
+}
diff --git a/LanternExtractor/EQ/ArchiveExtractor.cs b/LanternExtractor/EQ/ArchiveExtractor.cs
index e9b267a..45166be 100644
--- a/LanternExtractor/EQ/ArchiveExtractor.cs
+++ b/LanternExtractor/EQ/ArchiveExtractor.cs
@@ -1,5 +1,5 @@
using System.IO;
-using LanternExtractor.EQ.Pfs;
+using LanternExtractor.EQ.Archive;
using LanternExtractor.EQ.Sound;
using LanternExtractor.EQ.Wld;
using LanternExtractor.EQ.Wld.Helpers;
@@ -19,33 +19,32 @@ public static void Extract(string path, string rootFolder, ILogger logger, Setti
return;
}
-
+ var archive = ArchiveFactory.GetArchive(path, logger);
string shortName = archiveName.Split('_')[0];
- var s3dArchive = new PfsArchive(path, logger);
- if (!s3dArchive.Initialize())
+ if (!archive.Initialize())
{
- logger.LogError("LanternExtractor: Failed to initialize PFS archive at path: " + path);
+ logger.LogError("LanternExtractor: Failed to initialize archive at path: " + path);
return;
}
if (settings.RawS3dExtract)
{
- s3dArchive.WriteAllFiles(Path.Combine(rootFolder + shortName, archiveName));
+ archive.WriteAllFiles(Path.Combine(rootFolder, archiveName));
return;
}
// For non WLD files, we can just extract their contents
// Used for pure texture archives (e.g. bmpwad.s3d) and sound archives (e.g. snd1.pfs)
- // The difference between this and the raw export is that it will convert images to .png
- if (!s3dArchive.IsWldArchive)
+ // The difference between this and the raw export is that it will convert images to PNG
+ if (!archive.IsWldArchive)
{
- WriteS3dTextures(s3dArchive, rootFolder + shortName, logger);
+ WriteS3dTextures(archive, rootFolder + shortName, logger);
- if (EqFileHelper.IsSoundArchive(archiveName))
+ if (EqFileHelper.IsUsedSoundArchive(archiveName))
{
- var soundFolder = settings.ExportSoundsToSingleFolder ? "sounds" : shortName;
- WriteS3dSounds(s3dArchive, rootFolder + soundFolder, logger);
+ WriteS3dSounds(archive,
+ Path.Combine(rootFolder, settings.ExportSoundsToSingleFolder ? "sounds" : shortName), logger);
}
return;
@@ -53,7 +52,7 @@ public static void Extract(string path, string rootFolder, ILogger logger, Setti
string wldFileName = archiveName + LanternStrings.WldFormatExtension;
- PfsFile wldFileInArchive = s3dArchive.GetFile(wldFileName);
+ var wldFileInArchive = archive.GetFile(wldFileName);
if (wldFileInArchive == null)
{
@@ -63,49 +62,50 @@ public static void Extract(string path, string rootFolder, ILogger logger, Setti
if (EqFileHelper.IsEquipmentArchive(archiveName))
{
- ExtractArchiveEquipment(rootFolder, logger, settings, wldFileInArchive, shortName, s3dArchive);
+ ExtractArchiveEquipment(rootFolder, logger, settings, wldFileInArchive, shortName, archive);
}
else if (EqFileHelper.IsSkyArchive(archiveName))
{
- ExtractArchiveSky(rootFolder, logger, settings, wldFileInArchive, shortName, s3dArchive);
+ ExtractArchiveSky(rootFolder, logger, settings, wldFileInArchive, shortName, archive);
}
- else if (EqFileHelper.IsCharactersArchive(archiveName))
+ else if (EqFileHelper.IsCharacterArchive(archiveName))
{
- ExtractArchiveCharacters(path, rootFolder, logger, settings, archiveName, wldFileInArchive, shortName, s3dArchive);
+ ExtractArchiveCharacters(path, rootFolder, logger, settings, archiveName, wldFileInArchive, shortName,
+ archive);
}
else if (EqFileHelper.IsObjectsArchive(archiveName))
{
- ExtractArchiveObjects(path, rootFolder, logger, settings, wldFileInArchive, shortName, s3dArchive);
+ ExtractArchiveObjects(path, rootFolder, logger, settings, wldFileInArchive, shortName, archive);
}
else
{
- ExtractArchiveZone(path, rootFolder, logger, settings, shortName, wldFileInArchive, s3dArchive);
+ ExtractArchiveZone(path, rootFolder, logger, settings, shortName, wldFileInArchive, archive);
}
MissingTextureFixer.Fix(archiveName);
-
}
private static void ExtractArchiveZone(string path, string rootFolder, ILogger logger, Settings settings,
- string shortName, PfsFile wldFileInArchive, PfsArchive s3dArchive)
+ string shortName, ArchiveFile wldFileInArchive, ArchiveBase archive)
{
// Some Kunark zones have a "_lit" which needs to be injected into the main zone file
- var s3dArchiveLit = new PfsArchive(path.Replace(shortName, shortName + "_lit"), logger);
+ var archiveLit = ArchiveFactory.GetArchive(path.Replace(shortName, shortName + "_lit"), logger);
WldFileZone wldFileLit = null;
- if (s3dArchiveLit.Initialize())
+ if (archiveLit.Initialize())
{
- var litWldFileInArchive = s3dArchiveLit.GetFile(shortName + "_lit.wld");
+ var litWldFileInArchive = archiveLit.GetFile(shortName + "_lit.wld");
wldFileLit = new WldFileZone(litWldFileInArchive, shortName, WldType.Zone,
logger, settings);
wldFileLit.Initialize(rootFolder, false);
- var litWldLightsFileInArchive = s3dArchiveLit.GetFile(shortName + "_lit.wld");
+ var litWldLightsFileInArchive = archiveLit.GetFile(shortName + "_lit.wld");
if (litWldLightsFileInArchive != null)
{
var lightsWldFile =
- new WldFileLights(litWldLightsFileInArchive, shortName, WldType.Lights, logger, settings, wldFileLit);
+ new WldFileLights(litWldLightsFileInArchive, shortName, WldType.Lights, logger, settings,
+ wldFileLit);
lightsWldFile.Initialize(rootFolder);
}
}
@@ -116,15 +116,16 @@ private static void ExtractArchiveZone(string path, string rootFolder, ILogger l
if (settings.ExportZoneWithObjects)
{
wldFile.BasePath = path;
- wldFile.BaseS3DArchive = s3dArchive;
+ wldFile.BaseS3DArchive = archive;
wldFile.WldFileToInject = wldFileLit;
wldFile.RootFolder = rootFolder;
wldFile.ShortName = shortName;
}
+
InitializeWldAndWriteTextures(wldFile, rootFolder, rootFolder + shortName + "/Zone/Textures/",
- s3dArchive, settings, logger);
+ archive, settings, logger);
- PfsFile lightsFileInArchive = s3dArchive.GetFile("lights" + LanternStrings.WldFormatExtension);
+ var lightsFileInArchive = archive.GetFile("lights" + LanternStrings.WldFormatExtension);
if (lightsFileInArchive != null)
{
@@ -133,7 +134,7 @@ private static void ExtractArchiveZone(string path, string rootFolder, ILogger l
lightsWldFile.Initialize(rootFolder);
}
- PfsFile zoneObjectsFileInArchive = s3dArchive.GetFile("objects" + LanternStrings.WldFormatExtension);
+ var zoneObjectsFileInArchive = archive.GetFile("objects" + LanternStrings.WldFormatExtension);
if (zoneObjectsFileInArchive != null)
{
@@ -142,19 +143,19 @@ private static void ExtractArchiveZone(string path, string rootFolder, ILogger l
zoneObjectsWldFile.Initialize(rootFolder);
}
- ExtractSoundData(shortName, rootFolder, settings);
+ ExtractSoundData(shortName, rootFolder, logger, settings);
}
private static void ExtractArchiveObjects(string path, string rootFolder, ILogger logger, Settings settings,
- PfsFile wldFileInArchive, string shortName, PfsArchive s3dArchive)
+ ArchiveFile wldFileInArchive, string shortName, ArchiveBase archive)
{
// Some zones have a "_2_obj" which needs to be injected into the main zone file
- var s3dArchiveObj2 = new PfsArchive(path.Replace(shortName + "_obj", shortName + "_2_obj"), logger);
+ var archiveObj2 = ArchiveFactory.GetArchive(path.Replace(shortName + "_obj", shortName + "_2_obj"), logger);
WldFileZone wldFileObj2 = null;
- if (s3dArchiveObj2.Initialize())
+ if (archiveObj2.Initialize())
{
- var obj2WldFileInArchive = s3dArchiveObj2.GetFile(shortName + "_2_obj.wld");
+ var obj2WldFileInArchive = archiveObj2.GetFile(shortName + "_2_obj.wld");
wldFileObj2 = new WldFileZone(obj2WldFileInArchive, shortName, WldType.Zone,
logger, settings);
wldFileObj2.Initialize(rootFolder, false);
@@ -163,26 +164,26 @@ private static void ExtractArchiveObjects(string path, string rootFolder, ILogge
var wldFile = new WldFileZone(wldFileInArchive, shortName, WldType.Objects, logger, settings, wldFileObj2);
InitializeWldAndWriteTextures(wldFile, rootFolder,
rootFolder + ShortnameHelper.GetCorrectZoneShortname(shortName) + "/Objects/Textures/",
- s3dArchive, settings, logger);
+ archive, settings, logger);
}
private static void ExtractArchiveCharacters(string path, string rootFolder, ILogger logger, Settings settings,
- string archiveName, PfsFile wldFileInArchive, string shortName, PfsArchive s3dArchive)
+ string archiveName, ArchiveFile wldFileInArchive, string shortName, ArchiveBase archive)
{
WldFileCharacters wldFileToInject = null;
// global3_chr contains just animations
if (archiveName.StartsWith("global3_chr"))
{
- var s3dArchive2 = new PfsArchive(path.Replace("global3_chr", "global_chr"), logger);
+ var archive2 = ArchiveFactory.GetArchive(path.Replace("global3_chr", "global_chr"), logger);
- if (!s3dArchive2.Initialize())
+ if (!archive2.Initialize())
{
- logger.LogError("Failed to initialize PFS archive at path: " + path);
+ logger.LogError("Failed to initialize archive at path: " + path);
return;
}
- PfsFile wldFileInArchive2 = s3dArchive2.GetFile("global_chr.wld");
+ var wldFileInArchive2 = archive2.GetFile("global_chr.wld");
wldFileToInject = new WldFileCharacters(wldFileInArchive2, "global_chr", WldType.Characters,
logger, settings);
@@ -198,44 +199,44 @@ private static void ExtractArchiveCharacters(string path, string rootFolder, ILo
: ShortnameHelper.GetCorrectZoneShortname(shortName) + "/Characters/Textures/");
InitializeWldAndWriteTextures(wldFile, rootFolder, exportPath,
- s3dArchive, settings, logger);
+ archive, settings, logger);
}
- private static void ExtractArchiveSky(string rootFolder, ILogger logger, Settings settings, PfsFile wldFileInArchive,
- string shortName, PfsArchive s3dArchive)
+ private static void ExtractArchiveSky(string rootFolder, ILogger logger, Settings settings,
+ ArchiveFile wldFileInArchive, string shortName, ArchiveBase archive)
{
var wldFile = new WldFileZone(wldFileInArchive, shortName, WldType.Sky, logger, settings);
InitializeWldAndWriteTextures(wldFile, rootFolder, rootFolder + shortName + "/Textures/",
- s3dArchive, settings, logger);
+ archive, settings, logger);
}
private static void ExtractArchiveEquipment(string rootFolder, ILogger logger, Settings settings,
- PfsFile wldFileInArchive, string shortName, PfsArchive s3dArchive)
+ ArchiveFile wldFileInArchive, string shortName, ArchiveBase archive)
{
var wldFile = new WldFileEquipment(wldFileInArchive, shortName, WldType.Equipment, logger, settings);
var exportPath = rootFolder +
- (settings.ExportEquipmentToSingleFolder &&
- settings.ModelExportFormat == ModelExportFormat.Intermediate
- ? "equipment/Textures/"
- : shortName + "/Textures/");
+ (settings.ExportEquipmentToSingleFolder &&
+ settings.ModelExportFormat == ModelExportFormat.Intermediate
+ ? "equipment/Textures/"
+ : shortName + "/Textures/");
- InitializeWldAndWriteTextures(wldFile, rootFolder, exportPath, s3dArchive, settings, logger);
+ InitializeWldAndWriteTextures(wldFile, rootFolder, exportPath, archive, settings, logger);
}
private static void InitializeWldAndWriteTextures(WldFile wldFile, string rootFolder, string texturePath,
- PfsArchive s3dArchive, Settings settings, ILogger logger)
+ ArchiveBase archive, Settings settings, ILogger logger)
{
if (settings.ModelExportFormat != ModelExportFormat.GlTF)
{
wldFile.Initialize(rootFolder);
- s3dArchive.FilenameChanges = wldFile.FilenameChanges;
- WriteWldTextures(s3dArchive, wldFile, texturePath, logger);
+ archive.FilenameChanges = wldFile.FilenameChanges;
+ WriteWldTextures(archive, wldFile, texturePath, logger);
}
else // Exporting to GlTF requires that the texture images already be present
{
wldFile.Initialize(rootFolder, false);
- s3dArchive.FilenameChanges = wldFile.FilenameChanges;
- WriteWldTextures(s3dArchive, wldFile, texturePath, logger);
+ archive.FilenameChanges = wldFile.FilenameChanges;
+ WriteWldTextures(archive, wldFile, texturePath, logger);
wldFile.ExportData();
}
}
@@ -245,7 +246,7 @@ private static void InitializeWldAndWriteTextures(WldFile wldFile, string rootFo
///
///
///
- private static void WriteS3dSounds(PfsArchive s3dArchive, string filePath, ILogger logger)
+ private static void WriteS3dSounds(ArchiveBase s3dArchive, string filePath, ILogger logger)
{
var allFiles = s3dArchive.GetAllFiles();
@@ -259,13 +260,13 @@ private static void WriteS3dSounds(PfsArchive s3dArchive, string filePath, ILogg
}
///
- /// Writes textures from the PFS archive to disk, converting them to PNG
+ /// Writes textures from the archive to disk, converting them to PNG
///
- ///
+ ///
///
- private static void WriteS3dTextures(PfsArchive s3dArchive, string filePath, ILogger logger)
+ private static void WriteS3dTextures(ArchiveBase archive, string filePath, ILogger logger)
{
- var allFiles = s3dArchive.GetAllFiles();
+ var allFiles = archive.GetAllFiles();
foreach (var file in allFiles)
{
@@ -277,12 +278,12 @@ private static void WriteS3dTextures(PfsArchive s3dArchive, string filePath, ILo
}
///
- /// Writes textures from the PFS archive to disk, handling masked materials from the WLD
+ /// Writes textures from the archive to disk, handling masked materials from the WLD
///
- ///
+ ///
///
///
- public static void WriteWldTextures(PfsArchive s3dArchive, WldFile wldFile, string zoneName, ILogger logger)
+ public static void WriteWldTextures(ArchiveBase archive, WldFile wldFile, string zoneName, ILogger logger)
{
var allBitmaps = wldFile.GetAllBitmapNames();
var maskedBitmaps = wldFile.GetMaskedBitmaps();
@@ -290,13 +291,13 @@ public static void WriteWldTextures(PfsArchive s3dArchive, WldFile wldFile, stri
foreach (var bitmap in allBitmaps)
{
string filename = bitmap;
- if (s3dArchive.FilenameChanges != null &&
- s3dArchive.FilenameChanges.ContainsKey(Path.GetFileNameWithoutExtension(bitmap)))
+ if (archive.FilenameChanges != null &&
+ archive.FilenameChanges.ContainsKey(Path.GetFileNameWithoutExtension(bitmap)))
{
- filename = s3dArchive.FilenameChanges[Path.GetFileNameWithoutExtension(bitmap)] + ".bmp";
+ filename = archive.FilenameChanges[Path.GetFileNameWithoutExtension(bitmap)] + ".bmp";
}
- var pfsFile = s3dArchive.GetFile(filename);
+ var pfsFile = archive.GetFile(filename);
if (pfsFile == null)
{
@@ -308,15 +309,24 @@ public static void WriteWldTextures(PfsArchive s3dArchive, WldFile wldFile, stri
}
}
- private static void ExtractSoundData(string shortName, string rootFolder, Settings settings)
+ private static void ExtractSoundData(string shortName, string rootFolder, ILogger logger, Settings settings)
{
+ var envAudio = EnvAudio.Instance;
+ var ealFilePath = Path.Combine(settings.EverQuestDirectory, "defaults.dat");
+ if (!envAudio.Load(ealFilePath))
+ {
+ envAudio.Load(Path.ChangeExtension(ealFilePath, ".eal"));
+ }
+
var sounds = new EffSndBnk(settings.EverQuestDirectory + shortName + "_sndbnk" +
LanternStrings.SoundFormatExtension);
sounds.Initialize();
+
var soundEntries =
new EffSounds(
- settings.EverQuestDirectory + shortName + "_sounds" + LanternStrings.SoundFormatExtension, sounds);
- soundEntries.Initialize();
+ settings.EverQuestDirectory + shortName + "_sounds" + LanternStrings.SoundFormatExtension,
+ sounds, envAudio);
+ soundEntries.Initialize(logger);
soundEntries.ExportSoundData(shortName, rootFolder);
}
}
diff --git a/LanternExtractor/EQ/ClientDataCopier.cs b/LanternExtractor/EQ/ClientDataCopier.cs
index dbdb957..39c4488 100644
--- a/LanternExtractor/EQ/ClientDataCopier.cs
+++ b/LanternExtractor/EQ/ClientDataCopier.cs
@@ -6,7 +6,7 @@ namespace LanternExtractor.EQ
{
public static class ClientDataCopier
{
- public const string ClientDataDirectory = "clientdata";
+ private const string ClientDataDirectory = "clientdata";
public static void Copy(string fileName, string rootFolder, ILogger logger, Settings settings)
{
diff --git a/LanternExtractor/EQ/MusicCopier.cs b/LanternExtractor/EQ/MusicCopier.cs
new file mode 100644
index 0000000..013e24c
--- /dev/null
+++ b/LanternExtractor/EQ/MusicCopier.cs
@@ -0,0 +1,45 @@
+using System.IO;
+using System.Linq;
+using LanternExtractor.Infrastructure.Logger;
+
+namespace LanternExtractor.EQ
+{
+ public static class MusicCopier
+ {
+ private const string MusicDirectory = "music";
+
+ public static void Copy(string shortname, ILogger logger, Settings settings)
+ {
+ if (shortname != "music" && shortname != "all")
+ {
+ return;
+ }
+
+ if (!settings.CopyMusic)
+ {
+ return;
+ }
+
+ var xmiFiles = Directory.GetFiles(settings.EverQuestDirectory, "*.*", SearchOption.AllDirectories)
+ .Where(EqFileHelper.IsMusicFile).ToList();
+ var destinationFolder = "Exports/" + MusicDirectory;
+
+ if (!Directory.Exists(destinationFolder))
+ {
+ Directory.CreateDirectory(destinationFolder);
+ }
+
+ foreach (var xmi in xmiFiles)
+ {
+ var fileName = Path.GetFileName(xmi);
+ var destination = Path.Combine(destinationFolder, fileName);
+ if (File.Exists(destination))
+ {
+ continue;
+ }
+
+ File.Copy(xmi, destination);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Sound/AudioInstance.cs b/LanternExtractor/EQ/Sound/AudioInstance.cs
new file mode 100644
index 0000000..cd8d664
--- /dev/null
+++ b/LanternExtractor/EQ/Sound/AudioInstance.cs
@@ -0,0 +1,20 @@
+namespace LanternExtractor.EQ.Sound
+{
+ public abstract class AudioInstance
+ {
+ protected AudioInstance(AudioType type, float posX, float posY, float posZ, float radius)
+ {
+ AudioType = type;
+ PosX = posX;
+ PosY = posY;
+ PosZ = posZ;
+ Radius = radius;
+ }
+
+ public AudioType AudioType { get; }
+ public float PosX { get; }
+ public float PosY { get; }
+ public float PosZ { get; }
+ public float Radius { get; }
+ }
+}
diff --git a/LanternExtractor/EQ/Sound/AudioType.cs b/LanternExtractor/EQ/Sound/AudioType.cs
new file mode 100644
index 0000000..f58e840
--- /dev/null
+++ b/LanternExtractor/EQ/Sound/AudioType.cs
@@ -0,0 +1,23 @@
+namespace LanternExtractor.EQ.Sound
+{
+ ///
+ /// Describes the way the sound is heard by the player
+ ///
+ public enum AudioType : byte
+ {
+ ///
+ /// Sounds that play at a constant volume
+ ///
+ Sound2d = 0,
+
+ ///
+ /// Music instance. Can specify both day and night trackID.
+ ///
+ Music = 1,
+
+ ///
+ /// Sounds that have a falloff - the farther the player is from the center, the quieter it becomes.
+ ///
+ Sound3d = 2,
+ }
+}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Sound/ClientSounds.cs b/LanternExtractor/EQ/Sound/ClientSounds.cs
new file mode 100644
index 0000000..1470ac2
--- /dev/null
+++ b/LanternExtractor/EQ/Sound/ClientSounds.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace LanternExtractor.EQ.Sound
+{
+ public static class ClientSounds
+ {
+ // Hardcoded client sounds - verified that no other references exist in Trilogy client
+ private static Dictionary _clientSounds = new Dictionary
+ {
+ { 39, "death_me" },
+ { 143, "thunder1" },
+ { 144, "thunder2" },
+ { 158, "wind_lp1" },
+ { 159, "rainloop" },
+ { 160, "torch_lp" },
+ { 161, "watundlp" },
+ };
+
+ public static string GetClientSound(int index)
+ {
+ return _clientSounds.TryGetValue(index, out var soundName) ? soundName : SoundConstants.Unknown;
+ }
+ }
+}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Sound/EffSndBnk.cs b/LanternExtractor/EQ/Sound/EffSndBnk.cs
index 2f758d3..9122f07 100644
--- a/LanternExtractor/EQ/Sound/EffSndBnk.cs
+++ b/LanternExtractor/EQ/Sound/EffSndBnk.cs
@@ -10,11 +10,9 @@ namespace LanternExtractor.EQ.Sound
///
public class EffSndBnk
{
- public readonly List EmitSounds = new List();
-
- public readonly List LoopSounds = new List();
-
- private readonly string _soundFilePath;
+ private List _emitSounds = new List();
+ private List _loopSounds = new List();
+ private string _soundFilePath;
public EffSndBnk(string soundFilePath)
{
@@ -23,15 +21,14 @@ public EffSndBnk(string soundFilePath)
public void Initialize()
{
- List currentList = null;
-
if (!File.Exists(_soundFilePath))
{
return;
}
+
+ List currentList = null;
string fileText = File.ReadAllText(_soundFilePath);
-
List parsedLines = TextParser.ParseTextByNewline(fileText);
if (parsedLines == null || parsedLines.Count == 0)
@@ -41,15 +38,18 @@ public void Initialize()
foreach (var line in parsedLines)
{
+ if (string.IsNullOrEmpty(line))
+ {
+ continue;
+ }
+
switch (line)
{
- case "":
+ case SoundConstants.Emit:
+ currentList = _emitSounds;
continue;
- case "EMIT":
- currentList = EmitSounds;
- continue;
- case "LOOP":
- currentList = LoopSounds;
+ case SoundConstants.Loop:
+ currentList = _loopSounds;
continue;
default:
currentList?.Add(line);
@@ -57,5 +57,25 @@ public void Initialize()
}
}
}
+
+ private string GetValueFromList(int index, ref List list)
+ {
+ if (index < 0 || index >= list.Count)
+ {
+ return SoundConstants.Unknown;
+ }
+
+ return list[index];
+ }
+
+ public string GetEmitSound(int index)
+ {
+ return GetValueFromList(index, ref _emitSounds);
+ }
+
+ public string GetLoopSound(int index)
+ {
+ return GetValueFromList(index, ref _loopSounds);
+ }
}
}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Sound/EffSounds.cs b/LanternExtractor/EQ/Sound/EffSounds.cs
index 336b3a6..8bbd0da 100644
--- a/LanternExtractor/EQ/Sound/EffSounds.cs
+++ b/LanternExtractor/EQ/Sound/EffSounds.cs
@@ -1,7 +1,8 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
+using LanternExtractor.Infrastructure.Logger;
namespace LanternExtractor.EQ.Sound
{
@@ -12,44 +13,38 @@ namespace LanternExtractor.EQ.Sound
///
public class EffSounds
{
+ public static int EntryLengthInBytes = 84;
+
///
/// The sound bank referenced for sound names
///
private readonly EffSndBnk _soundBank;
-
+
+ private readonly EnvAudio _envAudio;
private readonly string _soundFilePath;
-
- private readonly List _soundEntries = new List();
-
- private readonly List _musicTrackEntries = new List();
-
- private const int EntryLengthInBytes = 84;
-
- public EffSounds(string soundFilePath, EffSndBnk soundBank)
+ private readonly List _audioInstances = new List();
+
+ public EffSounds(string soundFilePath, EffSndBnk soundBank, EnvAudio envAudio)
{
_soundFilePath = soundFilePath;
_soundBank = soundBank;
+ _envAudio = envAudio;
}
-
- public void Initialize()
+
+ public void Initialize(ILogger logger)
{
if (_soundBank == null || !File.Exists(_soundFilePath))
{
return;
}
- FileStream file = File.Open(_soundFilePath, FileMode.Open);
-
- string zoneShortName = Path.GetFileNameWithoutExtension(_soundFilePath).Split('_')[0];
-
- LoadMusicTrackNames(zoneShortName);
-
+ var file = File.Open(_soundFilePath, FileMode.Open);
var reader = new BinaryReader(file);
-
- int fileLength = (int) reader.BaseStream.Length;
+ int fileLength = (int)reader.BaseStream.Length;
if (fileLength % EntryLengthInBytes != 0)
{
+ logger.LogError($"Invalid .eff file - size must be multiple of {EntryLengthInBytes}");
return;
}
@@ -57,233 +52,191 @@ public void Initialize()
for (int i = 0; i < entryCount; ++i)
{
- var newSound = new SoundEntry();
- newSound.UnkRef00 = reader.ReadInt32();
- newSound.UnkRef04 = reader.ReadInt32();
- newSound.Reserved = reader.ReadInt32();
- newSound.Sequence = reader.ReadInt32();
+ var basePosition = EntryLengthInBytes * i;
+ reader.BaseStream.Position = basePosition + 16;
+ float posX = reader.ReadSingle();
+ float posY = reader.ReadSingle();
+ float posZ = reader.ReadSingle();
+ float radius = reader.ReadSingle();
- newSound.PosX = reader.ReadSingle();
- newSound.PosY = reader.ReadSingle();
- newSound.PosZ = reader.ReadSingle();
- newSound.Radius = reader.ReadSingle();
+ reader.BaseStream.Position = basePosition + 56;
- newSound.CooldownDay = reader.ReadInt32();
- newSound.CooldownNight = reader.ReadInt32();
- newSound.RandomDelay = reader.ReadInt32();
+ var typeByte = reader.ReadByte();
- newSound.Unk44 = reader.ReadInt32();
- int soundId1 = reader.ReadInt32();
- int soundId2 = reader.ReadInt32();
-
- byte soundType = reader.ReadByte();
- newSound.SoundType = (SoundType) soundType;
-
- if (soundType == 0 || soundType == 2 || soundType == 3)
- {
- // Find the sound names
- EmissionType newSoundEmissionType = newSound.EmissionType;
- newSound.SoundIdDay = GetSoundString(soundId1, _soundBank, ref newSoundEmissionType);
- EmissionType soundEmissionType = newSound.EmissionType;
- newSound.SoundIdNight = GetSoundString(soundId2, _soundBank, ref soundEmissionType);
- }
- else
+ if (!Enum.IsDefined(typeof(AudioType), typeByte))
{
- newSound.SoundIdDay = GetMusicTrackName(soundId1);
- newSound.SoundIdNight = GetMusicTrackName(soundId1);
+ logger.LogError($"Unable to parse sound type: {typeByte}");
+ continue;
}
- newSound.UnkPad57 = reader.ReadByte();
- newSound.UnkPad58 = reader.ReadByte();
- newSound.UnkPad59 = reader.ReadByte();
-
- newSound.AsDistance = reader.ReadInt32();
- newSound.UnkRange64 = reader.ReadInt32();
- newSound.FadeOutMs = reader.ReadInt32();
- newSound.UnkRange72 = reader.ReadInt32();
- newSound.FullVolRange = reader.ReadInt32();
- newSound.UnkRange80 = reader.ReadInt32();
+ var type = (AudioType)typeByte;
+ reader.BaseStream.Position = basePosition + 48;
+ int soundId1 = reader.ReadInt32();
+ string sound1 = GetSoundName(soundId1);
- if (newSound.SoundIdDay != "" || newSound.SoundIdNight != "")
+ if (type == AudioType.Music)
{
- _soundEntries.Add(newSound);
+ int soundId2 = reader.ReadInt32();
+ reader.BaseStream.Position = basePosition + 60;
+ int loopCountDay = reader.ReadInt32();
+ int loopCountNight = reader.ReadInt32();
+ int fadeOutMs = reader.ReadInt32();
+ var musicInstance = new MusicInstance(type, posX, posY, posZ, radius, soundId1, soundId2,
+ loopCountDay, loopCountNight, fadeOutMs);
+ _audioInstances.Add(musicInstance);
}
- }
- }
-
- private void LoadMusicTrackNames(string zoneShortName)
- {
- string[] trackLines = File.ReadAllLines("ClientData/musictracks.txt");
-
- bool isTargetZone = false;
-
- foreach (string line in trackLines)
- {
- if (!isTargetZone)
+ else if (type == AudioType.Sound2d)
{
- if (line == "#" + zoneShortName)
- {
- isTargetZone = true;
- }
-
- continue;
+ int soundId2 = reader.ReadInt32();
+ string sound2 = GetSoundName(soundId2);
+ reader.BaseStream.Position = basePosition + 32;
+ int cooldown1 = reader.ReadInt32();
+ int cooldown2 = reader.ReadInt32();
+ int cooldownRandom = reader.ReadInt32();
+ reader.BaseStream.Position = basePosition + 60;
+ int volume1Raw = reader.ReadInt32();
+ float volume1 = GetEalSoundVolume(sound1, volume1Raw);
+ int volume2Raw = reader.ReadInt32();
+ float volume2 = GetEalSoundVolume(sound2, volume2Raw);
+ var soundInstance = new SoundInstance2d(type, posX, posY, posZ, radius, volume1, sound1, cooldown1,
+ sound2, cooldown2, cooldownRandom, volume2);
+ _audioInstances.Add(soundInstance);
}
-
- if (line == string.Empty)
+ else
{
- break;
+ reader.BaseStream.Position = basePosition + 32;
+ int cooldown1 = reader.ReadInt32();
+ reader.BaseStream.Position = basePosition + 40;
+ int cooldownRandom = reader.ReadInt32();
+ reader.BaseStream.Position = basePosition + 60;
+ int volumeRaw = reader.ReadInt32();
+ float volume = GetEalSoundVolume(sound1, volumeRaw);
+ reader.BaseStream.Position = basePosition + 72;
+ int multiplier = reader.ReadInt32();
+ var soundInstance = new SoundInstance3d(type, posX, posY, posZ, radius, volume, sound1,
+ cooldown1, cooldownRandom, multiplier);
+ _audioInstances.Add(soundInstance);
}
-
- _musicTrackEntries.Add(line);
}
}
- ///
- /// Returns the name of the sound based on either the internal reference or the sound back definitions
- /// The client uses specific integer ranges to identify the type.
- /// There are also a handful of hardcoded sound ids.
- ///
- /// The id index of the sound
- /// The sound bank in which to look
- /// The emission type
- /// The name of the sound
- private string GetSoundString(int soundId, EffSndBnk soundBank, ref EmissionType soundType)
+ private string GetSoundName(int soundId)
{
- if (soundId == 0)
+ var emissionType = GetEmissionType(soundId);
+ switch (emissionType)
{
- return string.Empty;
+ case EmissionType.None:
+ return string.Empty;
+ case EmissionType.Emit:
+ return _soundBank.GetEmitSound(soundId - 1);
+ case EmissionType.Loop:
+ return _soundBank.GetLoopSound(soundId - 162);
+ case EmissionType.Internal:
+ return ClientSounds.GetClientSound(soundId);
+ default:
+ return SoundConstants.Unknown;
}
+ }
- if (soundId >= 1 && soundId < 32 && soundId < soundBank.EmitSounds.Count)
+ private float GetEalSoundVolume(string soundName, int volumeRaw)
+ {
+ if (volumeRaw > 0)
{
- soundType = EmissionType.Emit;
- return soundBank.EmitSounds[soundId - 1];
+ volumeRaw = -volumeRaw;
}
- // Hardcoded client sounds - verified that no other references exist in Trilogy client
- if (soundId >= 32 && soundId < 162)
- {
- soundType = EmissionType.Internal;
+ return volumeRaw == 0 ? _envAudio.GetVolumeLinear(soundName) : _envAudio.GetVolumeLinear(volumeRaw);
+ }
- switch (soundId)
- {
- case 39:
- return "death_me";
- case 143:
- return "thunder1";
- case 144:
- return "thunder2";
- case 158:
- return "wind_lp1";
- case 159:
- return "rainloop";
- case 160:
- return "torch_lp";
- case 161:
- return "watundlp";
- }
+ private EmissionType GetEmissionType(int soundId)
+ {
+ if (soundId <= 0)
+ {
+ return EmissionType.None;
}
- if (soundId < 162 || soundId >= 162 + soundBank.LoopSounds.Count)
+ if (soundId < 32)
{
- return string.Empty;
+ return EmissionType.Emit;
}
-
- soundType = EmissionType.Loop;
- return soundBank.LoopSounds[soundId - 161 - 1];
+ return soundId < 162 ? EmissionType.Internal : EmissionType.Loop;
}
-
+
public void ExportSoundData(string zoneName, string rootFolder)
{
- var soundExport = new StringBuilder();
+ var sound2dExport = new StringBuilder();
+ var sound3dExport = new StringBuilder();
var musicExport = new StringBuilder();
- foreach (SoundEntry entry in _soundEntries)
+ foreach (var entry in _audioInstances)
{
- if (entry.SoundType == SoundType.Music)
+ if (entry.AudioType == AudioType.Music)
{
- musicExport.Append(entry.PosX);
- musicExport.Append(",");
- musicExport.Append(entry.PosZ);
- musicExport.Append(",");
- musicExport.Append(entry.PosY);
- musicExport.Append(",");
- musicExport.Append(entry.Radius);
- musicExport.Append(",");
- musicExport.Append(entry.SoundIdDay);
- musicExport.Append(",");
- musicExport.Append(entry.SoundIdNight);
- musicExport.Append(",");
- musicExport.Append(entry.AsDistance); // Day loop count
- musicExport.Append(",");
- musicExport.Append(entry.UnkRange64); // Night loop count
- musicExport.Append(",");
- musicExport.Append(entry.FadeOutMs);
- musicExport.AppendLine();
+ if (!(entry is MusicInstance music))
+ {
+ continue;
+ }
+
+ musicExport.AppendLine(string.Join(",", music.PosX, music.PosZ, music.PosY, music.Radius,
+ music.TrackIndexDay, music.TrackIndexNight, music.LoopCountDay, music.LoopCountNight,
+ music.FadeOutMs));
+ }
+ else if (entry.AudioType == AudioType.Sound2d)
+ {
+ if (!(entry is SoundInstance2d sound2d))
+ {
+ continue;
+ }
+
+ sound2dExport.AppendLine(string.Join(",", sound2d.PosX, sound2d.PosZ,
+ sound2d.PosY, sound2d.Radius, sound2d.Sound1, sound2d.Sound2,
+ sound2d.Cooldown1, sound2d.Cooldown2, sound2d.CooldownRandom, sound2d.Volume1, sound2d.Volume2));
}
else
{
- soundExport.Append((int)entry.SoundType);
- soundExport.Append(",");
- soundExport.Append(entry.PosX);
- soundExport.Append(",");
- soundExport.Append(entry.PosZ);
- soundExport.Append(",");
- soundExport.Append(entry.PosY);
- soundExport.Append(",");
- soundExport.Append(entry.Radius);
- soundExport.Append(",");
- soundExport.Append(entry.SoundIdDay);
- soundExport.Append(",");
- soundExport.Append(entry.SoundIdNight);
- soundExport.Append(",");
- soundExport.Append(entry.CooldownDay);
- soundExport.Append(",");
- soundExport.Append(entry.CooldownNight);
- soundExport.Append(",");
- soundExport.Append(entry.RandomDelay);
- soundExport.AppendLine();
+ if (!(entry is SoundInstance3d sound3d))
+ {
+ continue;
+ }
+
+ sound3dExport.AppendLine(string.Join(",", sound3d.PosX, sound3d.PosZ,
+ sound3d.PosY, sound3d.Radius, sound3d.Sound1, sound3d.Cooldown1, sound3d.CooldownRandom, sound3d.Volume1,
+ sound3d.Multiplier));
}
}
- string exportPath = rootFolder + zoneName + "/Zone/";
+ string exportPath = Path.Combine(rootFolder, zoneName, "Zone/");
- if (soundExport.Length != 0)
+ if (musicExport.Length > 0)
{
StringBuilder exportHeader = new StringBuilder();
- exportHeader.AppendLine(LanternStrings.ExportHeaderTitle + "Sound Instances");
- exportHeader.AppendLine("# Format: SoundType, PosX, PosY, PosZ, Radius, SoundIdDay, SoundIdNight, CooldownDay, CooldownNight, RandomDelay");
-
+ exportHeader.AppendLine(LanternStrings.ExportHeaderTitle + "Music Instances");
+ exportHeader.AppendLine(
+ "# Format: PosX, PosY, PosZ, Radius, MusicIndexDay, MusicIndexNight, LoopCountDay, LoopCountNight, FadeOutMs");
Directory.CreateDirectory(exportPath);
- File.WriteAllText(exportPath + "sound_instances.txt", exportHeader.ToString() + soundExport);
+ File.WriteAllText(exportPath + "music_instances.txt", exportHeader.ToString() + musicExport);
}
- if (musicExport.Length != 0)
+ if (sound2dExport.Length > 0)
{
StringBuilder exportHeader = new StringBuilder();
- exportHeader.AppendLine(LanternStrings.ExportHeaderTitle + "Music Instances");
- exportHeader.AppendLine("# Format: PosX, PosY, PosZ, Radius, SoundIdDay, SoundIdNight, DayLoopCount, NightLoopCount, FadeOutMs");
-
+ exportHeader.AppendLine(LanternStrings.ExportHeaderTitle + "Sound 2D Instances");
+ exportHeader.AppendLine(
+ "# Format: PosX, PosY, PosZ, Radius, SoundNameDay, SoundNameNight, CooldownDay, CooldownNight, CooldownRandom, VolumeDay, VolumeNight");
Directory.CreateDirectory(exportPath);
- File.WriteAllText(exportPath + "music_instances.txt", exportHeader.ToString() + musicExport);
+ File.WriteAllText(exportPath + "sound2d_instances.txt", exportHeader.ToString() + sound2dExport);
}
- }
- ///
- /// Gets the name of the music track if it exists
- ///
- /// The zone shortname
- /// The index of the track
- /// The name of the track
- private string GetMusicTrackName(int index)
- {
- if (index < 0 || index >= _musicTrackEntries.Count)
+ if (sound3dExport.Length > 0)
{
- return "Unknown";
+ StringBuilder exportHeader = new StringBuilder();
+ exportHeader.AppendLine(LanternStrings.ExportHeaderTitle + "Sound 3D Instances");
+ exportHeader.AppendLine(
+ "# Format: PosX, PosY, PosZ, Radius, SoundName, Cooldown, CooldownRandom, Volume, Multiplier");
+ Directory.CreateDirectory(exportPath);
+ File.WriteAllText(exportPath + "sound3d_instances.txt", exportHeader.ToString() + sound3dExport);
}
-
- return _musicTrackEntries[index];
}
}
-}
+}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Sound/EmissionType.cs b/LanternExtractor/EQ/Sound/EmissionType.cs
index fc604b7..ec0543f 100644
--- a/LanternExtractor/EQ/Sound/EmissionType.cs
+++ b/LanternExtractor/EQ/Sound/EmissionType.cs
@@ -2,19 +2,21 @@
{
public enum EmissionType
{
+ None = 0,
+
///
/// Emitted sounds - things like bird noises
///
- Emit = 0,
+ Emit = 1,
///
/// Looped sounds - things like the ocean or a lake
///
- Loop = 1,
+ Loop = 2,
///
/// Sounds that are internal to the client
///
- Internal = 2
+ Internal = 3
}
}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Sound/EnvAudio.cs b/LanternExtractor/EQ/Sound/EnvAudio.cs
new file mode 100644
index 0000000..52667c5
--- /dev/null
+++ b/LanternExtractor/EQ/Sound/EnvAudio.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.IO;
+using EalTools;
+
+namespace LanternExtractor.EQ.Sound
+{
+ public class EnvAudio
+ {
+ public static EnvAudio Instance { get; } = new EnvAudio();
+
+ public EalData Data { get; private set; }
+
+ private bool _loaded;
+ private string _ealFilePath;
+ private EalFile _ealFile;
+ private Dictionary _sourceLevels;
+
+ static EnvAudio()
+ {
+ }
+
+ private EnvAudio()
+ {
+ }
+
+ public bool Load(string ealFilePath)
+ {
+ if (_ealFilePath == ealFilePath)
+ {
+ return _loaded;
+ }
+
+ if (ealFilePath == null || !File.Exists(ealFilePath))
+ {
+ return false;
+ }
+
+ _ealFilePath = ealFilePath;
+
+ // Allow other threads to open the same file for reading
+ _ealFile = new EalFile(new FileStream(
+ _ealFilePath, FileMode.Open, FileAccess.Read, FileShare.Read));
+
+ if (!_ealFile.Initialize())
+ {
+ return false;
+ }
+
+ Data = _ealFile.Data;
+
+ if (Data == null)
+ {
+ return false;
+ }
+
+ _sourceLevels = Data.SourceModels.ToDictionary(
+ s => Path.GetFileNameWithoutExtension(s.Name).ToLower(),
+ s => s.SourceAttributes.EaxAttributes.DirectPathLevel
+ );
+
+ _loaded = true;
+
+ return _loaded;
+ }
+
+ private int GetVolumeEq(string soundFile)
+ {
+ var volume = 0;
+ _sourceLevels?.TryGetValue(soundFile, out volume);
+ return volume;
+ }
+
+ public float GetVolumeLinear(string soundFile)
+ {
+ var volumeEq = GetVolumeEq(soundFile);
+ return GetVolumeLinear(volumeEq);
+ }
+
+ public float GetVolumeLinear(int directAudioLevel)
+ {
+ var linear = MathF.Pow(10.0f, directAudioLevel / 2000.0f);
+ return Math.Clamp(linear, 0f, 1f);
+ }
+ }
+}
diff --git a/LanternExtractor/EQ/Sound/MusicInstance.cs b/LanternExtractor/EQ/Sound/MusicInstance.cs
new file mode 100644
index 0000000..0e5411f
--- /dev/null
+++ b/LanternExtractor/EQ/Sound/MusicInstance.cs
@@ -0,0 +1,22 @@
+namespace LanternExtractor.EQ.Sound
+{
+ public class MusicInstance : AudioInstance
+ {
+ public MusicInstance(AudioType type, float posX, float posY, float posZ, float radius, int trackIndexDay,
+ int trackIndexNight, int loopCountDay, int loopCountNight, int fadeOutMs) : base(type, posX, posY, posZ,
+ radius)
+ {
+ TrackIndexDay = trackIndexDay;
+ TrackIndexNight = trackIndexNight;
+ LoopCountDay = loopCountDay;
+ LoopCountNight = loopCountNight;
+ FadeOutMs = fadeOutMs;
+ }
+
+ public int TrackIndexDay { get; }
+ public int TrackIndexNight { get; }
+ public int LoopCountDay { get; }
+ public int LoopCountNight { get; }
+ public int FadeOutMs { get; }
+ }
+}
diff --git a/LanternExtractor/EQ/Sound/SoundConstants.cs b/LanternExtractor/EQ/Sound/SoundConstants.cs
new file mode 100644
index 0000000..e4ff969
--- /dev/null
+++ b/LanternExtractor/EQ/Sound/SoundConstants.cs
@@ -0,0 +1,9 @@
+namespace LanternExtractor.EQ.Sound
+{
+ public class SoundConstants
+ {
+ public const string Emit = "EMIT";
+ public const string Loop = "LOOP";
+ public const string Unknown = "Unknown";
+ }
+}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Sound/SoundEntry.cs b/LanternExtractor/EQ/Sound/SoundEntry.cs
deleted file mode 100644
index 1e13a52..0000000
--- a/LanternExtractor/EQ/Sound/SoundEntry.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-namespace LanternExtractor.EQ.Sound
-{
- ///
- /// Contains information about a single sound instance in the world
- /// Big thanks to Shendare from the EQEmu forums for sharing this information
- /// More documentation will come as I verify all of this info
- ///
- public class SoundEntry
- {
- public EmissionType EmissionType { get; set; }
- public int UnkRef00 { get; set; }
- public int UnkRef04 { get; set; }
- public int Reserved { get; set; }
- public int Sequence { get; set; }
- public float PosX { get; set; }
- public float PosY { get; set; }
- public float PosZ { get; set; }
- public float Radius { get; set; }
- public int CooldownDay { get; set; }
- public int CooldownNight { get; set; }
- public int RandomDelay { get; set; }
- public int Unk44 { get; set; }
- public string SoundIdDay { get; set; }
- public string SoundIdNight { get; set; }
- public SoundType SoundType { get; set; }
- public byte UnkPad57 { get; set; }
- public byte UnkPad58 { get; set; }
- public byte UnkPad59 { get; set; }
- public int AsDistance { get; set; }
- public int UnkRange64 { get; set; }
- public int FadeOutMs { get; set; }
- public int UnkRange72 { get; set; }
- public int FullVolRange { get; set; }
- public int UnkRange80 { get; set; }
- }
-}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Sound/SoundInstance.cs b/LanternExtractor/EQ/Sound/SoundInstance.cs
new file mode 100644
index 0000000..f05a1cb
--- /dev/null
+++ b/LanternExtractor/EQ/Sound/SoundInstance.cs
@@ -0,0 +1,19 @@
+namespace LanternExtractor.EQ.Sound
+{
+ public abstract class SoundInstance : AudioInstance
+ {
+ public string Sound1 { get; }
+ public float Volume1 { get; }
+ public int Cooldown1 { get; }
+ public int CooldownRandom { get; }
+
+ protected SoundInstance(AudioType type, float posX, float posY, float posZ, float radius, float volume1,
+ string sound1, int cooldown1, int cooldownRandom) : base(type, posX, posY, posZ, radius)
+ {
+ Sound1 = sound1;
+ Volume1 = volume1;
+ Cooldown1 = cooldown1;
+ CooldownRandom = cooldownRandom;
+ }
+ }
+}
diff --git a/LanternExtractor/EQ/Sound/SoundInstance2d.cs b/LanternExtractor/EQ/Sound/SoundInstance2d.cs
new file mode 100644
index 0000000..fe2db15
--- /dev/null
+++ b/LanternExtractor/EQ/Sound/SoundInstance2d.cs
@@ -0,0 +1,18 @@
+namespace LanternExtractor.EQ.Sound
+{
+ public class SoundInstance2d : SoundInstance
+ {
+ public string Sound2 { get; }
+ public float Volume2 { get; }
+ public int Cooldown2 { get; }
+
+ public SoundInstance2d(AudioType type, float posX, float posY, float posZ, float radius, float volume1,
+ string sound1, int cooldown1, string sound2, int cooldown2, int cooldownRandom, float volume2) : base(type, posX, posY, posZ, radius, volume1,
+ sound1, cooldown1, cooldownRandom)
+ {
+ Sound2 = sound2;
+ Cooldown2 = cooldown2;
+ Volume2 = volume2;
+ }
+ }
+}
diff --git a/LanternExtractor/EQ/Sound/SoundInstance3d.cs b/LanternExtractor/EQ/Sound/SoundInstance3d.cs
new file mode 100644
index 0000000..0cc9f82
--- /dev/null
+++ b/LanternExtractor/EQ/Sound/SoundInstance3d.cs
@@ -0,0 +1,14 @@
+namespace LanternExtractor.EQ.Sound
+{
+ public class SoundInstance3d : SoundInstance
+ {
+ public int Multiplier;
+
+ public SoundInstance3d(AudioType type, float posX, float posY, float posZ, float radius, float volume,
+ string sound1, int cooldown1, int cooldownRandom, int multiplier) : base(type, posX, posY, posZ, radius, volume, sound1,
+ cooldown1, cooldownRandom)
+ {
+ Multiplier = multiplier;
+ }
+ }
+}
diff --git a/LanternExtractor/EQ/Sound/SoundTest.cs b/LanternExtractor/EQ/Sound/SoundTest.cs
new file mode 100644
index 0000000..b70bccf
--- /dev/null
+++ b/LanternExtractor/EQ/Sound/SoundTest.cs
@@ -0,0 +1,35 @@
+using System.IO;
+using System.Linq;
+
+namespace LanternExtractor.EQ.Sound
+{
+ ///
+ /// A small helper class useful for sound isolation and testing
+ ///
+ public static class SoundTest
+ {
+ public static void OutputSingleInstance(BinaryWriter writer, int index, string fileName)
+ {
+ writer.BaseStream.Position = 0;
+ var memoryStream = new MemoryStream();
+ writer.BaseStream.CopyTo(memoryStream);
+ var bytes = memoryStream.ToArray().Skip(index * EffSounds.EntryLengthInBytes)
+ .Take(EffSounds.EntryLengthInBytes).ToArray();
+ File.WriteAllBytes(fileName, bytes);
+ }
+
+ public static void ModifyInstance(BinaryWriter writer, string fileName)
+ {
+ writer.BaseStream.Position = 16; // positions
+ writer.Write(0f);
+ writer.Write(0f);
+ writer.Write(50f);
+
+
+ writer.BaseStream.Position = 0;
+ var memoryStream = new MemoryStream();
+ writer.BaseStream.CopyTo(memoryStream);
+ File.WriteAllBytes(fileName, memoryStream.ToArray());
+ }
+ }
+}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Sound/SoundType.cs b/LanternExtractor/EQ/Sound/SoundType.cs
deleted file mode 100644
index 7743a76..0000000
--- a/LanternExtractor/EQ/Sound/SoundType.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace LanternExtractor.EQ.Sound
-{
- ///
- /// Describes the way the sound is heard by the player
- ///
- public enum SoundType : byte
- {
- ///
- /// Sounds that play at a constant volume
- ///
- NoFalloff = 0,
-
- ///
- /// Background music. Can specify both a daytime and nighttime music via the soundID
- /// Music usually has a large fade out delay
- ///
- Music = 1,
-
- ///
- /// Sounds that have a falloff - the further the player is from the center, the quieter it becomes
- ///
- Falloff = 2,
-
- ///
- /// Sounds that stay at full volume until the user has wandered outside of the FullVolRange
- ///
- FullVolumeRange = 3,
- }
-}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Wld/DataTypes/Polygon.cs b/LanternExtractor/EQ/Wld/DataTypes/Polygon.cs
index 1114014..e1ac7f9 100644
--- a/LanternExtractor/EQ/Wld/DataTypes/Polygon.cs
+++ b/LanternExtractor/EQ/Wld/DataTypes/Polygon.cs
@@ -9,7 +9,8 @@ public Polygon GetCopy()
IsSolid = this.IsSolid,
Vertex1 = this.Vertex1,
Vertex2 = this.Vertex2,
- Vertex3 = this.Vertex3
+ Vertex3 = this.Vertex3,
+ MaterialIndex = this.MaterialIndex
};
}
@@ -17,5 +18,6 @@ public Polygon GetCopy()
public int Vertex1 { get; set; }
public int Vertex2 { get; set; }
public int Vertex3 { get; set; }
+ public int MaterialIndex { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/DataTypes/RegionObstacle.cs b/LanternExtractor/EQ/Wld/DataTypes/RegionObstacle.cs
new file mode 100644
index 0000000..1537bb1
--- /dev/null
+++ b/LanternExtractor/EQ/Wld/DataTypes/RegionObstacle.cs
@@ -0,0 +1,45 @@
+using System.Collections.Generic;
+using GlmSharp;
+
+namespace LanternExtractor.EQ.Wld.DataTypes
+{
+ public class RegionObstacle
+ {
+ /// bit 0 - is a FLOOR
+ /// bit 1 - is a GEOMETRYCUTTINGOBSTACLE
+ /// bit 2 - has USERDATA %s
+ public int Flags { get; set; }
+
+ /// NEXTREGION %d
+ public int NextRegion { get; set; }
+
+ /// XY_VERTEX 0 %d
+ /// XYZ_VERTEX 0 %d
+ /// XY_LINE 0 %d %d
+ /// XY_EDGE 0 %d %d
+ /// XYZ_EDGE 0 %d %d
+ /// PLANE 0 %d
+ /// EDGEPOLYGON 0
+ /// EDGEWALL 0 %d
+ public RegionObstacleType ObstacleType { get; set; }
+
+ // NUMVERTICES %d
+ public int NumVertices { get; set; }
+
+ /// VERTEXLIST %d ...%d
+ public List VertextList { get; set; }
+
+ /// NORMALABCD %f %f %f %f
+ public vec4 NormalAbcd { get; set; }
+
+ /// EDGEWALL 0 %d
+ /// Binary values are 0 based. "EDGEWALL 0 1" becomes edge_wall[0]
+ public int EdgeWall { get; set; }
+
+ /// Length of USERDATA string
+ public int UserDataSize { get; set; }
+
+ /// USERDATA %s
+ public byte[] UserData { get; set; }
+ }
+}
diff --git a/LanternExtractor/EQ/Wld/DataTypes/RegionObstacleType.cs b/LanternExtractor/EQ/Wld/DataTypes/RegionObstacleType.cs
new file mode 100644
index 0000000..8b20a91
--- /dev/null
+++ b/LanternExtractor/EQ/Wld/DataTypes/RegionObstacleType.cs
@@ -0,0 +1,15 @@
+namespace LanternExtractor.EQ.Wld.DataTypes
+{
+ public enum RegionObstacleType
+ {
+ XyVertex = 8,
+ XyzVertex = 9,
+ XyLine = 10,
+ XyEdge = 11,
+ XyzEdge = 12,
+ Plane = 13,
+ EdgePolygon = 14,
+ EdgeWall = 18,
+ EdgePolygonNormalAbcd = -15,
+ }
+}
diff --git a/LanternExtractor/EQ/Wld/DataTypes/RegionType.cs b/LanternExtractor/EQ/Wld/DataTypes/RegionType.cs
index d0217de..41f7186 100644
--- a/LanternExtractor/EQ/Wld/DataTypes/RegionType.cs
+++ b/LanternExtractor/EQ/Wld/DataTypes/RegionType.cs
@@ -7,7 +7,7 @@ public enum RegionType
Lava = 2,
Pvp = 3,
Zoneline = 4,
- WaterBlockLOS = 5,
+ WaterBlockLos = 5,
FreezingWater = 6,
Slippery = 7,
Unknown = 8,
diff --git a/LanternExtractor/EQ/Wld/DataTypes/RegionVisList.cs b/LanternExtractor/EQ/Wld/DataTypes/RegionVisList.cs
new file mode 100644
index 0000000..0cd0d99
--- /dev/null
+++ b/LanternExtractor/EQ/Wld/DataTypes/RegionVisList.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace LanternExtractor.EQ.Wld.DataTypes
+{
+ public class RegionVisList
+ {
+ /// RANGE %d
+ public int RangeCount { get; set; }
+
+ public List Ranges { get; set; }
+ }
+}
diff --git a/LanternExtractor/EQ/Wld/DataTypes/RegionVisNode.cs b/LanternExtractor/EQ/Wld/DataTypes/RegionVisNode.cs
new file mode 100644
index 0000000..cbba04e
--- /dev/null
+++ b/LanternExtractor/EQ/Wld/DataTypes/RegionVisNode.cs
@@ -0,0 +1,19 @@
+using GlmSharp;
+
+namespace LanternExtractor.EQ.Wld.DataTypes
+{
+ public class RegionVisNode
+ {
+ /// NORMALABCD %f %f %f %f
+ public vec4 NormalAbcd { get; set; }
+
+ /// VISLISTINDEX %d
+ public int VisListIndex { get; set; }
+
+ /// FRONTTREE %d
+ public int FrontTree { get; set; }
+
+ /// BACKTREE %d
+ public int BackTree { get; set; }
+ }
+}
diff --git a/LanternExtractor/EQ/Wld/DataTypes/RegionWall.cs b/LanternExtractor/EQ/Wld/DataTypes/RegionWall.cs
new file mode 100644
index 0000000..26bd1c1
--- /dev/null
+++ b/LanternExtractor/EQ/Wld/DataTypes/RegionWall.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using GlmSharp;
+
+namespace LanternExtractor.EQ.Wld.DataTypes
+{
+ public class RegionWall
+ {
+ /// bit 0 - has FLOOR (is floor?)
+ /// bit 1 - has RENDERMETHOD and NORMALABCD (is renderable?)
+ public int Flags { get; set; }
+
+ /// NUMVERTICES %d
+ public int NumVertices { get; set; }
+
+ /// RENDERMETHOD ...
+ public RenderMethod RenderMethod { get; set; }
+
+ /// RENDERINFO
+ public RenderInfo RenderInfo { get; set; }
+
+ /// NORMALABCD %f %f %f %f
+ public vec4 NormalAbcd { get; set; }
+
+ /// VERTEXLIST %d ...%d
+ /// Binary values are 0 based. "VERTEXLIST 1" becomes vertex_list[0]
+ public List VertexList { get; set; }
+ }
+}
diff --git a/LanternExtractor/EQ/Wld/DataTypes/RenderInfo.cs b/LanternExtractor/EQ/Wld/DataTypes/RenderInfo.cs
new file mode 100644
index 0000000..0c0c340
--- /dev/null
+++ b/LanternExtractor/EQ/Wld/DataTypes/RenderInfo.cs
@@ -0,0 +1,80 @@
+using System.Collections.Generic;
+using System.IO;
+using GlmSharp;
+using LanternExtractor.EQ.Wld.Fragments;
+using LanternExtractor.Infrastructure;
+
+namespace LanternExtractor.EQ.Wld.DataTypes
+{
+ public class RenderInfo
+ {
+ public int Flags { get; set; }
+ public int Pen { get; set; }
+ public float Brightness { get; set; }
+ public float ScaledAmbient { get; set; }
+ public BitmapInfoReference SimpleSpriteReference { get; set; }
+ public UvInfo UvInfo { get; set; }
+ public List UvMap { get; set; }
+
+ public static RenderInfo Parse(BinaryReader reader, List fragments)
+ {
+ var renderInfo = new RenderInfo
+ {
+ Flags = reader.ReadInt32()
+ };
+
+ var ba = new BitAnalyzer(renderInfo.Flags);
+
+ var hasPen = ba.IsBitSet(0);
+ var hasBrightness = ba.IsBitSet(1);
+ var hasScaledAmbient = ba.IsBitSet(2);
+ var hasSimpleSprite = ba.IsBitSet(3);
+ var hasUvInfo = ba.IsBitSet(4);
+ var hasUvMap = ba.IsBitSet(5);
+ var isTwoSided = ba.IsBitSet(6);
+
+ if (hasPen)
+ {
+ renderInfo.Pen = reader.ReadInt32();
+ }
+
+ if (hasBrightness)
+ {
+ renderInfo.Brightness = reader.ReadSingle();
+ }
+
+ if (hasScaledAmbient)
+ {
+ renderInfo.ScaledAmbient = reader.ReadSingle();
+ }
+
+ if (hasSimpleSprite)
+ {
+ var fragmentRef = reader.ReadInt32();
+ renderInfo.SimpleSpriteReference = fragments[fragmentRef - 1] as BitmapInfoReference;
+ }
+
+ if (hasUvInfo)
+ {
+ renderInfo.UvInfo = new UvInfo
+ {
+ UvOrigin = new vec3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()),
+ UAxis = new vec3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()),
+ VAxis = new vec3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle())
+ };
+ }
+
+ if (hasUvMap)
+ {
+ var uvMapCount = reader.ReadInt32();
+ renderInfo.UvMap = new List();
+ for (var i = 0; i < uvMapCount; i++)
+ {
+ renderInfo.UvMap.Add(new vec2(reader.ReadSingle(), reader.ReadSingle()));
+ }
+ }
+
+ return renderInfo;
+ }
+ }
+}
diff --git a/LanternExtractor/EQ/Wld/DataTypes/RenderMethod.cs b/LanternExtractor/EQ/Wld/DataTypes/RenderMethod.cs
new file mode 100644
index 0000000..507f86e
--- /dev/null
+++ b/LanternExtractor/EQ/Wld/DataTypes/RenderMethod.cs
@@ -0,0 +1,9 @@
+namespace LanternExtractor.EQ.Wld.DataTypes
+{
+ // TODO: Break out flags into built-in and user defined
+ // EQ's user defined combinations are defined as `MaterialType`
+ public class RenderMethod
+ {
+ public int Flags { get; set; }
+ }
+}
diff --git a/LanternExtractor/EQ/Wld/DataTypes/UVInfo.cs b/LanternExtractor/EQ/Wld/DataTypes/UVInfo.cs
new file mode 100644
index 0000000..28cc45d
--- /dev/null
+++ b/LanternExtractor/EQ/Wld/DataTypes/UVInfo.cs
@@ -0,0 +1,11 @@
+using GlmSharp;
+
+namespace LanternExtractor.EQ.Wld.DataTypes
+{
+ public class UvInfo
+ {
+ public vec3 UvOrigin { get; set; }
+ public vec3 UAxis { get; set; }
+ public vec3 VAxis { get; set; }
+ }
+}
diff --git a/LanternExtractor/EQ/Wld/Exporters/ActorGltfExporter.cs b/LanternExtractor/EQ/Wld/Exporters/ActorGltfExporter.cs
index da47e1f..5aa0ef5 100644
--- a/LanternExtractor/EQ/Wld/Exporters/ActorGltfExporter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/ActorGltfExporter.cs
@@ -1,4 +1,4 @@
-using LanternExtractor.EQ.Pfs;
+using LanternExtractor.EQ.Archive;
using LanternExtractor.EQ.Wld.DataTypes;
using LanternExtractor.EQ.Wld.Fragments;
using LanternExtractor.Infrastructure.Logger;
@@ -64,9 +64,9 @@ private static void ExportZone(WldFileZone wldFileZone, Settings settings, ILogg
}
// Find associated _obj archive e.g. qeytoqrg_obj.s3d, open it and add meshes and materials to our list
- var objPath = wldFileZone.BasePath.Replace(".s3d", "_obj.s3d");
+ var objPath = EqFileHelper.ObjArchivePath(wldFileZone.BasePath);
var objArchive = Path.GetFileNameWithoutExtension(objPath);
- var s3dObjArchive = new PfsArchive(objPath, logger);
+ var s3dObjArchive = ArchiveFactory.GetArchive(objPath, logger);
if (s3dObjArchive.Initialize())
{
string wldFileName = objArchive + LanternStrings.WldFormatExtension;
@@ -309,4 +309,4 @@ private static void ExportSkeletalActor(Actor actor, Settings settings, WldFile
// KHR_materials_variants extension is made for this, but no support for it in SharpGLTF
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Exporters/ActorObjExporter.cs b/LanternExtractor/EQ/Wld/Exporters/ActorObjExporter.cs
index 29902a9..80b264b 100644
--- a/LanternExtractor/EQ/Wld/Exporters/ActorObjExporter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/ActorObjExporter.cs
@@ -2,7 +2,7 @@
using System.IO;
using System.Linq;
using GlmSharp;
-using LanternExtractor.EQ.Pfs;
+using LanternExtractor.EQ.Archive;
using LanternExtractor.EQ.Wld.DataTypes;
using LanternExtractor.EQ.Wld.Fragments;
using LanternExtractor.EQ.Wld.Helpers;
@@ -12,7 +12,7 @@ namespace LanternExtractor.EQ.Wld.Exporters
{
public static class ActorObjExporter
{
- public static Dictionary> _backupVertices = new Dictionary>();
+ public static Dictionary> BackupVertices = new Dictionary>();
public static void ExportActors(WldFile wldFile, Settings settings, ILogger logger)
{
@@ -62,7 +62,7 @@ private static void ExportZone(WldFileZone wldFile, Settings settings, ILogger l
var shortName = wldFile.ShortName;
// Get object instances within this zone file to map up and instantiate later
- PfsFile zoneObjectsFileInArchive = s3dArchive.GetFile("objects" + LanternStrings.WldFormatExtension);
+ var zoneObjectsFileInArchive = s3dArchive.GetFile("objects" + LanternStrings.WldFormatExtension);
if (zoneObjectsFileInArchive != null)
{
var zoneObjectsWldFile = new WldFileZoneObjects(zoneObjectsFileInArchive, shortName,
@@ -72,9 +72,9 @@ private static void ExportZone(WldFileZone wldFile, Settings settings, ILogger l
}
// Find associated _obj archive e.g. qeytoqrg_obj.s3d, open it and add meshes and materials to our list
- string objPath = path.Replace(".s3d", "_obj.s3d");
+ string objPath = EqFileHelper.ObjArchivePath(path);
string objArchive = Path.GetFileNameWithoutExtension(objPath);
- var s3dObjArchive = new PfsArchive(objPath, logger);
+ var s3dObjArchive = ArchiveFactory.GetArchive(objPath, logger);
if (s3dObjArchive.Initialize())
{
string wldFileName = objArchive + LanternStrings.WldFormatExtension;
@@ -254,7 +254,7 @@ private static void WriteAnimationFrame(SkeletonHierarchy skeleton, string anima
{
foreach (var mesh in skeleton.Meshes)
{
- _backupVertices[mesh] = MeshExportHelper.ShiftMeshVertices(mesh, skeleton,
+ BackupVertices[mesh] = MeshExportHelper.ShiftMeshVertices(mesh, skeleton,
wldFile.WldType == WldType.Characters, animation, frameIndex);
meshWriter.AddFragmentData(mesh);
}
@@ -266,7 +266,7 @@ private static void WriteAnimationFrame(SkeletonHierarchy skeleton, string anima
settings.ExportZoneMeshGroups, wldFile.ZoneShortname);
meshWriter2.SetIsCharacterModel(true);
meshWriter2.AddFragmentData(skeleton.Meshes[0]);
- _backupVertices[m2] = MeshExportHelper.ShiftMeshVertices(m2, skeleton,
+ BackupVertices[m2] = MeshExportHelper.ShiftMeshVertices(m2, skeleton,
wldFile.WldType == WldType.Characters, animation, frameIndex);
meshWriter2.AddFragmentData(m2);
meshWriter2.WriteAssetToFile(GetMeshPath(wldFile, FragmentNameCleaner.CleanName(skeleton), i + 1));
@@ -290,7 +290,7 @@ private static void WriteAnimationFrame(SkeletonHierarchy skeleton, string anima
private static void RestoreVertices()
{
- foreach (var shiftedMesh in _backupVertices)
+ foreach (var shiftedMesh in BackupVertices)
{
shiftedMesh.Key.Vertices = shiftedMesh.Value;
}
@@ -326,4 +326,4 @@ private static string GetMaterialListPath(WldFile wldFile, string materialListNa
materialListName + "_" + skinIndex + ".mtl";
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Exporters/ActorWriter.cs b/LanternExtractor/EQ/Wld/Exporters/ActorWriter.cs
index e673959..3e9dc83 100644
--- a/LanternExtractor/EQ/Wld/Exporters/ActorWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/ActorWriter.cs
@@ -30,30 +30,30 @@ public override void AddFragmentData(WldFragment data)
if (_actorType == ActorType.Skeletal)
{
- _export.Append(FragmentNameCleaner.CleanName(actor));
- _export.Append(",");
- _export.Append(FragmentNameCleaner.CleanName(actor.SkeletonReference.SkeletonHierarchy));
- _export.AppendLine();
+ Export.Append(FragmentNameCleaner.CleanName(actor));
+ Export.Append(",");
+ Export.Append(FragmentNameCleaner.CleanName(actor.SkeletonReference.SkeletonHierarchy));
+ Export.AppendLine();
}
else if (_actorType == ActorType.Static)
{
- _export.Append(FragmentNameCleaner.CleanName(actor));
- _export.Append(",");
+ Export.Append(FragmentNameCleaner.CleanName(actor));
+ Export.Append(",");
if (actor.MeshReference.Mesh != null)
{
- _export.Append(FragmentNameCleaner.CleanName(actor.MeshReference.Mesh));
+ Export.Append(FragmentNameCleaner.CleanName(actor.MeshReference.Mesh));
}
else if (actor.MeshReference.LegacyMesh != null)
{
- _export.Append(FragmentNameCleaner.CleanName(actor.MeshReference.LegacyMesh));
+ Export.Append(FragmentNameCleaner.CleanName(actor.MeshReference.LegacyMesh));
}
- _export.AppendLine();
+ Export.AppendLine();
}
else
{
- _export.AppendLine(FragmentNameCleaner.CleanName(actor));
+ Export.AppendLine(FragmentNameCleaner.CleanName(actor));
}
_actorCount++;
@@ -61,7 +61,7 @@ public override void AddFragmentData(WldFragment data)
public override void WriteAssetToFile(string fileName)
{
- if (_export.Length == 0)
+ if (Export.Length == 0)
{
return;
}
@@ -70,7 +70,7 @@ public override void WriteAssetToFile(string fileName)
headerBuilder.AppendLine(LanternStrings.ExportHeaderTitle +
"Actor List");
headerBuilder.AppendLine("# Total models: " + _actorCount);
- _export.Insert(0, headerBuilder.ToString());
+ Export.Insert(0, headerBuilder.ToString());
base.WriteAssetToFile(fileName);
}
diff --git a/LanternExtractor/EQ/Wld/Exporters/ActorWriterNew.cs b/LanternExtractor/EQ/Wld/Exporters/ActorWriterNew.cs
index 095ca6b..4b2993f 100644
--- a/LanternExtractor/EQ/Wld/Exporters/ActorWriterNew.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/ActorWriterNew.cs
@@ -15,24 +15,24 @@ public override void AddFragmentData(WldFragment data)
return;
}
- _export.Append(actor.ActorType.ToString());
- _export.Append(",");
- _export.Append(actor.ReferenceName);
+ Export.Append(actor.ActorType.ToString());
+ Export.Append(",");
+ Export.Append(actor.ReferenceName);
- _export.Append(FragmentNameCleaner.CleanName(actor));
- _export.AppendLine();
+ Export.Append(FragmentNameCleaner.CleanName(actor));
+ Export.AppendLine();
}
public override void WriteAssetToFile(string fileName)
{
- if (_export.Length == 0)
+ if (Export.Length == 0)
{
return;
}
StringBuilder headerBuilder = new StringBuilder();
headerBuilder.AppendLine(LanternStrings.ExportHeaderTitle + "Actor");
- _export.Insert(0, headerBuilder.ToString());
+ Export.Insert(0, headerBuilder.ToString());
base.WriteAssetToFile(fileName);
}
}
diff --git a/LanternExtractor/EQ/Wld/Exporters/ActorWriterNewGlobal.cs b/LanternExtractor/EQ/Wld/Exporters/ActorWriterNewGlobal.cs
index 8d0ba0d..38a3425 100644
--- a/LanternExtractor/EQ/Wld/Exporters/ActorWriterNewGlobal.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/ActorWriterNewGlobal.cs
@@ -88,7 +88,7 @@ public override void WriteAssetToFile(string fileName)
foreach (var o in _objects)
{
- _export.AppendLine(o);
+ Export.AppendLine(o);
}
//StringBuilder headerBuilder = new StringBuilder();
diff --git a/LanternExtractor/EQ/Wld/Exporters/AmbientLightColorWriter.cs b/LanternExtractor/EQ/Wld/Exporters/AmbientLightColorWriter.cs
index f0d75af..22cd4c0 100644
--- a/LanternExtractor/EQ/Wld/Exporters/AmbientLightColorWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/AmbientLightColorWriter.cs
@@ -6,8 +6,8 @@ public class AmbientLightColorWriter : TextAssetWriter
{
public AmbientLightColorWriter()
{
- _export.AppendLine(LanternStrings.ExportHeaderTitle + "Ambient Light Color");
- _export.AppendLine(LanternStrings.ExportHeaderFormat + "R, G, B");
+ Export.AppendLine(LanternStrings.ExportHeaderTitle + "Ambient Light Color");
+ Export.AppendLine(LanternStrings.ExportHeaderFormat + "R, G, B");
}
public override void AddFragmentData(WldFragment data)
@@ -19,11 +19,11 @@ public override void AddFragmentData(WldFragment data)
return;
}
- _export.Append(globalAmbientLight.Color.R.ToString());
- _export.Append(",");
- _export.Append(globalAmbientLight.Color.G.ToString());
- _export.Append(",");
- _export.Append(globalAmbientLight.Color.B.ToString());
+ Export.Append(globalAmbientLight.Color.R.ToString());
+ Export.Append(",");
+ Export.Append(globalAmbientLight.Color.G.ToString());
+ Export.Append(",");
+ Export.Append(globalAmbientLight.Color.B.ToString());
}
}
}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Wld/Exporters/AnimationWriter.cs b/LanternExtractor/EQ/Wld/Exporters/AnimationWriter.cs
index 6023733..019d1cb 100644
--- a/LanternExtractor/EQ/Wld/Exporters/AnimationWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/AnimationWriter.cs
@@ -10,7 +10,7 @@ public class AnimationWriter : TextAssetWriter
public AnimationWriter(bool isCharacterAnimation)
{
- _export.AppendLine(LanternStrings.ExportHeaderTitle + "Animation");
+ Export.AppendLine(LanternStrings.ExportHeaderTitle + "Animation");
_isCharacterAnimation = isCharacterAnimation;
}
@@ -38,9 +38,9 @@ public override void AddFragmentData(WldFragment data)
Animation anim = skeleton.Animations[_targetAnimation];
- _export.AppendLine("# Animation: " + _targetAnimation);
- _export.AppendLine("framecount," + anim.FrameCount);
- _export.AppendLine("totalTimeMs," + anim.AnimationTimeMs);
+ Export.AppendLine("# Animation: " + _targetAnimation);
+ Export.AppendLine("framecount," + anim.FrameCount);
+ Export.AppendLine("totalTimeMs," + anim.AnimationTimeMs);
for (int i = 0; i < skeleton.Skeleton.Count; ++i)
{
@@ -87,39 +87,39 @@ public override void AddFragmentData(WldFragment data)
private void CreateTrackString(string fullPath, int frame, BoneTransform boneTransform, int delay)
{
- _export.Append(fullPath);
- _export.Append(",");
+ Export.Append(fullPath);
+ Export.Append(",");
- _export.Append(frame);
- _export.Append(",");
+ Export.Append(frame);
+ Export.Append(",");
- _export.Append(boneTransform.Translation.x);
- _export.Append(",");
+ Export.Append(boneTransform.Translation.x);
+ Export.Append(",");
- _export.Append(boneTransform.Translation.z);
- _export.Append(",");
+ Export.Append(boneTransform.Translation.z);
+ Export.Append(",");
- _export.Append(boneTransform.Translation.y);
- _export.Append(",");
+ Export.Append(boneTransform.Translation.y);
+ Export.Append(",");
- _export.Append(-boneTransform.Rotation.x);
- _export.Append(",");
+ Export.Append(-boneTransform.Rotation.x);
+ Export.Append(",");
- _export.Append(-boneTransform.Rotation.z);
- _export.Append(",");
+ Export.Append(-boneTransform.Rotation.z);
+ Export.Append(",");
- _export.Append(-boneTransform.Rotation.y);
- _export.Append(",");
+ Export.Append(-boneTransform.Rotation.y);
+ Export.Append(",");
- _export.Append(boneTransform.Rotation.w);
- _export.Append(",");
+ Export.Append(boneTransform.Rotation.w);
+ Export.Append(",");
- _export.Append(boneTransform.Scale);
- _export.Append(",");
+ Export.Append(boneTransform.Scale);
+ Export.Append(",");
- _export.Append(delay.ToString());
+ Export.Append(delay.ToString());
- _export.AppendLine();
+ Export.AppendLine();
}
diff --git a/LanternExtractor/EQ/Wld/Exporters/BspTreeWriter.cs b/LanternExtractor/EQ/Wld/Exporters/BspTreeWriter.cs
index 3446750..729c6ca 100644
--- a/LanternExtractor/EQ/Wld/Exporters/BspTreeWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/BspTreeWriter.cs
@@ -8,10 +8,10 @@ public class BspTreeWriter : TextAssetWriter
{
public BspTreeWriter()
{
- _export.AppendLine(LanternStrings.ExportHeaderTitle + "BSP Tree");
- _export.AppendLine(LanternStrings.ExportHeaderFormat +
+ Export.AppendLine(LanternStrings.ExportHeaderTitle + "BSP Tree");
+ Export.AppendLine(LanternStrings.ExportHeaderFormat +
"Normal nodes: NormalX, NormalY, NormalZ, SplitDistance, LeftNodeId, RightNodeId");
- _export.AppendLine(LanternStrings.ExportHeaderFormat +
+ Export.AppendLine(LanternStrings.ExportHeaderFormat +
"Leaf nodes: BSPRegionId, RegionType");
}
@@ -29,24 +29,24 @@ public override void AddFragmentData(WldFragment data)
// Normal node
if (node.Region == null)
{
- _export.Append(node.NormalX.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(node.NormalZ.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(node.NormalY.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(node.SplitDistance.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(node.LeftNode.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(node.RightNode.ToString(_numberFormat));
- _export.AppendLine();
+ Export.Append(node.NormalX.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(node.NormalZ.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(node.NormalY.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(node.SplitDistance.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(node.LeftNode.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(node.RightNode.ToString(NumberFormat));
+ Export.AppendLine();
}
else
// Leaf node
{
- _export.Append(node.RegionId.ToString(_numberFormat));
- _export.Append(",");
+ Export.Append(node.RegionId.ToString(NumberFormat));
+ Export.Append(",");
string types = string.Empty;
@@ -67,11 +67,11 @@ public override void AddFragmentData(WldFragment data)
types = RegionType.Normal.ToString();
}
- _export.Append(types);
+ Export.Append(types);
if (node.Region.RegionType == null)
{
- _export.AppendLine();
+ Export.AppendLine();
continue;
}
@@ -81,30 +81,30 @@ public override void AddFragmentData(WldFragment data)
if (zoneline != null)
{
- _export.Append(",");
- _export.Append(zoneline.Type.ToString());
- _export.Append(",");
+ Export.Append(",");
+ Export.Append(zoneline.Type.ToString());
+ Export.Append(",");
if (zoneline.Type == ZonelineType.Reference)
{
- _export.Append(zoneline.Index);
+ Export.Append(zoneline.Index);
}
else
{
- _export.Append(zoneline.ZoneIndex);
- _export.Append(",");
- _export.Append(zoneline.Position.x);
- _export.Append(",");
- _export.Append(zoneline.Position.y);
- _export.Append(",");
- _export.Append(zoneline.Position.z);
- _export.Append(",");
- _export.Append(zoneline.Heading);
+ Export.Append(zoneline.ZoneIndex);
+ Export.Append(",");
+ Export.Append(zoneline.Position.x);
+ Export.Append(",");
+ Export.Append(zoneline.Position.y);
+ Export.Append(",");
+ Export.Append(zoneline.Position.z);
+ Export.Append(",");
+ Export.Append(zoneline.Heading);
}
}
}
- _export.AppendLine();
+ Export.AppendLine();
}
}
}
diff --git a/LanternExtractor/EQ/Wld/Exporters/GltfWriter.cs b/LanternExtractor/EQ/Wld/Exporters/GltfWriter.cs
index 85294f3..b4baea4 100644
--- a/LanternExtractor/EQ/Wld/Exporters/GltfWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/GltfWriter.cs
@@ -81,13 +81,13 @@ public enum ModelGenerationMode
"p03", // rotating
"p06", // swim
"p07", // sitting
- "p08", // stand (arms at sides)
+ "p08", // stand (arms at sides)
"sky"
};
private static readonly Matrix4x4 MirrorXAxisMatrix = Matrix4x4.CreateReflection(new Plane(1, 0, 0, 0));
private static readonly Matrix4x4 CorrectedWorldMatrix = MirrorXAxisMatrix * Matrix4x4.CreateScale(0.1f);
-
+
private SceneBuilder _scene;
private IMeshBuilder _combinedMeshBuilder;
private ISet _meshMaterialsToSkip;
@@ -110,7 +110,7 @@ public GltfWriter(bool exportVertexColors, GltfExportFormat exportFormat, ILogge
public override void AddFragmentData(WldFragment fragment)
{
AddFragmentData(
- mesh:(Mesh)fragment,
+ mesh:(Mesh)fragment,
generationMode:ModelGenerationMode.Separate );
}
@@ -123,10 +123,10 @@ public void AddFragmentData(Mesh mesh, SkeletonHierarchy skeleton,
}
AddFragmentData(
- mesh: mesh,
- generationMode: ModelGenerationMode.Combine,
- isSkinned: true,
- meshNameOverride: meshNameOverride,
+ mesh: mesh,
+ generationMode: ModelGenerationMode.Combine,
+ isSkinned: true,
+ meshNameOverride: meshNameOverride,
singularBoneIndex: singularBoneIndex);
}
@@ -167,7 +167,7 @@ public void GenerateGltfMaterials(IEnumerable materialLists, strin
if (string.IsNullOrEmpty(imageFileNameWithoutExtension)) continue;
var imagePath = $"{textureImageFolder}{eqMaterial.GetFirstBitmapExportFilename()}";
-
+
ImageBuilder imageBuilder;
if (ShaderTypesThatNeedAlphaAddedToImage.Contains(eqMaterial.ShaderType))
{
@@ -187,7 +187,7 @@ public void GenerateGltfMaterials(IEnumerable materialLists, strin
var imageName = Path.GetFileNameWithoutExtension(imagePath);
imageBuilder = ImageBuilder.From(new MemoryImage(imagePath), imageName);
}
-
+
var gltfMaterial = new MaterialBuilder(materialName)
.WithDoubleSide(false)
.WithMetallicRoughnessShader()
@@ -235,13 +235,13 @@ public void GenerateGltfMaterials(IEnumerable materialLists, strin
}
public void AddFragmentData(
- Mesh mesh,
- ModelGenerationMode generationMode,
- bool isSkinned = false,
+ Mesh mesh,
+ ModelGenerationMode generationMode,
+ bool isSkinned = false,
string meshNameOverride = null,
- int singularBoneIndex = -1,
- ObjectInstance objectInstance = null,
- int instanceIndex = 0,
+ int singularBoneIndex = -1,
+ ObjectInstance objectInstance = null,
+ int instanceIndex = 0,
bool isZoneMesh = false)
{
var meshName = meshNameOverride ?? FragmentNameCleaner.CleanName(mesh);
@@ -251,8 +251,8 @@ public void AddFragmentData(
var canExportVertexColors = _exportVertexColors &&
((objectInstance?.Colors?.Colors != null && objectInstance.Colors.Colors.Any())
|| (mesh?.Colors != null && mesh.Colors.Any()));
-
- if (mesh.AnimatedVerticesReference != null && !canExportVertexColors && objectInstance != null &&
+
+ if (mesh.AnimatedVerticesReference != null && !canExportVertexColors && objectInstance != null &&
_sharedMeshes.TryGetValue(meshName, out var existingMesh))
{
if (generationMode == ModelGenerationMode.Separate)
@@ -284,7 +284,7 @@ public void AddFragmentData(
// Keeping track of vertex indexes for each vertex position in case it's an
// animated mesh so we can create morph targets later
var gltfVertexPositionToWldVertexIndex = new Dictionary();
-
+
var polygonIndex = 0;
foreach (var materialGroup in mesh.MaterialGroups)
{
@@ -338,7 +338,7 @@ public void AddFragmentData(
{
_scene.AddRigidMesh(gltfMesh, transformMatrix);
_sharedMeshes[meshName] = gltfMesh;
- }
+ }
}
}
@@ -355,9 +355,9 @@ public void ApplyAnimationToSkeleton(SkeletonHierarchy skeleton, string animatio
var poseArray = isCharacterAnimation
? skeleton.Animations[DefaultModelPoseAnimationKey].TracksCleanedStripped
: skeleton.Animations[DefaultModelPoseAnimationKey].TracksCleaned;
-
+
if (poseArray == null) return;
-
+
for (var i = 0; i < skeleton.Skeleton.Count; i++)
{
var boneName = isCharacterAnimation
@@ -396,9 +396,9 @@ public void ApplyAnimationToSkeleton(SkeletonHierarchy skeleton, string animatio
}
public void AddCombinedMeshToScene(
- bool isZoneMesh = false,
- string meshName = null,
- string skeletonModelBase = null,
+ bool isZoneMesh = false,
+ string meshName = null,
+ string skeletonModelBase = null,
ObjectInstance objectInstance = null)
{
IMeshBuilder combinedMesh;
@@ -625,7 +625,7 @@ private IVertexBuilder GetGltfVertex(
return (VertexBuilder)vertexBuilder;
}
- private (Vector4 v0, Vector4 v1, Vector4 v2) GetVertexColorVectors(Mesh mesh,
+ private (Vector4 v0, Vector4 v1, Vector4 v2) GetVertexColorVectors(Mesh mesh,
(int v0, int v1, int v2) vertexIndices, ObjectInstance objectInstance = null)
{
var objInstanceColors = objectInstance?.Colors?.Colors ?? new List();
@@ -659,11 +659,12 @@ private void AddAnimatedMeshMorphTargets(Mesh mesh, IMeshBuilder();
var weights = new List();
- var frameDelay = mesh.AnimatedVerticesReference.MeshAnimatedVertices.Delay/1000f;
+ var animatedVertices = mesh.AnimatedVerticesReference.GetAnimatedVertices();
+ var frameDelay = animatedVertices.Delay/1000f;
- for (var frame = 0; frame < mesh.AnimatedVerticesReference.MeshAnimatedVertices.Frames.Count; frame++)
+ for (var frame = 0; frame < animatedVertices.Frames.Count; frame++)
{
- var vertexPositionsForFrame = mesh.AnimatedVerticesReference.MeshAnimatedVertices.Frames[frame];
+ var vertexPositionsForFrame = animatedVertices.Frames[frame];
var morphTarget = gltfMesh.UseMorphTarget(frame);
foreach (var vertexGeometry in gltfVertexPositionToWldVertexIndex.Keys)
@@ -720,7 +721,7 @@ private List AddNewSkeleton(SkeletonHierarchy skeleton)
return skeletonNodes;
}
- private void ApplyBoneTransformation(NodeBuilder boneNode, DataTypes.BoneTransform boneTransform,
+ private void ApplyBoneTransformation(NodeBuilder boneNode, DataTypes.BoneTransform boneTransform,
string animationKey, int timeMs, bool staticPose)
{
var scaleVector = new Vector3(boneTransform.Scale);
diff --git a/LanternExtractor/EQ/Wld/Exporters/LegacyMeshIntermediateAssetWriter.cs b/LanternExtractor/EQ/Wld/Exporters/LegacyMeshIntermediateAssetWriter.cs
index df69add..ccdc0db 100644
--- a/LanternExtractor/EQ/Wld/Exporters/LegacyMeshIntermediateAssetWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/LegacyMeshIntermediateAssetWriter.cs
@@ -1,4 +1,6 @@
-using LanternExtractor.EQ.Wld.DataTypes;
+using System;
+using GlmSharp;
+using LanternExtractor.EQ.Wld.DataTypes;
using LanternExtractor.EQ.Wld.Fragments;
using LanternExtractor.EQ.Wld.Helpers;
@@ -9,84 +11,201 @@ namespace LanternExtractor.EQ.Wld.Exporters
///
public class LegacyMeshIntermediateAssetWriter : TextAssetWriter
{
+ private bool _useGroups;
+ private bool _isCollisionMesh;
+ private bool _isFirstMesh = true;
+ private int _currentBaseIndex;
+
+ public override void ClearExportData()
+ {
+ base.ClearExportData();
+ _isFirstMesh = true;
+ _currentBaseIndex = 0;
+ }
+
+ public LegacyMeshIntermediateAssetWriter(bool useGroups, bool isCollisionMesh)
+ {
+ _useGroups = useGroups;
+ _isCollisionMesh = isCollisionMesh;
+ }
+
public override void AddFragmentData(WldFragment data)
{
- if (!(data is LegacyMesh am))
+ if (!(data is LegacyMesh lm))
{
return;
}
-
- _export.AppendLine(LanternStrings.ExportHeaderTitle + "Alternate Mesh Intermediate Format");
- _export.AppendLine($"ml,{FragmentNameCleaner.CleanName(am.MaterialList)}");
- foreach (var v in am.Vertices)
+ if (_isCollisionMesh && lm.PolyhedronReference != null)
{
- _export.Append("v");
- _export.Append(",");
- _export.Append(v.x);
- _export.Append(",");
- _export.Append(v.z);
- _export.Append(",");
- _export.Append(v.y);
- _export.AppendLine();
+ var polyhedron = lm.PolyhedronReference.Polyhedron;
+
+ // TODO: polyhedron scale factor
+ foreach(var vertex in polyhedron.Vertices)
+ {
+ Export.Append("v");
+ Export.Append(",");
+ Export.Append(vertex.x + lm.Center.x);
+ Export.Append(",");
+ Export.Append(vertex.z + lm.Center.z);
+ Export.Append(",");
+ Export.Append(vertex.y + lm.Center.y);
+ Export.AppendLine();
+ }
+
+ foreach(var polygon in polyhedron.Faces)
+ {
+ Export.Append("i");
+ Export.Append(",");
+ Export.Append(0);
+ Export.Append(",");
+ Export.Append(_currentBaseIndex + polygon.Vertex1);
+ Export.Append(",");
+ Export.Append(_currentBaseIndex + polygon.Vertex2);
+ Export.Append(",");
+ Export.Append(_currentBaseIndex + polygon.Vertex3);
+ Export.AppendLine();
+ }
+
+ return;
}
-
- foreach (var uv in am.TexCoords)
+
+ if (!_isCollisionMesh && (_isFirstMesh || _useGroups))
{
- _export.Append("uv");
- _export.Append(",");
- _export.Append(uv.x);
- _export.Append(",");
- _export.Append(uv.y);
- _export.AppendLine();
+ Export.Append("ml");
+ Export.Append(",");
+ Export.Append(FragmentNameCleaner.CleanName(lm.MaterialList));
+ Export.AppendLine();
+ _isFirstMesh = false;
}
-
- foreach (var n in am.Normals)
+
+ foreach (var vertex in lm.Vertices)
+ {
+ Export.Append("v");
+ Export.Append(",");
+ Export.Append(vertex.x + lm.Center.x);
+ Export.Append(",");
+ Export.Append(vertex.z + lm.Center.z);
+ Export.Append(",");
+ Export.Append(vertex.y + lm.Center.y);
+ Export.AppendLine();
+ }
+
+ foreach (var uv in lm.TexCoords)
+ {
+ Export.Append("uv");
+ Export.Append(",");
+ Export.Append(uv.x);
+ Export.Append(",");
+ Export.Append(uv.y);
+ Export.AppendLine();
+ }
+
+ foreach (var normal in lm.Normals)
{
- _export.Append("n");
- _export.Append(",");
- _export.Append(n.x);
- _export.Append(",");
- _export.Append(n.y);
- _export.Append(",");
- _export.Append(n.z);
- _export.AppendLine();
+ Export.Append("n");
+ Export.Append(",");
+ Export.Append(normal.x);
+ Export.Append(",");
+ Export.Append(normal.y);
+ Export.Append(",");
+ Export.Append(normal.z);
+ Export.AppendLine();
}
int currentPolygon = 0;
- for (var i = 0; i < am.RenderGroups.Count; i++)
+ for (var i = 0; i < lm.RenderGroups.Count; i++)
{
- var renderGroup = am.RenderGroups[i];
+ var renderGroup = lm.RenderGroups[i];
for (int j = 0; j < renderGroup.PolygonCount; ++j)
{
- Polygon polygon = am.Polygons[j + currentPolygon];
-
- _export.Append("i");
- _export.Append(",");
- _export.Append(renderGroup.MaterialIndex);
- _export.Append(",");
- _export.Append(polygon.Vertex1);
- _export.Append(",");
- _export.Append(polygon.Vertex2);
- _export.Append(",");
- _export.Append(polygon.Vertex3);
- _export.AppendLine();
+ Polygon polygon = lm.Polygons[currentPolygon];
+ currentPolygon++;
+
+ Export.Append("i");
+ Export.Append(",");
+ Export.Append(renderGroup.MaterialIndex);
+ Export.Append(",");
+ Export.Append(_currentBaseIndex + polygon.Vertex1);
+ Export.Append(",");
+ Export.Append(_currentBaseIndex + polygon.Vertex2);
+ Export.Append(",");
+ Export.Append(_currentBaseIndex + polygon.Vertex3);
+ Export.AppendLine();
+ }
+ }
+
+ if (lm.RenderGroups.Count == 0)
+ {
+ foreach (var polygon in lm.Polygons)
+ {
+ Export.Append("i");
+ Export.Append(",");
+ Export.Append(polygon.MaterialIndex);
+ Export.Append(",");
+ Export.Append(_currentBaseIndex + polygon.Vertex1);
+ Export.Append(",");
+ Export.Append(_currentBaseIndex + polygon.Vertex2);
+ Export.Append(",");
+ Export.Append(_currentBaseIndex + polygon.Vertex3);
+ Export.AppendLine();
}
+ }
- currentPolygon += renderGroup.PolygonCount;
+ foreach (var bone in lm.MobPieces)
+ {
+ Export.Append("b");
+ Export.Append(",");
+ Export.Append(bone.Key);
+ Export.Append(",");
+ Export.Append(bone.Value.Start);
+ Export.Append(",");
+ Export.Append(bone.Value.Count);
+ Export.AppendLine();
}
-
- foreach (var bone in am.MobPieces)
+
+ var animatedVertices = lm.AnimatedVerticesReference?.GetAnimatedVertices();
+ if (animatedVertices != null && !_isCollisionMesh)
{
- _export.Append("b");
- _export.Append(",");
- _export.Append(bone.Key);
- _export.Append(",");
- _export.Append(bone.Value.Start);
- _export.Append(",");
- _export.Append(bone.Value.Count);
- _export.AppendLine();
+ Export.Append("ad");
+ Export.Append(",");
+ Export.Append(animatedVertices.Delay);
+ Export.AppendLine();
+
+ for (var i = 0; i < animatedVertices.Frames.Count; i++)
+ {
+ foreach (vec3 position in animatedVertices.Frames[i])
+ {
+ Export.Append("av");
+ Export.Append(",");
+ Export.Append(i);
+ Export.Append(",");
+ Export.Append(position.x + lm.Center.x);
+ Export.Append(",");
+ Export.Append(position.z + lm.Center.z);
+ Export.Append(",");
+ Export.Append(position.y + lm.Center.y);
+ Export.AppendLine();
+ }
+ }
}
+
+ if (!_useGroups)
+ {
+ _currentBaseIndex += lm.Vertices.Count;
+ }
+ }
+
+ public override void WriteAssetToFile(string fileName)
+ {
+ if (Export.Length == 0)
+ {
+ return;
+ }
+
+ Export.Insert(0, LanternStrings.ExportHeaderTitle + "Alternate Mesh Intermediate Format" + Environment.NewLine);
+
+ base.WriteAssetToFile(fileName);
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Exporters/LightInstancesWriter.cs b/LanternExtractor/EQ/Wld/Exporters/LightInstancesWriter.cs
index 2e23340..37a6999 100644
--- a/LanternExtractor/EQ/Wld/Exporters/LightInstancesWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/LightInstancesWriter.cs
@@ -6,8 +6,8 @@ public class LightInstancesWriter : TextAssetWriter
{
public LightInstancesWriter()
{
- _export.AppendLine(LanternStrings.ExportHeaderTitle + "Light Instances");
- _export.AppendLine(LanternStrings.ExportHeaderFormat +
+ Export.AppendLine(LanternStrings.ExportHeaderTitle + "Light Instances");
+ Export.AppendLine(LanternStrings.ExportHeaderFormat +
"PosX, PosY, PosZ, Radius, ColorR, ColorG, ColorB");
}
@@ -20,20 +20,20 @@ public override void AddFragmentData(WldFragment data)
return;
}
- _export.Append(light.Position.x.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(light.Position.z.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(light.Position.y.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(light.Radius.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(light.LightReference.LightSource.Color.r.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(light.LightReference.LightSource.Color.g.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(light.LightReference.LightSource.Color.b.ToString(_numberFormat));
- _export.AppendLine();
+ Export.Append(light.Position.x.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(light.Position.z.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(light.Position.y.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(light.Radius.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(light.LightReference.LightSource.Color.r.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(light.LightReference.LightSource.Color.g.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(light.LightReference.LightSource.Color.b.ToString(NumberFormat));
+ Export.AppendLine();
}
}
}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Wld/Exporters/MeshExporter.cs b/LanternExtractor/EQ/Wld/Exporters/MeshExporter.cs
index edc0edb..b9d2e69 100644
--- a/LanternExtractor/EQ/Wld/Exporters/MeshExporter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/MeshExporter.cs
@@ -11,6 +11,17 @@ public static class MeshExporter
{
public static void ExportMeshes(WldFile wldFile, Settings settings, ILogger logger)
{
+ var meshFolder = "Meshes/";
+ var legacyMeshFolder = "AlternateMeshes/";
+
+ // FIXME: Surface this as a config?
+ // Some legacy meshes will be overwritten
+ var mergeMeshFolders = true;
+ if (mergeMeshFolders)
+ {
+ legacyMeshFolder = meshFolder;
+ }
+
List meshes = wldFile.GetFragmentsOfType();
List materialLists = wldFile.GetFragmentsOfType();
List legacyMeshes = wldFile.GetFragmentsOfType();
@@ -21,10 +32,11 @@ public static void ExportMeshes(WldFile wldFile, Settings settings, ILogger logg
}
string exportFolder = wldFile.GetExportFolderForWldType() + "/";
-
+
var meshWriter = new MeshIntermediateAssetWriter(settings.ExportZoneMeshGroups, false);
- var legacyMeshWriter = new LegacyMeshIntermediateAssetWriter();
+ var legacyMeshWriter = new LegacyMeshIntermediateAssetWriter(settings.ExportZoneMeshGroups, false);
var collisionMeshWriter = new MeshIntermediateAssetWriter(settings.ExportZoneMeshGroups, true);
+ var collisionLegacyMeshWriter = new LegacyMeshIntermediateAssetWriter(settings.ExportZoneMeshGroups, true);
var materialListWriter = new MeshIntermediateMaterialsWriter();
bool exportEachPass = wldFile.WldType != WldType.Zone || settings.ExportZoneMeshGroups;
@@ -32,34 +44,12 @@ public static void ExportMeshes(WldFile wldFile, Settings settings, ILogger logg
// If it's a zone mesh, we need to ensure we should export a collision mesh.
// There are some zones with no non solid polygons (e.g. arena). No collision mesh is exported in this case.
// For objects, it's done for each fragment
- bool exportCollisionMesh = !exportEachPass && meshes.Where(m => m.ExportSeparateCollision).Any();
- /*if (!exportEachPass)
+ bool exportCollisionMesh = false;
+ if (!exportEachPass)
{
- foreach (Mesh mesh in meshes)
- {
- if (!mesh.ExportSeparateCollision)
- {
- continue;
- }
-
- exportCollisionMesh = true;
- break;
- }
-
- if (legacyMeshes != null)
- {
- foreach (var alternateMesh in legacyMeshes)
- {
- // if (!mesh.ExportSeparateCollision)
- // {
- // continue;
- // }
-
- exportCollisionMesh = true;
- break;
- }
- }
- }*/
+ exportCollisionMesh = meshes.Where(m => m.ExportSeparateCollision).Any() ||
+ legacyMeshes.Where(m => m.ExportSeparateCollision).Any();
+ }
// Export materials
if (materialLists != null)
@@ -76,7 +66,7 @@ public static void ExportMeshes(WldFile wldFile, Settings settings, ILogger logg
{
continue;
}
-
+
if (settings.ExportCharactersToSingleFolder && wldFile.WldType == WldType.Characters)
{
if (File.Exists(filePath))
@@ -92,7 +82,7 @@ public static void ExportMeshes(WldFile wldFile, Settings settings, ILogger logg
}
}
}
-
+
materialList.HasBeenExported = true;
materialListWriter.WriteAssetToFile(filePath);
materialListWriter.ClearExportData();
@@ -107,6 +97,44 @@ public static void ExportMeshes(WldFile wldFile, Settings settings, ILogger logg
materialListWriter.WriteAssetToFile(filePath);
}
+ if (legacyMeshes != null)
+ {
+ foreach (var alternateMesh in legacyMeshes)
+ {
+ legacyMeshWriter.AddFragmentData(alternateMesh);
+
+ // Determine if we need collision
+ if (exportEachPass)
+ {
+ exportCollisionMesh = alternateMesh.ExportSeparateCollision;
+ }
+
+ if (exportCollisionMesh)
+ {
+ collisionLegacyMeshWriter.AddFragmentData(alternateMesh);
+ }
+
+ if (exportEachPass)
+ {
+ var newExportFolder = wldFile.GetExportFolderForWldType() + legacyMeshFolder;
+ Directory.CreateDirectory(newExportFolder);
+ legacyMeshWriter.WriteAssetToFile(newExportFolder +
+ FragmentNameCleaner.CleanName(alternateMesh) +
+ ".txt");
+ legacyMeshWriter.ClearExportData();
+
+ if (exportCollisionMesh)
+ {
+ collisionLegacyMeshWriter.WriteAssetToFile(exportFolder + legacyMeshFolder +
+ FragmentNameCleaner.CleanName(alternateMesh) +
+ "_collision" +
+ ".txt");
+ collisionLegacyMeshWriter.ClearExportData();
+ }
+ }
+ }
+ }
+
// Exporting meshes
foreach (Mesh mesh in meshes)
{
@@ -114,7 +142,7 @@ public static void ExportMeshes(WldFile wldFile, Settings settings, ILogger logg
{
continue;
}
-
+
meshWriter.AddFragmentData(mesh);
// Determine if we need collision
@@ -141,13 +169,13 @@ public static void ExportMeshes(WldFile wldFile, Settings settings, ILogger logg
}
}
- meshWriter.WriteAssetToFile(exportFolder + "Meshes/" + FragmentNameCleaner.CleanName(mesh) +
+ meshWriter.WriteAssetToFile(exportFolder + meshFolder + FragmentNameCleaner.CleanName(mesh) +
".txt");
meshWriter.ClearExportData();
if (exportCollisionMesh)
{
- collisionMeshWriter.WriteAssetToFile(exportFolder + "Meshes/" +
+ collisionMeshWriter.WriteAssetToFile(exportFolder + meshFolder +
FragmentNameCleaner.CleanName(mesh) +
"_collision" +
".txt");
@@ -156,36 +184,18 @@ public static void ExportMeshes(WldFile wldFile, Settings settings, ILogger logg
}
}
- if (legacyMeshes != null)
- {
- foreach (var alternateMesh in legacyMeshes)
- {
- legacyMeshWriter.AddFragmentData(alternateMesh);
-
- if (exportEachPass)
- {
- var newExportFolder = wldFile.GetExportFolderForWldType() + "/AlternateMeshes/";
- Directory.CreateDirectory(newExportFolder);
- legacyMeshWriter.WriteAssetToFile(newExportFolder +
- FragmentNameCleaner.CleanName(alternateMesh) +
- ".txt");
- legacyMeshWriter.ClearExportData();
- }
- }
- }
-
-
if (!exportEachPass)
{
- meshWriter.WriteAssetToFile(exportFolder + "Meshes/" + wldFile.ZoneShortname +
- ".txt");
+ legacyMeshWriter.WriteAssetToFile(exportFolder + legacyMeshFolder + wldFile.ZoneShortname + ".txt");
+ meshWriter.WriteAssetToFile(exportFolder + meshFolder + wldFile.ZoneShortname + ".txt");
if (exportCollisionMesh)
{
- collisionMeshWriter.WriteAssetToFile(exportFolder + "Meshes/" + wldFile.ZoneShortname + "_collision" +
- ".txt");
+ var collisionFileName = wldFile.ZoneShortname + "_collision" + ".txt";
+ collisionLegacyMeshWriter.WriteAssetToFile(exportFolder + legacyMeshFolder + collisionFileName);
+ collisionMeshWriter.WriteAssetToFile(exportFolder + meshFolder + collisionFileName);
}
}
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Exporters/MeshIntermediateAssetWriter.cs b/LanternExtractor/EQ/Wld/Exporters/MeshIntermediateAssetWriter.cs
index ccffcce..950edee 100644
--- a/LanternExtractor/EQ/Wld/Exporters/MeshIntermediateAssetWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/MeshIntermediateAssetWriter.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using GlmSharp;
using LanternExtractor.EQ.Wld.DataTypes;
@@ -24,9 +25,8 @@ public MeshIntermediateAssetWriter(bool useGroups, bool isCollisionMesh)
{
_useGroups = useGroups;
_isCollisionMesh = isCollisionMesh;
- _export.AppendLine("# Lantern Test Intermediate Format");
}
-
+
public override void AddFragmentData(WldFragment data)
{
Mesh mesh = data as Mesh;
@@ -35,7 +35,7 @@ public override void AddFragmentData(WldFragment data)
{
return;
}
-
+
HashSet usedVertices = new HashSet();
List newIndices = new List();
@@ -46,7 +46,7 @@ public override void AddFragmentData(WldFragment data)
for (int i = 0; i < group.PolygonCount; ++i)
{
Polygon polygon = mesh.Indices[currentPolygon];
-
+
newIndices.Add(polygon.GetCopy());
currentPolygon++;
@@ -54,7 +54,7 @@ public override void AddFragmentData(WldFragment data)
{
continue;
}
-
+
usedVertices.Add(polygon.Vertex1);
usedVertices.Add(polygon.Vertex2);
usedVertices.Add(polygon.Vertex3);
@@ -81,7 +81,7 @@ public override void AddFragmentData(WldFragment data)
}
unusedVertices++;
-
+
foreach (var polygon in newIndices)
{
if (polygon.Vertex1 >= i && polygon.Vertex1 != 0)
@@ -101,10 +101,10 @@ public override void AddFragmentData(WldFragment data)
if (!_isCollisionMesh && (_isFirstMesh || _useGroups))
{
- _export.Append("ml");
- _export.Append(",");
- _export.Append(FragmentNameCleaner.CleanName(mesh.MaterialList));
- _export.AppendLine();
+ Export.Append("ml");
+ Export.Append(",");
+ Export.Append(FragmentNameCleaner.CleanName(mesh.MaterialList));
+ Export.AppendLine();
_isFirstMesh = false;
}
@@ -114,16 +114,16 @@ public override void AddFragmentData(WldFragment data)
{
continue;
}
-
+
var vertex = mesh.Vertices[i];
- _export.Append("v");
- _export.Append(",");
- _export.Append(vertex.x + mesh.Center.x);
- _export.Append(",");
- _export.Append(vertex.z + mesh.Center.z);
- _export.Append(",");
- _export.Append(vertex.y + mesh.Center.y);
- _export.AppendLine();
+ Export.Append("v");
+ Export.Append(",");
+ Export.Append(vertex.x + mesh.Center.x);
+ Export.Append(",");
+ Export.Append(vertex.z + mesh.Center.z);
+ Export.Append(",");
+ Export.Append(vertex.y + mesh.Center.y);
+ Export.AppendLine();
}
for (var i = 0; i < mesh.TextureUvCoordinates.Count; i++)
@@ -132,14 +132,14 @@ public override void AddFragmentData(WldFragment data)
{
continue;
}
-
+
var textureUv = mesh.TextureUvCoordinates[i];
- _export.Append("uv");
- _export.Append(",");
- _export.Append(textureUv.x);
- _export.Append(",");
- _export.Append(textureUv.y);
- _export.AppendLine();
+ Export.Append("uv");
+ Export.Append(",");
+ Export.Append(textureUv.x);
+ Export.Append(",");
+ Export.Append(textureUv.y);
+ Export.AppendLine();
}
for (var i = 0; i < mesh.Normals.Count; i++)
@@ -148,16 +148,16 @@ public override void AddFragmentData(WldFragment data)
{
continue;
}
-
+
var normal = mesh.Normals[i];
- _export.Append("n");
- _export.Append(",");
- _export.Append(normal.x);
- _export.Append(",");
- _export.Append(normal.y);
- _export.Append(",");
- _export.Append(normal.z);
- _export.AppendLine();
+ Export.Append("n");
+ Export.Append(",");
+ Export.Append(normal.x);
+ Export.Append(",");
+ Export.Append(normal.y);
+ Export.Append(",");
+ Export.Append(normal.z);
+ Export.AppendLine();
}
for (var i = 0; i < mesh.Colors.Count; i++)
@@ -166,18 +166,18 @@ public override void AddFragmentData(WldFragment data)
{
continue;
}
-
+
var vertexColor = mesh.Colors[i];
- _export.Append("c");
- _export.Append(",");
- _export.Append(vertexColor.B);
- _export.Append(",");
- _export.Append(vertexColor.G);
- _export.Append(",");
- _export.Append(vertexColor.R);
- _export.Append(",");
- _export.Append(vertexColor.A);
- _export.AppendLine();
+ Export.Append("c");
+ Export.Append(",");
+ Export.Append(vertexColor.B);
+ Export.Append(",");
+ Export.Append(vertexColor.G);
+ Export.Append(",");
+ Export.Append(vertexColor.R);
+ Export.Append(",");
+ Export.Append(vertexColor.A);
+ Export.AppendLine();
}
currentPolygon = 0;
@@ -197,56 +197,57 @@ public override void AddFragmentData(WldFragment data)
for (int i = 0; i < group.PolygonCount; ++i)
{
Polygon polygon = newIndices[currentPolygon];
-
+
currentPolygon++;
-
- _export.Append("i");
- _export.Append(",");
- _export.Append(group.MaterialIndex);
- _export.Append(",");
- _export.Append(_currentBaseIndex + polygon.Vertex1);
- _export.Append(",");
- _export.Append(_currentBaseIndex + polygon.Vertex2);
- _export.Append(",");
- _export.Append(_currentBaseIndex + polygon.Vertex3);
- _export.AppendLine();
+
+ Export.Append("i");
+ Export.Append(",");
+ Export.Append(group.MaterialIndex);
+ Export.Append(",");
+ Export.Append(_currentBaseIndex + polygon.Vertex1);
+ Export.Append(",");
+ Export.Append(_currentBaseIndex + polygon.Vertex2);
+ Export.Append(",");
+ Export.Append(_currentBaseIndex + polygon.Vertex3);
+ Export.AppendLine();
}
}
-
+
foreach (var bone in mesh.MobPieces)
{
- _export.Append("b");
- _export.Append(",");
- _export.Append(bone.Key);
- _export.Append(",");
- _export.Append(bone.Value.Start);
- _export.Append(",");
- _export.Append(bone.Value.Count);
- _export.AppendLine();
+ Export.Append("b");
+ Export.Append(",");
+ Export.Append(bone.Key);
+ Export.Append(",");
+ Export.Append(bone.Value.Start);
+ Export.Append(",");
+ Export.Append(bone.Value.Count);
+ Export.AppendLine();
}
- if (mesh.AnimatedVerticesReference != null && !_isCollisionMesh)
+ var animatedVertices = mesh.AnimatedVerticesReference?.GetAnimatedVertices();
+ if (animatedVertices != null && !_isCollisionMesh)
{
- _export.Append("ad");
- _export.Append(",");
- _export.Append(mesh.AnimatedVerticesReference.MeshAnimatedVertices.Delay);
- _export.AppendLine();
+ Export.Append("ad");
+ Export.Append(",");
+ Export.Append(animatedVertices.Delay);
+ Export.AppendLine();
- for (var i = 0; i < mesh.AnimatedVerticesReference.MeshAnimatedVertices.Frames.Count; i++)
+ for (var i = 0; i < animatedVertices.Frames.Count; i++)
{
- List frame = mesh.AnimatedVerticesReference.MeshAnimatedVertices.Frames[i];
+ List frame = animatedVertices.Frames[i];
foreach (vec3 position in frame)
{
- _export.Append("av");
- _export.Append(",");
- _export.Append(i);
- _export.Append(",");
- _export.Append(position.x + mesh.Center.x);
- _export.Append(",");
- _export.Append(position.z + mesh.Center.z);
- _export.Append(",");
- _export.Append(position.y + mesh.Center.y);
- _export.AppendLine();
+ Export.Append("av");
+ Export.Append(",");
+ Export.Append(i);
+ Export.Append(",");
+ Export.Append(position.x + mesh.Center.x);
+ Export.Append(",");
+ Export.Append(position.z + mesh.Center.z);
+ Export.Append(",");
+ Export.Append(position.y + mesh.Center.y);
+ Export.AppendLine();
}
}
}
@@ -256,5 +257,17 @@ public override void AddFragmentData(WldFragment data)
_currentBaseIndex += mesh.Vertices.Count - unusedVertices;
}
}
+
+ public override void WriteAssetToFile(string fileName)
+ {
+ if (Export.Length == 0)
+ {
+ return;
+ }
+
+ Export.Insert(0, LanternStrings.ExportHeaderTitle + "Mesh Intermediate Format" + Environment.NewLine);
+
+ base.WriteAssetToFile(fileName);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Exporters/MeshIntermediateMaterialsExporter.cs b/LanternExtractor/EQ/Wld/Exporters/MeshIntermediateMaterialsExporter.cs
index 12e7e4f..e894dc4 100644
--- a/LanternExtractor/EQ/Wld/Exporters/MeshIntermediateMaterialsExporter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/MeshIntermediateMaterialsExporter.cs
@@ -14,14 +14,14 @@ public override void AddFragmentData(WldFragment data)
return;
}
- _export.AppendLine(LanternStrings.ExportHeaderTitle + "Material List Intermediate Format");
- _export.AppendLine(LanternStrings.ExportHeaderFormat + "Index, MaterialName, AnimationTextures, AnimationDelayMs, SkinTextures");
+ Export.AppendLine(LanternStrings.ExportHeaderTitle + "Material List Intermediate Format");
+ Export.AppendLine(LanternStrings.ExportHeaderFormat + "Index, MaterialName, AnimationTextures, AnimationDelayMs, SkinTextures");
for (int i = 0; i < list.Materials.Count; i++)
{
Material material = list.Materials[i];
- _export.Append(i);
- _export.Append(",");
+ Export.Append(i);
+ Export.Append(",");
List allMaterials = new List {material};
allMaterials.AddRange(list.GetMaterialVariants(material, null));
@@ -35,17 +35,17 @@ public override void AddFragmentData(WldFragment data)
currentMaterial = allMaterials.First();
}
- _export.Append(GetMaterialString(currentMaterial));
+ Export.Append(GetMaterialString(currentMaterial));
if (j < list.VariantCount)
{
- _export.Append(";");
+ Export.Append(";");
}
}
- _export.Append(",");
- _export.Append(material.BitmapInfoReference?.BitmapInfo.AnimationDelayMs ?? 0);
- _export.AppendLine();
+ Export.Append(",");
+ Export.Append(material.BitmapInfoReference?.BitmapInfo.AnimationDelayMs ?? 0);
+ Export.AppendLine();
}
}
diff --git a/LanternExtractor/EQ/Wld/Exporters/MeshObjMtlWriter.cs b/LanternExtractor/EQ/Wld/Exporters/MeshObjMtlWriter.cs
index c152d64..ee56690 100644
--- a/LanternExtractor/EQ/Wld/Exporters/MeshObjMtlWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/MeshObjMtlWriter.cs
@@ -52,12 +52,12 @@ public override void AddFragmentData(WldFragment data)
{
if(!createdNullMaterial)
{
- _export.AppendLine(LanternStrings.ObjNewMaterialPrefix + " " + "null");
- _export.AppendLine("Ka 1.000 1.000 1.000");
- _export.AppendLine("Kd 1.000 1.000 1.000");
- _export.AppendLine("Ks 0.000 0.000 0.000");
- _export.AppendLine("d 1.0 ");
- _export.AppendLine("illum 2");
+ Export.AppendLine(LanternStrings.ObjNewMaterialPrefix + " " + "null");
+ Export.AppendLine("Ka 1.000 1.000 1.000");
+ Export.AppendLine("Kd 1.000 1.000 1.000");
+ Export.AppendLine("Ks 0.000 0.000 0.000");
+ Export.AppendLine("d 1.0 ");
+ Export.AppendLine("illum 2");
createdNullMaterial = true;
}
@@ -70,13 +70,13 @@ public override void AddFragmentData(WldFragment data)
continue;
}
- _export.AppendLine(LanternStrings.ObjNewMaterialPrefix + " " + MaterialList.GetMaterialPrefix(material.ShaderType) + material.GetFirstBitmapNameWithoutExtension());
- _export.AppendLine("Ka 1.000 1.000 1.000");
- _export.AppendLine("Kd 1.000 1.000 1.000");
- _export.AppendLine("Ks 0.000 0.000 0.000");
- _export.AppendLine("d 1.0 ");
- _export.AppendLine("illum 2");
- _export.AppendLine("map_Kd " + "Textures/" + skinMaterial.GetFirstBitmapExportFilename());
+ Export.AppendLine(LanternStrings.ObjNewMaterialPrefix + " " + MaterialList.GetMaterialPrefix(material.ShaderType) + material.GetFirstBitmapNameWithoutExtension());
+ Export.AppendLine("Ka 1.000 1.000 1.000");
+ Export.AppendLine("Kd 1.000 1.000 1.000");
+ Export.AppendLine("Ks 0.000 0.000 0.000");
+ Export.AppendLine("d 1.0 ");
+ Export.AppendLine("illum 2");
+ Export.AppendLine("map_Kd " + "Textures/" + skinMaterial.GetFirstBitmapExportFilename());
}
}
}
diff --git a/LanternExtractor/EQ/Wld/Exporters/MeshObjWriter.cs b/LanternExtractor/EQ/Wld/Exporters/MeshObjWriter.cs
index 38d651d..669ab95 100644
--- a/LanternExtractor/EQ/Wld/Exporters/MeshObjWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/MeshObjWriter.cs
@@ -95,17 +95,17 @@ public void AddFragmentData(WldFragment fragment, ObjectInstance associatedObjec
var cosc = Math.Cos(roll);
var sinc = Math.Sin(roll);
- var Axx = cosa * cosb;
- var Axy = cosa * sinb * sinc - sina * cosc;
- var Axz = cosa * sinb * cosc + sina * sinc;
+ var axx = cosa * cosb;
+ var axy = cosa * sinb * sinc - sina * cosc;
+ var axz = cosa * sinb * cosc + sina * sinc;
- var Ayx = sina * cosb;
- var Ayy = sina * sinb * sinc + cosa * cosc;
- var Ayz = sina * sinb * cosc - cosa * sinc;
+ var ayx = sina * cosb;
+ var ayy = sina * sinb * sinc + cosa * cosc;
+ var ayz = sina * sinb * cosc - cosa * sinc;
- var Azx = -sinb;
- var Azy = cosb * sinc;
- var Azz = cosb * cosc;
+ var azx = -sinb;
+ var azy = cosb * sinc;
+ var azz = cosb * cosc;
if (mesh == null)
{
@@ -125,13 +125,13 @@ public void AddFragmentData(WldFragment fragment, ObjectInstance associatedObjec
name = LanternStrings.ObjMaterialHeader + _forcedMeshList + ".mtl";
}
- _export.AppendLine(name);
+ Export.AppendLine(name);
_isFirstMesh = false;
}
if (_exportGroups)
{
- _export.AppendLine("g " + FragmentNameCleaner.CleanName(mesh));
+ Export.AppendLine("g " + FragmentNameCleaner.CleanName(mesh));
}
if (mesh.ExportSeparateCollision)
@@ -268,11 +268,12 @@ public void AddFragmentData(WldFragment fragment, ObjectInstance associatedObjec
}
int frameCount = 1;
+ var animatedVertices = mesh.AnimatedVerticesReference?.GetAnimatedVertices();
// We end up with OOM errors trying to concat frames of exported zones with objects, i.e. when we have associatedObject
- if (associatedObject == null && mesh.AnimatedVerticesReference != null)
+ if (associatedObject == null && animatedVertices != null)
{
- frameCount += mesh.AnimatedVerticesReference.MeshAnimatedVertices.Frames.Count;
+ frameCount += animatedVertices.Frames.Count;
}
for (int i = 0; i < frameCount; ++i)
@@ -294,12 +295,12 @@ public void AddFragmentData(WldFragment fragment, ObjectInstance associatedObjec
}
else
{
- if (mesh.AnimatedVerticesReference == null)
+ if (mesh.AnimatedVerticesReference == null || animatedVertices == null)
{
continue;
}
- vertex = mesh.AnimatedVerticesReference.MeshAnimatedVertices.Frames[i - 1][usedVertex];
+ vertex = animatedVertices.Frames[i - 1][usedVertex];
}
// Apply transformation for scale
@@ -316,14 +317,14 @@ public void AddFragmentData(WldFragment fragment, ObjectInstance associatedObjec
var py = vertex.y;
var pz = vertex.z;
- float x = (float)(Axx * px + Axy * py + Axz * pz);
- float y = (float)(Ayx * px + Ayy * py + Ayz * pz);
- float z = (float)(Azx * px + Azy * py + Azz * pz);
+ float x = (float)(axx * px + axy * py + axz * pz);
+ float y = (float)(ayx * px + ayy * py + ayz * pz);
+ float z = (float)(azx * px + azy * py + azz * pz);
vertex = new vec3(x, y, z);
}
- vertexOutput.AppendLine("v " + (-(vertex.x + mesh.Center.x + offset.x)).ToString(_numberFormat) + " " +
- (vertex.z + mesh.Center.z + offset.z).ToString(_numberFormat) + " " +
- (vertex.y + mesh.Center.y + offset.y).ToString(_numberFormat));
+ vertexOutput.AppendLine("v " + (-(vertex.x + mesh.Center.x + offset.x)).ToString(NumberFormat) + " " +
+ (vertex.z + mesh.Center.z + offset.z).ToString(NumberFormat) + " " +
+ (vertex.y + mesh.Center.y + offset.y).ToString(NumberFormat));
if (_objExportType == ObjExportType.Collision)
{
@@ -338,8 +339,8 @@ public void AddFragmentData(WldFragment fragment, ObjectInstance associatedObjec
}
vec2 vertexUvs = mesh.TextureUvCoordinates[usedVertex];
- vertexOutput.AppendLine("vt " + vertexUvs.x.ToString(_numberFormat) + " " +
- vertexUvs.y.ToString(_numberFormat));
+ vertexOutput.AppendLine("vt " + vertexUvs.x.ToString(NumberFormat) + " " +
+ vertexUvs.y.ToString(NumberFormat));
}
frames.Add(vertexOutput.ToString() + faceOutput);
@@ -350,7 +351,7 @@ public void AddFragmentData(WldFragment fragment, ObjectInstance associatedObjec
{
if (i == 0)
{
- _export.Append(frames[i]);
+ Export.Append(frames[i]);
}
else
{
@@ -381,7 +382,7 @@ public void WriteAllFrames(string fileName)
for (int i = 1; i < _frames.Count; ++i)
{
- _export = _frames[i];
+ Export = _frames[i];
WriteAssetToFile(fileName.Replace(".obj", "") + "_frame" + i + ".obj");
}
}
@@ -405,4 +406,4 @@ public override void ClearExportData()
_isFirstMesh = true;
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Exporters/ObjectInstanceWriter.cs b/LanternExtractor/EQ/Wld/Exporters/ObjectInstanceWriter.cs
index 881f461..8fc4942 100644
--- a/LanternExtractor/EQ/Wld/Exporters/ObjectInstanceWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/ObjectInstanceWriter.cs
@@ -7,8 +7,8 @@ public class ObjectInstanceWriter : TextAssetWriter
public ObjectInstanceWriter()
{
- _export.AppendLine(LanternStrings.ExportHeaderTitle + "Object Instances");
- _export.AppendLine(LanternStrings.ExportHeaderFormat +
+ Export.AppendLine(LanternStrings.ExportHeaderTitle + "Object Instances");
+ Export.AppendLine(LanternStrings.ExportHeaderFormat +
"ModelName, PosX, PosY, PosZ, RotX, RotY, RotZ, ScaleX, ScaleY, ScaleZ, ColorIndex");
}
@@ -21,29 +21,29 @@ public override void AddFragmentData(WldFragment data)
return;
}
- _export.Append(instance.ObjectName);
- _export.Append(",");
- _export.Append(instance.Position.x.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(instance.Position.z.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(instance.Position.y.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(instance.Rotation.x.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(instance.Rotation.z.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(instance.Rotation.y.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(instance.Scale.x.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(instance.Scale.y.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(instance.Scale.z.ToString(_numberFormat));
- _export.Append(",");
- _export.Append(instance.Colors == null ? -1 :instance.Colors.Index);
+ Export.Append(instance.ObjectName);
+ Export.Append(",");
+ Export.Append(instance.Position.x.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(instance.Position.z.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(instance.Position.y.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(instance.Rotation.x.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(instance.Rotation.z.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(instance.Rotation.y.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(instance.Scale.x.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(instance.Scale.y.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(instance.Scale.z.ToString(NumberFormat));
+ Export.Append(",");
+ Export.Append(instance.Colors == null ? -1 :instance.Colors.Index);
- _export.AppendLine();
+ Export.AppendLine();
}
}
}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Wld/Exporters/ParticleSystemWriter.cs b/LanternExtractor/EQ/Wld/Exporters/ParticleSystemWriter.cs
index 75f30a9..afb5f68 100644
--- a/LanternExtractor/EQ/Wld/Exporters/ParticleSystemWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/ParticleSystemWriter.cs
@@ -7,7 +7,7 @@ public class ParticleSystemWriter : TextAssetWriter
{
public ParticleSystemWriter()
{
- _export.AppendLine(LanternStrings.ExportHeaderTitle + "Particle System");
+ Export.AppendLine(LanternStrings.ExportHeaderTitle + "Particle System");
}
public override void AddFragmentData(WldFragment data)
@@ -17,7 +17,7 @@ public override void AddFragmentData(WldFragment data)
return;
}
- _export.AppendLine(FragmentNameCleaner.CleanName(data));
+ Export.AppendLine(FragmentNameCleaner.CleanName(data));
}
}
}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Wld/Exporters/SkeletonHierarchyWriter.cs b/LanternExtractor/EQ/Wld/Exporters/SkeletonHierarchyWriter.cs
index ead29f3..29d4527 100644
--- a/LanternExtractor/EQ/Wld/Exporters/SkeletonHierarchyWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/SkeletonHierarchyWriter.cs
@@ -7,56 +7,68 @@ namespace LanternExtractor.EQ.Wld.Exporters
public class SkeletonHierarchyWriter : TextAssetWriter
{
private bool _stripModelBase;
-
+
public SkeletonHierarchyWriter(bool stripModelBase)
{
_stripModelBase = stripModelBase;
}
-
+
public override void AddFragmentData(WldFragment data)
{
- _export.AppendLine(LanternStrings.ExportHeaderTitle + "Skeleton Hierarchy");
- _export.AppendLine(LanternStrings.ExportHeaderFormat + "BoneName, Children, Mesh, AlternateMesh, ParticleCloud");
-
+ Export.AppendLine(LanternStrings.ExportHeaderTitle + "Skeleton Hierarchy");
+ Export.AppendLine(LanternStrings.ExportHeaderFormat + "BoneName, Children, Mesh, AlternateMesh, ParticleCloud");
+
SkeletonHierarchy skeleton = data as SkeletonHierarchy;
if (skeleton == null)
{
return;
}
-
+
if (skeleton.Meshes != null && skeleton.Meshes.Count != 0)
{
- _export.Append("meshes");
+ Export.Append("meshes");
foreach (var mesh in skeleton.Meshes)
{
- _export.Append(",");
- _export.Append(FragmentNameCleaner.CleanName(mesh));
+ Export.Append(",");
+ Export.Append(FragmentNameCleaner.CleanName(mesh));
}
+ Export.AppendLine();
+
+ Export.Append("secondary_meshes");
foreach (var mesh in skeleton.SecondaryMeshes)
{
- _export.Append(",");
- _export.Append(FragmentNameCleaner.CleanName(mesh));
+ Export.Append(",");
+ Export.Append(FragmentNameCleaner.CleanName(mesh));
}
- _export.AppendLine();
+ Export.AppendLine();
}
if (skeleton.AlternateMeshes != null && skeleton.AlternateMeshes.Count != 0)
{
- _export.Append("meshes,");
+ Export.Append("meshes");
foreach (var mesh in skeleton.AlternateMeshes)
{
- _export.Append(FragmentNameCleaner.CleanName(mesh));
+ Export.Append(",");
+ Export.Append(FragmentNameCleaner.CleanName(mesh));
+ }
+ Export.AppendLine();
+
+ Export.Append("secondary_meshes");
+ foreach (var mesh in skeleton.SecondaryAlternateMeshes)
+ {
+ Export.Append(",");
+ Export.Append(FragmentNameCleaner.CleanName(mesh));
}
-
- _export.AppendLine();
+
+ Export.AppendLine();
}
foreach (var node in skeleton.Skeleton)
{
string childrenList = string.Empty;
-
+
foreach (var children in node.Children)
{
childrenList += children;
@@ -73,36 +85,36 @@ public override void AddFragmentData(WldFragment data)
{
boneName = StripModelBase(boneName, skeleton.ModelBase);
}
-
- _export.Append(CleanSkeletonNodeName(boneName));
- _export.Append(",");
- _export.Append(childrenList);
- _export.Append(",");
+ Export.Append(CleanSkeletonNodeName(boneName));
+ Export.Append(",");
+ Export.Append(childrenList);
+
+ Export.Append(",");
if (node.MeshReference?.Mesh != null)
{
- _export.Append(FragmentNameCleaner.CleanName(node.MeshReference.Mesh));
+ Export.Append(FragmentNameCleaner.CleanName(node.MeshReference.Mesh));
}
-
- _export.Append(",");
+
+ Export.Append(",");
if (node.MeshReference?.LegacyMesh != null)
{
- _export.Append(FragmentNameCleaner.CleanName(node.MeshReference.LegacyMesh));
+ Export.Append(FragmentNameCleaner.CleanName(node.MeshReference.LegacyMesh));
}
-
- _export.Append(",");
+
+ Export.Append(",");
if (node.ParticleCloud != null)
{
- _export.Append(FragmentNameCleaner.CleanName(node.ParticleCloud));
+ Export.Append(FragmentNameCleaner.CleanName(node.ParticleCloud));
}
-
- _export.AppendLine();
+
+ Export.AppendLine();
}
}
-
+
private string CleanSkeletonNodeName(string name)
{
return name.Replace("_DAG", "").ToLower();
@@ -123,4 +135,4 @@ private string StripModelBase(string boneName, string modelBase)
return boneName;
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Exporters/TextAssetWriter.cs b/LanternExtractor/EQ/Wld/Exporters/TextAssetWriter.cs
index c313af8..225753e 100644
--- a/LanternExtractor/EQ/Wld/Exporters/TextAssetWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/TextAssetWriter.cs
@@ -7,8 +7,8 @@ namespace LanternExtractor.EQ.Wld.Exporters
{
public abstract class TextAssetWriter
{
- protected StringBuilder _export = new StringBuilder();
- protected NumberFormatInfo _numberFormat = new NumberFormatInfo {NumberDecimalSeparator = "."};
+ protected StringBuilder Export = new StringBuilder();
+ protected NumberFormatInfo NumberFormat = new NumberFormatInfo {NumberDecimalSeparator = "."};
public abstract void AddFragmentData(WldFragment data);
@@ -21,24 +21,24 @@ public virtual void WriteAssetToFile(string fileName)
return;
}
- if (_export.Length == 0)
+ if (Export.Length == 0)
{
return;
}
Directory.CreateDirectory(directory);
- File.WriteAllText(fileName, _export.ToString());
+ File.WriteAllText(fileName, Export.ToString());
}
public virtual void ClearExportData()
{
- _export.Clear();
+ Export.Clear();
}
public int GetExportByteCount()
{
- return _export.ToString().Length;
+ return Export.ToString().Length;
}
}
}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Wld/Exporters/VertexColorsWriter.cs b/LanternExtractor/EQ/Wld/Exporters/VertexColorsWriter.cs
index 4b04041..a1646f4 100644
--- a/LanternExtractor/EQ/Wld/Exporters/VertexColorsWriter.cs
+++ b/LanternExtractor/EQ/Wld/Exporters/VertexColorsWriter.cs
@@ -12,8 +12,8 @@ public VertexColorsWriter()
private void AddHeader()
{
- _export.AppendLine(LanternStrings.ExportHeaderTitle + "Vertex Colors");
- _export.AppendLine(LanternStrings.ExportHeaderFormat +
+ Export.AppendLine(LanternStrings.ExportHeaderTitle + "Vertex Colors");
+ Export.AppendLine(LanternStrings.ExportHeaderFormat +
"Red, Green, Blue, Sunlight");
}
@@ -28,14 +28,14 @@ public override void AddFragmentData(WldFragment data)
foreach (Color color in instance.Colors)
{
- _export.Append(color.R);
- _export.Append(",");
- _export.Append(color.G);
- _export.Append(",");
- _export.Append(color.B);
- _export.Append(",");
- _export.Append(color.A);
- _export.AppendLine();
+ Export.Append(color.R);
+ Export.Append(",");
+ Export.Append(color.G);
+ Export.Append(",");
+ Export.Append(color.B);
+ Export.Append(",");
+ Export.Append(color.A);
+ Export.AppendLine();
}
}
diff --git a/LanternExtractor/EQ/Wld/Fragments/Actor.cs b/LanternExtractor/EQ/Wld/Fragments/Actor.cs
index 34a8026..213c45d 100644
--- a/LanternExtractor/EQ/Wld/Fragments/Actor.cs
+++ b/LanternExtractor/EQ/Wld/Fragments/Actor.cs
@@ -17,17 +17,17 @@ class Actor : WldFragment
/// Mesh reference (optional)
///
public MeshReference MeshReference { get; private set; }
-
+
///
/// Skeleton track reference (optional)
///
public SkeletonHierarchyReference SkeletonReference { get; private set; }
-
+
///
/// Camera reference (optional)
///
public CameraReference CameraReference { get; private set; }
-
+
///
/// Camera reference (optional)
///
@@ -36,9 +36,9 @@ class Actor : WldFragment
public Fragment07 Fragment07;
public ActorType ActorType;
-
+
public string ReferenceName;
-
+
public override void Initialize(int index, int size, byte[] data,
List fragments,
Dictionary stringHash, bool isNewWldFormat, ILogger logger)
@@ -48,17 +48,17 @@ public override void Initialize(int index, int size, byte[] data,
int flags = Reader.ReadInt32();
BitAnalyzer ba = new BitAnalyzer(flags);
-
+
bool params1Exist = ba.IsBitSet(0);
bool params2Exist = ba.IsBitSet(1);
bool fragment2MustContainZero = ba.IsBitSet(7);
-
+
// Is an index in the string hash
int fragment1 = Reader.ReadInt32();
// For objects, SPRITECALLBACK - and it's the same reference value
string stringValue = stringHash[-fragment1];
-
+
// 1 for both static and animated objects
int size1 = Reader.ReadInt32();
@@ -78,7 +78,7 @@ public override void Initialize(int index, int size, byte[] data,
{
Reader.BaseStream.Position += 7 * sizeof(int);
}
-
+
// Size 1 entries
for (int i = 0; i < size1; ++i)
{
@@ -86,7 +86,7 @@ public override void Initialize(int index, int size, byte[] data,
int dataPairCount = Reader.ReadInt32();
// Unknown purpose
- // Always 0 and 1.00000002E+30
+ // Always 0 and 1.00000002E+30
for (int j = 0; j < dataPairCount; ++j)
{
int value = Reader.ReadInt32();
@@ -99,13 +99,14 @@ public override void Initialize(int index, int size, byte[] data,
{
logger.LogWarning("Actor: More than one component references");
}
-
+
// Can contain either a skeleton reference (animated), mesh reference (static) or a camera reference
for (int i = 0; i < componentCount; ++i)
{
int fragmentIndex = Reader.ReadInt32() - 1;
-
- SkeletonReference = fragments[fragmentIndex] as SkeletonHierarchyReference;
+ var fragment = fragments[fragmentIndex];
+
+ SkeletonReference = fragment as SkeletonHierarchyReference;
if (SkeletonReference != null)
{
@@ -113,37 +114,41 @@ public override void Initialize(int index, int size, byte[] data,
break;
}
- MeshReference = fragments[fragmentIndex] as MeshReference;
+ MeshReference = fragment as MeshReference;
- // Why would the mesh reference be null?
if (MeshReference != null && MeshReference.Mesh != null)
{
MeshReference.Mesh.IsHandled = true;
break;
}
-
+
+ if (MeshReference != null && MeshReference.LegacyMesh != null)
+ {
+ break;
+ }
+
// This only exists in the main zone WLD
- CameraReference = fragments[fragmentIndex - 1] as CameraReference;
+ CameraReference = fragment as CameraReference;
if (CameraReference != null)
{
break;
}
-
- ParticleSpriteReference = fragments[fragmentIndex - 1] as ParticleSpriteReference;
+
+ ParticleSpriteReference = fragment as ParticleSpriteReference;
if (ParticleSpriteReference != null)
{
break;
}
-
- Fragment07 = fragments[fragmentIndex - 1] as Fragment07;
+
+ Fragment07 = fragment as Fragment07;
if (Fragment07 != null)
{
break;
}
-
+
logger.LogError($"Actor: Cannot link fragment with index {fragmentIndex}");
}
@@ -188,7 +193,7 @@ private void CalculateActorType(ILogger logger)
logger.LogError("Cannot determine actor type!");
}
}
-
+
public override void OutputInfo(ILogger logger)
{
base.OutputInfo(logger);
@@ -201,9 +206,9 @@ public void AssignSkeletonReference(SkeletonHierarchy skeleton, ILogger logger)
{
SkeletonHierarchy = skeleton
};
-
+
CalculateActorType(logger);
skeleton.IsAssigned = true;
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Fragments/BspRegion.cs b/LanternExtractor/EQ/Wld/Fragments/BspRegion.cs
index f46cfa4..4fe8b42 100644
--- a/LanternExtractor/EQ/Wld/Fragments/BspRegion.cs
+++ b/LanternExtractor/EQ/Wld/Fragments/BspRegion.cs
@@ -1,4 +1,7 @@
using System.Collections.Generic;
+using GlmSharp;
+using LanternExtractor.EQ.Wld.DataTypes;
+using LanternExtractor.Infrastructure;
using LanternExtractor.Infrastructure.Logger;
namespace LanternExtractor.EQ.Wld.Fragments
@@ -21,8 +24,12 @@ public class BspRegion : WldFragment
///
public Mesh Mesh { get; private set; }
+ public LegacyMesh LegacyMesh { get; private set; }
+
public BspRegionType RegionType { get; private set; }
+ public List RegionVertices = new List();
+
public override void Initialize(int index, int size, byte[] data,
List fragments,
Dictionary stringHash, bool isNewWldFormat, ILogger logger)
@@ -31,68 +38,214 @@ public override void Initialize(int index, int size, byte[] data,
Name = stringHash[-Reader.ReadInt32()];
// Flags
- // 0x181 - Regions with polygons
- // 0x81 - Regions without
- // Bit 5 - PVS is WORDS
- // Bit 7 - PVS is bytes
int flags = Reader.ReadInt32();
- if (flags == 0x181)
- {
- ContainsPolygons = true;
- }
+ BitAnalyzer ba = new BitAnalyzer(flags);
+ var hasSphere = ba.IsBitSet(0);
+ var hasReverbVolume = ba.IsBitSet(1);
+ var hasReverbOffset = ba.IsBitSet(2);
+ var regionFog = ba.IsBitSet(3);
+ var enableGoraud2 = ba.IsBitSet(4);
+ var encodedVisibility = ba.IsBitSet(5);
+ var hasLegacyMeshReference = ba.IsBitSet(6);
+ var hasByteEntries = ba.IsBitSet(7);
+ var hasMeshReference = ba.IsBitSet(8);
+
+ ContainsPolygons = hasMeshReference || hasLegacyMeshReference;
// Always 0
- int unknown1 = Reader.ReadInt32();
- int data1Size = Reader.ReadInt32();
- int data2Size = Reader.ReadInt32();
+ int ambientLight = Reader.ReadInt32();
+ int numRegionVertex = Reader.ReadInt32();
+ int numProximalRegions = Reader.ReadInt32();
// Always 0
- int unknown2 = Reader.ReadInt32();
- int data3Size = Reader.ReadInt32();
- int data4Size = Reader.ReadInt32();
+ int numRenderVertices = Reader.ReadInt32();
+ int numWalls = Reader.ReadInt32();
+ int numObstacles = Reader.ReadInt32();
// Always 0
- int unknown3 = Reader.ReadInt32();
- int data5Size = Reader.ReadInt32();
- int data6Size = Reader.ReadInt32();
+ int numCuttingObstacles = Reader.ReadInt32();
+ int numVisNode = Reader.ReadInt32();
+ int numVisList = Reader.ReadInt32();
+
+ for (int i = 0; i < numRegionVertex; i++)
+ {
+ RegionVertices.Add(new vec3(Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle()));
+ }
+
+ var proximalRegions = new List<(int, float)>();
+ for (int i = 0; i < numProximalRegions; i++)
+ {
+ proximalRegions.Add((Reader.ReadInt32(), Reader.ReadSingle()));
+ }
+
+ var renderVertices = new List();
+ for (int i = 0; i < numRenderVertices; i++)
+ {
+ renderVertices.Add(new vec3(Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle()));
+ }
+
+ var walls = new List();
+ for (int i = 0; i < numWalls; i++)
+ {
+ var wall = new RegionWall();
+
+ wall.Flags = Reader.ReadInt32();
+ var wallBa = new BitAnalyzer(wall.Flags);
+ var isFloor = wallBa.IsBitSet(0);
+ var isRenderable = wallBa.IsBitSet(1);
+
+ wall.NumVertices = Reader.ReadInt32();
+ wall.VertexList = new List();
+ for (int v = 0; v < wall.NumVertices; v++)
+ {
+ wall.VertexList.Add(Reader.ReadInt32());
+ }
+
+ if (isRenderable)
+ {
+ wall.RenderMethod = new RenderMethod
+ {
+ Flags = Reader.ReadInt32()
+ };
+
+ wall.RenderInfo = RenderInfo.Parse(Reader, fragments);
+ wall.NormalAbcd = new vec4(
+ Reader.ReadSingle(),
+ Reader.ReadSingle(),
+ Reader.ReadSingle(),
+ Reader.ReadSingle()
+ );
+ }
- // Move past data1 and 2
- Reader.BaseStream.Position += 12 * data1Size + 12 * data2Size;
+ walls.Add(wall);
+ }
+
+ var obstacles = new List();
+ for (int i = 0; i < numObstacles; i++)
+ {
+ var obstacle = new RegionObstacle();
+ obstacle.Flags = Reader.ReadInt32();
+
+ var obstacleBa = new BitAnalyzer(obstacle.Flags);
+ var isFloor = obstacleBa.IsBitSet(0);
+ var isGeometryCutting = obstacleBa.IsBitSet(1);
+ var hasUserData = obstacleBa.IsBitSet(2);
+
+ obstacle.NextRegion = Reader.ReadInt32();
+ obstacle.ObstacleType = (RegionObstacleType) Reader.ReadInt32();
+
+ if (obstacle.ObstacleType == RegionObstacleType.EdgePolygon ||
+ obstacle.ObstacleType == RegionObstacleType.EdgePolygonNormalAbcd)
+ {
+ obstacle.NumVertices = Reader.ReadInt32();
+ }
+
+ obstacle.VertextList = new List();
+ for (int v = 0; v < obstacle.NumVertices; v++)
+ {
+ obstacle.VertextList.Add(Reader.ReadInt32());
+ }
+
+ if (obstacle.ObstacleType == RegionObstacleType.EdgePolygonNormalAbcd)
+ {
+ obstacle.NormalAbcd = new vec4(
+ Reader.ReadSingle(),
+ Reader.ReadSingle(),
+ Reader.ReadSingle(),
+ Reader.ReadSingle()
+ );
+ }
+
+ if (obstacle.ObstacleType == RegionObstacleType.EdgeWall)
+ {
+ obstacle.EdgeWall = Reader.ReadInt32();
+ }
+
+ if (hasUserData)
+ {
+ obstacle.UserDataSize = Reader.ReadInt32();
+ obstacle.UserData = Reader.ReadBytes(obstacle.UserDataSize);
+ }
- // Move past data3
- for (int i = 0; i < data3Size; ++i)
+ obstacles.Add(obstacle);
+ }
+
+ var visNodes = new List();
+ for (int i = 0; i < numVisNode; i++)
+ {
+ var visNode = new RegionVisNode
+ {
+ NormalAbcd = new vec4(
+ Reader.ReadSingle(),
+ Reader.ReadSingle(),
+ Reader.ReadSingle(),
+ Reader.ReadSingle()
+ ),
+ VisListIndex = Reader.ReadInt32(),
+ FrontTree = Reader.ReadInt32(),
+ BackTree = Reader.ReadInt32()
+ };
+ visNodes.Add(visNode);
+ }
+
+ var visLists = new List();
+ for (int i = 0; i < numVisList; i++)
{
- int data3Flags = Reader.ReadInt32();
- int data3Size2 = Reader.ReadInt32();
- Reader.BaseStream.Position += data3Size2 * 4;
+ var visList = new RegionVisList
+ {
+ RangeCount = Reader.ReadInt16()
+ };
+
+ visList.Ranges = new List();
+ for (int r = 0; r < visList.RangeCount; r++)
+ {
+ int range = hasByteEntries ? Reader.ReadByte() : Reader.ReadInt16();
+ visList.Ranges.Add(range);
+ }
+
+ visLists.Add(visList);
}
- // Move past the data 4
- for (int i = 0; i < data4Size; ++i)
+ vec4 sphere;
+ if (hasSphere)
{
- // Unhandled for now
+ sphere = new vec4(
+ Reader.ReadSingle(),
+ Reader.ReadSingle(),
+ Reader.ReadSingle(),
+ Reader.ReadSingle()
+ );
}
- // Move past the data5
- for (int i = 0; i < data5Size; i++)
+ float reverbVolume;
+ if (hasReverbVolume)
{
- Reader.BaseStream.Position += 7 * 4;
+ reverbVolume = Reader.ReadSingle();
}
- // Get the size of the PVS and allocate memory
- short pvsSize = Reader.ReadInt16();
- Reader.BaseStream.Position += pvsSize;
+ int reverbOffset;
+ if (hasReverbOffset)
+ {
+ reverbOffset = Reader.ReadInt32();
+ }
- // Move past the unknowns
- uint bytes = Reader.ReadUInt32();
- Reader.BaseStream.Position += 16;
+ var userDataSize = Reader.ReadInt32();
+ var userData = Reader.ReadBytes(userDataSize);
// Get the mesh reference index and link to it
if (ContainsPolygons)
{
int meshReference = Reader.ReadInt32() - 1;
- Mesh = fragments[meshReference] as Mesh;
+
+ if (hasMeshReference)
+ {
+ Mesh = fragments[meshReference] as Mesh;
+ }
+ else if (hasLegacyMeshReference)
+ {
+ LegacyMesh = fragments[meshReference] as LegacyMesh;
+ }
}
}
@@ -109,8 +262,9 @@ public override void OutputInfo(ILogger logger)
if (ContainsPolygons)
{
- logger.LogInfo("BspRegion: Mesh index: " + Mesh.Index);
+ int meshIndex = Mesh?.Index ?? LegacyMesh?.Index ?? 0;
+ logger.LogInfo("BspRegion: Mesh index: " + meshIndex);
}
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Fragments/BspRegionType.cs b/LanternExtractor/EQ/Wld/Fragments/BspRegionType.cs
index efeed23..4cb3ab8 100644
--- a/LanternExtractor/EQ/Wld/Fragments/BspRegionType.cs
+++ b/LanternExtractor/EQ/Wld/Fragments/BspRegionType.cs
@@ -94,7 +94,7 @@ public override void Initialize(int index, int size, byte[] data,
else if (regionTypeString.StartsWith("sln_"))
{
// gukbottom, cazicthule (gumdrop), runnyeye, velketor
- RegionTypes.Add(RegionType.WaterBlockLOS);
+ RegionTypes.Add(RegionType.WaterBlockLos);
}
else if (regionTypeString.StartsWith("vwn_"))
{
diff --git a/LanternExtractor/EQ/Wld/Fragments/Fragment07.cs b/LanternExtractor/EQ/Wld/Fragments/Fragment07.cs
index fcb3cde..7a98e7b 100644
--- a/LanternExtractor/EQ/Wld/Fragments/Fragment07.cs
+++ b/LanternExtractor/EQ/Wld/Fragments/Fragment07.cs
@@ -11,15 +11,15 @@ namespace LanternExtractor.EQ.Wld.Fragments
///
public class Fragment07 : WldFragment
{
- private Fragment06 Fragment06;
+ private Fragment06 _fragment06;
public override void Initialize(int index, int size, byte[] data, List fragments, Dictionary stringHash,
bool isNewWldFormat, ILogger logger)
{
base.Initialize(index, size, data, fragments, stringHash, isNewWldFormat, logger);
Name = stringHash[-Reader.ReadInt32()];
- Fragment06 = fragments[Reader.ReadInt32() - 1] as Fragment06;
- int value_08 = Reader.ReadInt32(); // always 0
+ _fragment06 = fragments[Reader.ReadInt32() - 1] as Fragment06;
+ int value08 = Reader.ReadInt32(); // always 0
}
}
}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Wld/Fragments/Fragment17.cs b/LanternExtractor/EQ/Wld/Fragments/Fragment17.cs
deleted file mode 100644
index 1b9d441..0000000
--- a/LanternExtractor/EQ/Wld/Fragments/Fragment17.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System.Collections.Generic;
-using LanternExtractor.Infrastructure.Logger;
-
-namespace LanternExtractor.EQ.Wld.Fragments
-{
- ///
- /// Fragment17 (0x17) - PolygonAnimation?
- /// Internal Name: _POLYHDEF
- /// Need to figure this fragment out.
- ///
- public class Fragment17 : WldFragment
- {
- public override void Initialize(int index, int size, byte[] data,
- List fragments,
- Dictionary stringHash, bool isNewWldFormat, ILogger logger)
- {
- base.Initialize(index, size, data, fragments, stringHash, isNewWldFormat, logger);
- Name = stringHash[-Reader.ReadInt32()];
- int flags = Reader.ReadInt32();
- int size1 = Reader.ReadInt32();
- int size2 = Reader.ReadInt32();
- float unknown = Reader.ReadSingle();
-
- for (int i = 0; i < size1; ++i)
- {
- float x = Reader.ReadSingle();
- float y = Reader.ReadSingle();
- float z = Reader.ReadSingle();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/LanternExtractor/EQ/Wld/Fragments/IAnimatedVertices.cs b/LanternExtractor/EQ/Wld/Fragments/IAnimatedVertices.cs
new file mode 100644
index 0000000..da97345
--- /dev/null
+++ b/LanternExtractor/EQ/Wld/Fragments/IAnimatedVertices.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+using GlmSharp;
+
+namespace LanternExtractor.EQ.Wld.Fragments
+{
+ public interface IAnimatedVertices
+ {
+ List> Frames { get; set; }
+ int Delay { get; set; }
+ }
+}
diff --git a/LanternExtractor/EQ/Wld/Fragments/LegacyMesh.cs b/LanternExtractor/EQ/Wld/Fragments/LegacyMesh.cs
index e3003da..afd736b 100644
--- a/LanternExtractor/EQ/Wld/Fragments/LegacyMesh.cs
+++ b/LanternExtractor/EQ/Wld/Fragments/LegacyMesh.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using GlmSharp;
using LanternExtractor.EQ.Wld.DataTypes;
using LanternExtractor.Infrastructure;
@@ -14,61 +15,126 @@ namespace LanternExtractor.EQ.Wld.Fragments
///
public class LegacyMesh : WldFragment
{
+ public vec3 Center { get; private set; }
public List Vertices = new List();
public List TexCoords = new List();
public List Normals = new List();
public List Polygons = new List();
public List VertexTex = new List();
+ public List Colors = new List();
public List RenderGroups = new List();
public MaterialList MaterialList;
+ public PolyhedronReference PolyhedronReference;
public Dictionary MobPieces { get; private set; }
+ ///
+ /// The animated vertex fragment (0x2E or 0x37) reference
+ ///
+ public MeshAnimatedVerticesReference AnimatedVerticesReference { get; private set; }
+
+ ///
+ /// Set to true if there are non solid polygons in the mesh
+ /// This means we export collision separately (e.g. trees, fire)
+ ///
+ public bool ExportSeparateCollision { get; private set; }
public override void Initialize(int index, int size, byte[] data, List fragments, Dictionary stringHash,
bool isNewWldFormat, ILogger logger)
{
base.Initialize(index, size, data, fragments, stringHash, isNewWldFormat, logger);
Name = stringHash[-Reader.ReadInt32()];
+
+ // TODO: investigate flags further
+ // looks like some flags will zero and 1.0 fields if they are missing.
+ // 0x1 (bit0) center offset
+ // 0x2 (bit1) bounding radius?
+ // 0x200 (bit9)
+ // 0x400 (bit10) colors?
+ // 0x800 (bit11) RenderGroups
+ // 0x1000 (bit12) VertexTex
+ // 0x2000 (bit13)
+ // 0x4000 (bit14) shown in ghidra as 0x40 bounding box?
+ // 0x8000 (bit15) shown in ghidra as 0x80
int flags = Reader.ReadInt32();
+ BitAnalyzer ba = new BitAnalyzer(flags);
+
int vertexCount = Reader.ReadInt32();
int texCoordCount = Reader.ReadInt32();
int normalsCount = Reader.ReadInt32();
int colorsCount = Reader.ReadInt32(); // size4
int polygonCount = Reader.ReadInt32();
int size6 = Reader.ReadInt16();
- int fragment1maybe = Reader.ReadInt16();
+ int fragment1Maybe = Reader.ReadInt16();
int vertexPieceCount = Reader.ReadInt32(); // -1
MaterialList = fragments[Reader.ReadInt32() - 1] as MaterialList;
- int fragment3 = Reader.ReadInt32();
- float centerX = Reader.ReadSingle();
- float centerY = Reader.ReadSingle();
- float centerZ = Reader.ReadSingle();
- int params2 = Reader.ReadInt32();
- int something2 = Reader.ReadInt32();
- float something3 = Reader.ReadInt32();
-
+ int meshAnimation = Reader.ReadInt32();
+
+ // Vertex animation only
+ if (meshAnimation != 0)
+ {
+ AnimatedVerticesReference = fragments[meshAnimation - 1] as MeshAnimatedVerticesReference;
+ }
+
+ float something1 = Reader.ReadSingle();
+
+ // This might also be able to take a sphere (0x16) or sphere list (0x1a) collision volume
+ var polyhedronReference = Reader.ReadInt32();
+ if (polyhedronReference > 0)
+ {
+ PolyhedronReference = fragments[polyhedronReference - 1] as PolyhedronReference;
+ var sphereFragment = fragments[polyhedronReference - 1] as Fragment16;
+ if (sphereFragment != null)
+ {
+ System.Console.WriteLine(sphereFragment.Name);
+ }
+ ExportSeparateCollision = true;
+ }
+
+ Center = new vec3(Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle());
+ if (!ba.IsBitSet(0))
+ {
+ Center = vec3.Zero;
+ }
+
+ float boundingRadiusMaybe = Reader.ReadSingle();
+ if (!ba.IsBitSet(1))
+ {
+ boundingRadiusMaybe = 1.0f;
+ }
+
for (int i = 0; i < vertexCount; ++i)
{
Vertices.Add(new vec3(Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle()));
}
-
+
for (int i = 0; i < texCoordCount; ++i)
{
TexCoords.Add(new vec2(Reader.ReadSingle(), Reader.ReadSingle()));
}
-
+
for (int i = 0; i < normalsCount; ++i)
{
Normals.Add(new vec3(Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle()));
}
- Reader.BaseStream.Position += colorsCount * sizeof(int);
-
+ // I don't think this is colors
+ // count seems to match the vertexCount when present
+ // found in gequip.t3d
+ for (int i = 0; i < colorsCount; ++i)
+ {
+ // byte 0 seems to always be 1
+ // byte 1 seems to always be 0,1,2
+ // byte 2 seems to be between 0 and polygonCount - 1
+ // byte 3 seems to always be 0
+ var unkBytes = Reader.ReadBytes(4);
+ }
+
+ // faces
for (int i = 0; i < polygonCount; ++i)
{
int flag = Reader.ReadInt16();
-
+
int unk1 = Reader.ReadInt16();
- int unk2 = Reader.ReadInt16();
+ int materialIndex = Reader.ReadInt16();
int unk3 = Reader.ReadInt16();
int unk4 = Reader.ReadInt16();
@@ -77,18 +143,19 @@ public override void Initialize(int index, int size, byte[] data, List();
int mobStart = 0;
for (int i = 0; i < vertexPieceCount; ++i)
@@ -116,8 +183,6 @@ public override void Initialize(int index, int size, byte[] data, List
+ /// LegactMeshAnimatedVertices (0x2E)
+ /// Internal name: _DMTRACKDEF
+ /// Contains a list of frames each containing a position for each vertex.
+ /// The frame vertices are cycled through, animating the model.
+ ///
+ public class LegacyMeshAnimatedVertices : WldFragment, IAnimatedVertices
+ {
+ ///
+ /// The model frames
+ ///
+ public List> Frames { get; set; }
+
+ ///
+ /// The delay between the vertex swaps
+ ///
+ public int Delay { get; set; }
+
+ public override void Initialize(int index, int size, byte[] data,
+ List fragments,
+ Dictionary stringHash, bool isNewWldFormat, ILogger logger)
+ {
+ base.Initialize(index, size, data, fragments, stringHash, isNewWldFormat, logger);
+
+ Name = stringHash[-Reader.ReadInt32()];
+ int flags = Reader.ReadInt32();
+ int vertexCount = Reader.ReadInt32();
+ int frameCount = Reader.ReadInt32();
+ Delay = Reader.ReadInt32();
+ int param1 = Reader.ReadInt32();
+
+ Frames = new List>();
+ for (var i = 0; i < frameCount; i++)
+ {
+ var positions = new List();
+
+ for (var v = 0; v < vertexCount; v++)
+ {
+ positions.Add(
+ new vec3(Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle())
+ );
+ }
+
+ Frames.Add(positions);
+ }
+ }
+
+ public override void OutputInfo(ILogger logger)
+ {
+ base.OutputInfo(logger);
+ logger.LogInfo("-----");
+ logger.LogInfo("LegacyMeshAnimatedVertices: Frame count: " + Frames.Count);
+ }
+ }
+}
diff --git a/LanternExtractor/EQ/Wld/Fragments/Mesh.cs b/LanternExtractor/EQ/Wld/Fragments/Mesh.cs
index f8d7494..669ba5b 100644
--- a/LanternExtractor/EQ/Wld/Fragments/Mesh.cs
+++ b/LanternExtractor/EQ/Wld/Fragments/Mesh.cs
@@ -45,7 +45,7 @@ public class Mesh : WldFragment
/// The vertices of the mesh
///
public List Vertices { get; set; }
-
+
///
/// The normals of the mesh
///
@@ -55,7 +55,7 @@ public class Mesh : WldFragment
/// The polygon indices of the mesh
///
public List Indices { get; private set; }
-
+
public List Colors { get; set; }
///
@@ -123,9 +123,9 @@ public override void Initialize(int index, int size, byte[] data,
// Seems to be related to lighting models? (torches, etc.)
if (unknownDword1 != 0 || unknownDword2 != 0 || unknownDword3 != 0)
{
-
+
}
-
+
MaxDistance = Reader.ReadSingle();
MinPosition = new vec3(Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle());
MaxPosition = new vec3(Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle());
@@ -145,7 +145,7 @@ public override void Initialize(int index, int size, byte[] data,
short vertexTextureCount = Reader.ReadInt16();
short size9 = Reader.ReadInt16();
float scale = 1.0f / (1 << Reader.ReadInt16());
-
+
for (int i = 0; i < vertexCount; ++i)
{
Vertices.Add(new vec3(Reader.ReadInt16() * scale, Reader.ReadInt16() * scale,
@@ -156,7 +156,7 @@ public override void Initialize(int index, int size, byte[] data,
{
if (isNewWldFormat)
{
- TextureUvCoordinates.Add(new vec2(Reader.ReadInt32() / 256.0f, Reader.ReadInt32() / 256.0f));
+ TextureUvCoordinates.Add(new vec2(Reader.ReadSingle(), Reader.ReadSingle()));
}
else
{
@@ -179,7 +179,7 @@ public override void Initialize(int index, int size, byte[] data,
int g = colorBytes[1];
int r = colorBytes[2];
int a = colorBytes[3];
-
+
Colors.Add(new Color( r, g, b, a));
}
@@ -202,7 +202,7 @@ public override void Initialize(int index, int size, byte[] data,
Vertex3 = Reader.ReadInt16(),
});
}
-
+
MobPieces = new Dictionary();
int mobStart = 0;
@@ -224,7 +224,7 @@ public override void Initialize(int index, int size, byte[] data,
MaterialGroups = new List();
StartTextureIndex = Int32.MaxValue;
-
+
for (int i = 0; i < polygonTextureCount; ++i)
{
var group = new RenderGroup();
@@ -247,7 +247,7 @@ public override void Initialize(int index, int size, byte[] data,
{
Reader.BaseStream.Position += 12;
}
-
+
// In some rare cases, the number of uvs does not match the number of vertices
if (Vertices.Count != TextureUvCoordinates.Count)
{
@@ -291,4 +291,4 @@ public void ClearCollision()
ExportSeparateCollision = true;
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Fragments/MeshAnimatedVertices.cs b/LanternExtractor/EQ/Wld/Fragments/MeshAnimatedVertices.cs
index 7222e47..cab95a6 100644
--- a/LanternExtractor/EQ/Wld/Fragments/MeshAnimatedVertices.cs
+++ b/LanternExtractor/EQ/Wld/Fragments/MeshAnimatedVertices.cs
@@ -10,17 +10,17 @@ namespace LanternExtractor.EQ.Wld.Fragments
/// Contains a list of frames each containing a position for each vertex.
/// The frame vertices are cycled through, animating the model.
///
- public class MeshAnimatedVertices : WldFragment
+ public class MeshAnimatedVertices : WldFragment, IAnimatedVertices
{
///
/// The model frames
///
- public List> Frames { get; private set; }
+ public List> Frames { get; set; }
///
/// The delay between the vertex swaps
///
- public int Delay { get; private set; }
+ public int Delay { get; set; }
public override void Initialize(int index, int size, byte[] data,
List fragments,
@@ -62,4 +62,4 @@ public override void OutputInfo(ILogger logger)
logger.LogInfo("MeshAnimatedVertices: Frame count: " + Frames.Count);
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Fragments/MeshAnimatedVerticesReference.cs b/LanternExtractor/EQ/Wld/Fragments/MeshAnimatedVerticesReference.cs
index bb41dad..c682d53 100644
--- a/LanternExtractor/EQ/Wld/Fragments/MeshAnimatedVerticesReference.cs
+++ b/LanternExtractor/EQ/Wld/Fragments/MeshAnimatedVerticesReference.cs
@@ -6,27 +6,36 @@ namespace LanternExtractor.EQ.Wld.Fragments
///
/// MeshAnimatedVerticesReference (0x2F)
/// Internal name: None
- /// References a MeshAnimatedVertices fragment.
+ /// References a LegacyMeshAnimatedVertices or MeshAnimatedVertices fragment.
/// This fragment is referenced from the Mesh fragment, if it's animated.
///
public class MeshAnimatedVerticesReference : WldFragment
{
+ public LegacyMeshAnimatedVertices LegacyMeshAnimatedVertices { get; set; }
public MeshAnimatedVertices MeshAnimatedVertices { get; set; }
-
+
public override void Initialize(int index, int size, byte[] data,
List fragments,
Dictionary stringHash, bool isNewWldFormat, ILogger logger)
{
base.Initialize(index, size, data, fragments, stringHash, isNewWldFormat, logger);
Name = stringHash[-Reader.ReadInt32()];
- MeshAnimatedVertices = fragments[Reader.ReadInt32() -1] as MeshAnimatedVertices;
+
+ var fragmentId = Reader.ReadInt32() - 1;
+ MeshAnimatedVertices = fragments[fragmentId] as MeshAnimatedVertices;
+ LegacyMeshAnimatedVertices = fragments[fragmentId] as LegacyMeshAnimatedVertices;
int flags = Reader.ReadInt32();
}
-
+
+ public IAnimatedVertices GetAnimatedVertices()
+ {
+ return MeshAnimatedVertices as IAnimatedVertices ?? LegacyMeshAnimatedVertices;
+ }
+
public override void OutputInfo(ILogger logger)
{
base.OutputInfo(logger);
logger.LogInfo("-----");
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Fragments/ObjectInstance.cs b/LanternExtractor/EQ/Wld/Fragments/ObjectInstance.cs
index b0c6e35..60b0ff0 100644
--- a/LanternExtractor/EQ/Wld/Fragments/ObjectInstance.cs
+++ b/LanternExtractor/EQ/Wld/Fragments/ObjectInstance.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using GlmSharp;
using LanternExtractor.Infrastructure.Logger;
@@ -95,4 +94,4 @@ public override void OutputInfo(ILogger logger)
logger.LogInfo($"{GetType()}: Scale: " + Scale);
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Fragments/ParticleCloud.cs b/LanternExtractor/EQ/Wld/Fragments/ParticleCloud.cs
index fc98d53..67a602c 100644
--- a/LanternExtractor/EQ/Wld/Fragments/ParticleCloud.cs
+++ b/LanternExtractor/EQ/Wld/Fragments/ParticleCloud.cs
@@ -22,28 +22,28 @@ public override void Initialize(int index, int size, byte[] data, List
+ /// Polyhedron (0x17)
+ /// Internal Name: _POLYHDEF
+ ///
+ public class Polyhedron : WldFragment
+ {
+ // POLYHEDRONDEFINITION
+ // BOUNDINGRADIUS %f
+ // SCALEFACTOR %f
+ // NUMVERTICES %d
+ // XYZ %f %f %f
+ // NUMFACES %d
+ // FACE
+ // NORMALABCD %f %f %f %f
+ // NUMVERTICES %d
+ // VERTEXLIST %d ...%d
+ // ENDFACE
+ // ENDPOLYHEDRONDEFINITION
+
+ public float BoundingRadius { get; set; }
+ public float ScaleFactor { get; set; }
+ public List Vertices { get; set; }
+ public List Faces { get; set; }
+
+ public override void Initialize(int index, int size, byte[] data,
+ List fragments,
+ Dictionary stringHash, bool isNewWldFormat, ILogger logger)
+ {
+ base.Initialize(index, size, data, fragments, stringHash, isNewWldFormat, logger);
+ Name = stringHash[-Reader.ReadInt32()];
+ int flags = Reader.ReadInt32();
+
+ var ba = new BitAnalyzer(flags);
+ var hasScaleFactor = ba.IsBitSet(0);
+ var hasNormalAbcd = ba.IsBitSet(1);
+
+ int vertexCount = Reader.ReadInt32();
+ int faceCount = Reader.ReadInt32();
+ float boundingRadius = Reader.ReadSingle();
+
+ if (hasScaleFactor)
+ {
+ ScaleFactor = Reader.ReadSingle();
+ }
+ else
+ {
+ ScaleFactor = 1.0f;
+ }
+
+ Vertices = new List();
+ for (var i = 0; i < vertexCount; i++)
+ {
+ var vertex = new vec3(Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle());
+ Vertices.Add(vertex);
+ }
+
+ Faces = new List();
+ for (var i = 0; i < faceCount; i++)
+ {
+ var faceVertexCount = Reader.ReadInt32();
+ var faceVertices = new List();
+ for (var v = 0; v < faceVertexCount; v++)
+ {
+ faceVertices.Add(Reader.ReadInt32());
+ }
+
+ if (hasNormalAbcd)
+ {
+ var normalAbcd = new vec4(
+ Reader.ReadSingle(),
+ Reader.ReadSingle(),
+ Reader.ReadSingle(),
+ Reader.ReadSingle()
+ );
+ }
+
+ // 4 vertices will result in 2 triangles
+ var polygonCount = faceVertexCount - 2;
+ for (var f = 0; f < polygonCount; f++)
+ {
+ var polygon = new Polygon
+ {
+ IsSolid = true,
+ Vertex1 = faceVertices[0],
+ Vertex2 = faceVertices[f + 1],
+ Vertex3 = faceVertices[f + 2],
+ };
+ Faces.Add(polygon);
+ }
+ }
+ }
+ }
+}
diff --git a/LanternExtractor/EQ/Wld/Fragments/Fragment18.cs b/LanternExtractor/EQ/Wld/Fragments/PolyhedronReference.cs
similarity index 74%
rename from LanternExtractor/EQ/Wld/Fragments/Fragment18.cs
rename to LanternExtractor/EQ/Wld/Fragments/PolyhedronReference.cs
index 41efada..f3106c8 100644
--- a/LanternExtractor/EQ/Wld/Fragments/Fragment18.cs
+++ b/LanternExtractor/EQ/Wld/Fragments/PolyhedronReference.cs
@@ -1,31 +1,31 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using LanternExtractor.Infrastructure.Logger;
namespace LanternExtractor.EQ.Wld.Fragments
{
///
- /// Fragment18 (0x18) - PolygonAnimationReference?
+ /// PolyhedronReference (0x18)
/// Internal Name: None
/// Need to figure this fragment out.
///
- public class Fragment18 : WldFragment
+ public class PolyhedronReference : WldFragment
{
- public Fragment17 Fragment17;
-
+ public Polyhedron Polyhedron;
+
public override void Initialize(int index, int size, byte[] data,
List fragments,
Dictionary stringHash, bool isNewWldFormat, ILogger logger)
{
base.Initialize(index, size, data, fragments, stringHash, isNewWldFormat, logger);
Name = stringHash[-Reader.ReadInt32()];
- Fragment17 = fragments[Reader.ReadInt32() - 1] as Fragment17;
+ Polyhedron = fragments[Reader.ReadInt32() - 1] as Polyhedron;
float params1 = Reader.ReadSingle();
}
-
+
public override void OutputInfo(ILogger logger)
{
base.OutputInfo(logger);
logger.LogInfo("-----");
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Fragments/SkeletonHierarchy.cs b/LanternExtractor/EQ/Wld/Fragments/SkeletonHierarchy.cs
index 781f44b..6ff962f 100644
--- a/LanternExtractor/EQ/Wld/Fragments/SkeletonHierarchy.cs
+++ b/LanternExtractor/EQ/Wld/Fragments/SkeletonHierarchy.cs
@@ -20,7 +20,7 @@ public class SkeletonHierarchy : WldFragment
public List AlternateMeshes { get; private set; }
public List Skeleton { get; set; }
- private Fragment18 _fragment18Reference;
+ private PolyhedronReference _fragment18Reference;
public string ModelBase { get; set; }
public bool IsAssigned { get; set; }
@@ -34,6 +34,7 @@ public class SkeletonHierarchy : WldFragment
public float BoundingRadius;
public List SecondaryMeshes = new List();
+ public List SecondaryAlternateMeshes = new List();
private bool _hasBuiltData;
@@ -71,7 +72,7 @@ public override void Initialize(int index, int size, byte[] data,
if (fragment18Reference > 0)
{
- _fragment18Reference = fragments[fragment18Reference] as Fragment18;
+ _fragment18Reference = fragments[fragment18Reference] as PolyhedronReference;
}
// Three sequential DWORDs
@@ -92,7 +93,7 @@ public override void Initialize(int index, int size, byte[] data,
// An index into the string has to get this bone's name
int boneNameIndex = Reader.ReadInt32();
string boneName = string.Empty;
-
+
if (stringHash.ContainsKey(-boneNameIndex))
{
boneName = stringHash[-boneNameIndex];
@@ -111,17 +112,17 @@ public override void Initialize(int index, int size, byte[] data,
var pieceNew = new SkeletonBone
{
- Index = i,
- Track = track,
+ Index = i,
+ Track = track,
Name = boneName
};
-
+
pieceNew.Track.IsPoseAnimation = true;
pieceNew.AnimationTracks = new Dictionary();
-
+
BoneMappingClean[i] = Animation.CleanBoneAndStripBase(boneName, ModelBase);
BoneMapping[i] = boneName;
-
+
if (pieceNew.Track == null)
{
logger.LogError("Unable to link track reference!");
@@ -216,8 +217,8 @@ public void BuildSkeletonData(bool stripModelBase)
{
return;
}
-
- BuildSkeletonTreeData(0, Skeleton, null, string.Empty,
+
+ BuildSkeletonTreeData(0, Skeleton, null, string.Empty,
string.Empty, string.Empty, stripModelBase);
_hasBuiltData = true;
}
@@ -375,14 +376,14 @@ public void AddTrackData(TrackFragment track, bool isDefault = false)
track.IsProcessed = true;
}
- private void BuildSkeletonTreeData(int index, List treeNodes, SkeletonBone parent,
+ private void BuildSkeletonTreeData(int index, List treeNodes, SkeletonBone parent,
string runningName, string runningNameCleaned, string runningIndex, bool stripModelBase)
{
SkeletonBone bone = treeNodes[index];
bone.Parent = parent;
bone.CleanedName = CleanBoneName(bone.Name, stripModelBase);
BoneMappingClean[index] = bone.CleanedName;
-
+
if (bone.Name != string.Empty)
{
runningIndex += bone.Index + "/";
@@ -424,12 +425,12 @@ private string CleanBoneName(string nodeName, bool stripModelBase)
public void AddAdditionalMesh(Mesh mesh)
{
- if (Meshes.Any(x => x.Name == mesh.Name)
+ if (Meshes.Any(x => x.Name == mesh.Name)
|| SecondaryMeshes.Any(x => x.Name == mesh.Name))
{
return;
}
-
+
if (mesh.MobPieces.Count == 0)
{
return;
@@ -439,6 +440,23 @@ public void AddAdditionalMesh(Mesh mesh)
SecondaryMeshes = SecondaryMeshes.OrderBy(x => x.Name).ToList();
}
+ public void AddAdditionalAlternateMesh(LegacyMesh mesh)
+ {
+ if (AlternateMeshes.Any(x => x.Name == mesh.Name)
+ || SecondaryAlternateMeshes.Any(x => x.Name == mesh.Name))
+ {
+ return;
+ }
+
+ if (mesh.MobPieces.Count == 0)
+ {
+ return;
+ }
+
+ SecondaryAlternateMeshes.Add(mesh);
+ SecondaryAlternateMeshes = SecondaryAlternateMeshes.OrderBy(x => x.Name).ToList();
+ }
+
public bool IsValidSkeleton(string trackName, out string boneName)
{
string track = trackName.Substring(3);
@@ -476,7 +494,7 @@ public mat4 GetBoneMatrix(int boneIndex, string animName, int frame)
}
var currentBone = Skeleton[boneIndex];
-
+
mat4 boneMatrix = mat4.Identity;
while (currentBone != null)
@@ -485,16 +503,16 @@ public mat4 GetBoneMatrix(int boneIndex, string animName, int frame)
{
break;
}
-
+
var track = Animations[animName].TracksCleanedStripped[currentBone.CleanedName].TrackDefFragment;
int realFrame = frame >= track.Frames.Count ? 0 : frame;
currentBone = Skeleton[boneIndex].Parent;
-
+
float scaleValue = track.Frames[realFrame].Scale;
var scaleMat = mat4.Scale(scaleValue, scaleValue, scaleValue);
-
+
var rotationMatrix = new mat4(track.Frames[realFrame].Rotation);
-
+
var translation = track.Frames[realFrame].Translation;
var translateMat = mat4.Translate(translation);
@@ -529,4 +547,4 @@ public void RenameNodeBase(string newBase)
ModelBase = newBase;
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Fragments/TrackDefFragment.cs b/LanternExtractor/EQ/Wld/Fragments/TrackDefFragment.cs
index a05dc34..42d4621 100644
--- a/LanternExtractor/EQ/Wld/Fragments/TrackDefFragment.cs
+++ b/LanternExtractor/EQ/Wld/Fragments/TrackDefFragment.cs
@@ -19,7 +19,7 @@ public class TrackDefFragment : WldFragment
/// A list of bone positions for each frame
///
public List Frames { get; set; }
-
+
public bool IsAssigned;
public override void Initialize(int index, int size, byte[] data,
@@ -36,46 +36,72 @@ public override void Initialize(int index, int size, byte[] data,
// Flags are always 8 when dealing with object animations
if (flags != 8)
{
-
+
}
BitAnalyzer bitAnalyzer = new BitAnalyzer(flags);
- bool hasData2Values = bitAnalyzer.IsBitSet(3);
-
+ bool isS3dTrack2 = bitAnalyzer.IsBitSet(3);
+
int frameCount = Reader.ReadInt32();
-
+
Frames = new List();
- for (int i = 0; i < frameCount; ++i)
+ if (isS3dTrack2)
{
- Int16 rotDenominator = Reader.ReadInt16();
- Int16 rotX = Reader.ReadInt16();
- Int16 rotY = Reader.ReadInt16();
- Int16 rotZ = Reader.ReadInt16();
- Int16 shiftX = Reader.ReadInt16();
- Int16 shiftY = Reader.ReadInt16();
- Int16 shiftZ = Reader.ReadInt16();
- Int16 shiftDenominator = Reader.ReadInt16();
-
- BoneTransform frameTransform = new BoneTransform();
-
- if (shiftDenominator != 0)
+ for (int i = 0; i < frameCount; ++i)
{
- float x = shiftX / 256f;
- float y = shiftY / 256f;
- float z = shiftZ / 256f;
+ Int16 rotDenominator = Reader.ReadInt16();
+ Int16 rotX = Reader.ReadInt16();
+ Int16 rotY = Reader.ReadInt16();
+ Int16 rotZ = Reader.ReadInt16();
+ Int16 shiftX = Reader.ReadInt16();
+ Int16 shiftY = Reader.ReadInt16();
+ Int16 shiftZ = Reader.ReadInt16();
+ Int16 shiftDenominator = Reader.ReadInt16();
+
+ BoneTransform frameTransform = new BoneTransform();
+
+ if (shiftDenominator != 0)
+ {
+ float x = shiftX / 256f;
+ float y = shiftY / 256f;
+ float z = shiftZ / 256f;
- frameTransform.Scale = shiftDenominator / 256f;
- frameTransform.Translation = new vec3(x, y, z);
+ frameTransform.Scale = shiftDenominator / 256f;
+ frameTransform.Translation = new vec3(x, y, z);
+ }
+ else
+ {
+ frameTransform.Translation = vec3.Zero;
+ }
+
+ frameTransform.Rotation = new quat(rotX, rotY, rotZ, rotDenominator).Normalized;
+ Frames.Add(frameTransform);
}
- else
+ }
+ else
+ {
+ for (int i = 0; i < frameCount; ++i)
{
- frameTransform.Translation = vec3.Zero;
+ var shiftDenominator = Reader.ReadSingle();
+ var shiftX = Reader.ReadSingle();
+ var shiftY = Reader.ReadSingle();
+ var shiftZ = Reader.ReadSingle();
+ var rotW = Reader.ReadSingle();
+ var rotX = Reader.ReadSingle();
+ var rotY = Reader.ReadSingle();
+ var rotZ = Reader.ReadSingle();
+
+ var frameTransform = new BoneTransform()
+ {
+ Scale = shiftDenominator,
+ Translation = new vec3(shiftX, shiftY, shiftZ),
+ Rotation = new quat(rotX, rotY, rotZ, rotW).Normalized,
+ };
+
+ Frames.Add(frameTransform);
}
-
- frameTransform.Rotation = new quat(rotX, rotY, rotZ, rotDenominator).Normalized;
- Frames.Add(frameTransform);
}
}
@@ -86,4 +112,4 @@ public override void OutputInfo(ILogger logger)
logger.LogInfo("0x12: Bone frame count: " + Frames.Count);
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Helpers/CharacterFixer.cs b/LanternExtractor/EQ/Wld/Helpers/CharacterFixer.cs
index 5f63067..06d471b 100644
--- a/LanternExtractor/EQ/Wld/Helpers/CharacterFixer.cs
+++ b/LanternExtractor/EQ/Wld/Helpers/CharacterFixer.cs
@@ -29,6 +29,7 @@ public void Fix(WldFileCharacters wld)
FixBlackAndWhiteDragon();
FixGhoulTextures();
FixHalasFemale();
+ FixBetaBeetle();
FixHighpassMale();
}
@@ -292,7 +293,7 @@ private void FixShipNames()
actor?.MeshReference?.Mesh?.MaterialList?.Name?.Replace("GHOSTSHIP", "GSP") ?? "GSP";
}
- if (actor.Name.StartsWith("LAUNCH"))
+ if (actor.Name.StartsWith("LAUNCH") && (actor?.MeshReference?.Mesh?.Name ?? null) != null)
{
actor.Name = actor.MeshReference.Mesh.Name.Replace("DMSPRITEDEF", "ACTORDEF");
}
@@ -384,13 +385,32 @@ private void FixHalasFemale()
return;
}
- skeleton.BoneMappingClean[7] = "bi_l";
- skeleton.BoneMappingClean[10] = "l_point";
- skeleton.BoneMappingClean[15] = "head_point";
+ RenameBone(skeleton, 7, "bi_l");
+ RenameBone(skeleton, 10, "l_point");
+ RenameBone(skeleton, 15, "head_point");
+ }
+
+ ///
+ /// Early beta beetles have SPI named bones
+ ///
+ private void FixBetaBeetle()
+ {
+ var skeleton = _wld.GetFragmentByName("BET_HS_DEF");
- skeleton.Skeleton[7].CleanedName = "bi_l";
- skeleton.Skeleton[10].CleanedName = "l_point";
- skeleton.Skeleton[15].CleanedName = "head_point";
+ if (skeleton == null)
+ {
+ return;
+ }
+
+ for (var i = 0; i < skeleton.Skeleton.Count; i++)
+ {
+ var bone = skeleton.Skeleton[i];
+ var boneName = bone.CleanedName;
+ if (boneName.StartsWith("spi"))
+ {
+ RenameBone(skeleton, i, boneName.Substring(3));
+ }
+ }
}
///
@@ -406,6 +426,11 @@ private void FixHighpassMale()
return;
}
+ if (skeleton.Skeleton.Count < 25)
+ {
+ return;
+ }
+
skeleton.Skeleton[0].Children = new List
{
1
@@ -413,5 +438,24 @@ private void FixHighpassMale()
skeleton.Skeleton.RemoveAt(24);
}
+
+ private void RenameBone(SkeletonHierarchy skeleton, int index, string newBoneName)
+ {
+ var oldBoneName = skeleton.BoneMappingClean[index];
+
+ skeleton.BoneMappingClean[index] = newBoneName;
+ skeleton.Skeleton[index].CleanedName = newBoneName;
+
+ for (var i = 0; i < skeleton.Skeleton.Count; i++)
+ {
+ var fullPath = skeleton.Skeleton[i].CleanedFullPath;
+ if (!fullPath.Contains(oldBoneName))
+ {
+ continue;
+ }
+
+ skeleton.Skeleton[i].CleanedFullPath = fullPath.Replace(oldBoneName, newBoneName);
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Helpers/FragmentNameCleaner.cs b/LanternExtractor/EQ/Wld/Helpers/FragmentNameCleaner.cs
index 8df4857..ac2cf36 100644
--- a/LanternExtractor/EQ/Wld/Helpers/FragmentNameCleaner.cs
+++ b/LanternExtractor/EQ/Wld/Helpers/FragmentNameCleaner.cs
@@ -28,13 +28,13 @@ public static string CleanName(WldFragment fragment, bool toLower = true)
{
cleanedName = cleanedName.Replace(_prefixes[fragment.GetType()], string.Empty);
}
-
+
if(toLower)
{
cleanedName = cleanedName.ToLower();
}
-
- return cleanedName;
+
+ return cleanedName.Trim();
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/Helpers/MaterialFixer.cs b/LanternExtractor/EQ/Wld/Helpers/MaterialFixer.cs
index 6bfffdf..48fd64a 100644
--- a/LanternExtractor/EQ/Wld/Helpers/MaterialFixer.cs
+++ b/LanternExtractor/EQ/Wld/Helpers/MaterialFixer.cs
@@ -11,24 +11,47 @@ public static class MaterialFixer
{
public static void Fix(WldFile wld)
{
- FixShaderAssignment(wld, "TREE20_MDF", ShaderType.TransparentMasked);
- FixShaderAssignment(wld, "TOP_MDF", ShaderType.TransparentMasked);
- FixShaderAssignment(wld, "FURPILE1_MDF", ShaderType.TransparentMasked);
- FixShaderAssignment(wld, "BEARRUG_MDF", ShaderType.TransparentMasked);
- FixShaderAssignment(wld, "FIRE1_MDF", ShaderType.TransparentAdditiveUnlit);
- FixShaderAssignment(wld, "ICE1_MDF", ShaderType.Invisible);
- FixShaderAssignment(wld, "AIRCLOUD_MDF", ShaderType.TransparentSkydome);
- FixShaderAssignment(wld, "NORMALCLOUD_MDF", ShaderType.TransparentSkydome);
- }
-
- private static void FixShaderAssignment(WldFile wld, string materialName, ShaderType shader)
- {
- var material = wld.GetFragmentByName(materialName);
+ var materials = wld.GetFragmentsOfType();
- if (material != null)
+ foreach (var material in materials)
{
- material.ShaderType = shader;
+ switch (material.Name)
+ {
+ case "TREE7_MDF":
+ case "TREE9B1_MDF":
+ case "TREE16_MDF":
+ case "TREE16B1_MDF":
+ case "TREE17_MDF":
+ case "TREE18_MDF":
+ case "TREE18B1_MDF":
+ case "TREE20_MDF":
+ case "TREE20B1_MDF":
+ case "TREE21_MDF":
+ case "TREE22_MDF":
+ case "TREE22B1_MDF":
+ case "TOP_MDF":
+ case "TOPB_MDF":
+ case "FURPILE1_MDF":
+ case "BEARRUG_MDF":
+ FixShaderAssignment(material, ShaderType.TransparentMasked);
+ break;
+ case "FIRE1_MDF":
+ FixShaderAssignment(material, ShaderType.TransparentAdditiveUnlit);
+ break;
+ case "ICE1_MDF":
+ FixShaderAssignment(material, ShaderType.Invisible);
+ break;
+ case "AIRCLOUD_MDF":
+ case "NORMALCLOUD_MDF":
+ FixShaderAssignment(material, ShaderType.TransparentSkydome);
+ break;
+ }
}
}
+
+ private static void FixShaderAssignment(Material material, ShaderType shader)
+ {
+ material.ShaderType = shader;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/WldFile.cs b/LanternExtractor/EQ/Wld/WldFile.cs
index 98394b2..2966080 100644
--- a/LanternExtractor/EQ/Wld/WldFile.cs
+++ b/LanternExtractor/EQ/Wld/WldFile.cs
@@ -2,13 +2,12 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using LanternExtractor.EQ.Pfs;
+using LanternExtractor.EQ.Archive;
using LanternExtractor.EQ.Wld.DataTypes;
using LanternExtractor.EQ.Wld.Exporters;
using LanternExtractor.EQ.Wld.Fragments;
using LanternExtractor.EQ.Wld.Helpers;
using LanternExtractor.Infrastructure.Logger;
-using LanternExtractor.EQ;
namespace LanternExtractor.EQ.Wld
{
@@ -18,20 +17,17 @@ namespace LanternExtractor.EQ.Wld
public abstract class WldFile
{
public string RootExportFolder;
- public string ZoneShortname => _zoneName;
-
-
- public WldType WldType => _wldType;
+ public string ZoneShortname => ZoneName;
///
- /// The link between fragment types and fragment classes
+ /// The type of WLD file this is
///
- private Dictionary> _fragmentBuilder;
+ public WldType WldType { get; }
///
/// A link of indices to fragments
///
- protected List _fragments;
+ protected List Fragments;
///
/// The string has containing the index in the hash and the decoded string that is there
@@ -42,46 +38,41 @@ public abstract class WldFile
/// A collection of fragment lists that can be referenced by a fragment type
///
//protected Dictionary> _fragmentTypeDictionary;
- protected Dictionary> _fragmentTypeDictionary;
+ protected Dictionary> FragmentTypeDictionary;
///
/// A collection of fragment lists that can be referenced by a fragment type
///
- protected Dictionary _fragmentNameDictionary;
+ protected Dictionary FragmentNameDictionary;
- protected List _bspRegions;
+ protected List BspRegions;
///
/// The shortname of the zone this WLD is from
///
- protected readonly string _zoneName;
+ protected readonly string ZoneName;
///
/// The logger to use to output WLD information
///
- protected readonly ILogger _logger;
-
- ///
- /// The type of WLD file this is
- ///
- protected readonly WldType _wldType;
+ protected readonly ILogger Logger;
///
- /// The WLD file found in the PFS archive
+ /// The WLD file found in the archive
///
- private readonly PfsFile _wldFile;
+ private readonly ArchiveFile _wldFile;
///
/// Cached settings
///
- protected readonly Settings _settings;
+ protected readonly Settings Settings;
///
/// Is this the new WLD format? Some data types are different
///
private bool _isNewWldFormat;
- protected readonly WldFile _wldToInject;
+ protected readonly WldFile WldToInject;
public Dictionary FilenameChanges = new Dictionary();
@@ -93,15 +84,15 @@ public abstract class WldFile
/// The shortname of the zone
/// The type of WLD - used to determine what to extract
/// The logger used for debug output
- protected WldFile(PfsFile wldFile, string zoneName, WldType type, ILogger logger, Settings settings,
+ protected WldFile(ArchiveFile wldFile, string zoneName, WldType type, ILogger logger, Settings settings,
WldFile fileToInject)
{
_wldFile = wldFile;
- _zoneName = zoneName.ToLower();
- _wldType = type;
- _logger = logger;
- _settings = settings;
- _wldToInject = fileToInject;
+ ZoneName = zoneName.ToLower();
+ WldType = type;
+ Logger = logger;
+ Settings = settings;
+ WldToInject = fileToInject;
}
///
@@ -110,14 +101,14 @@ protected WldFile(PfsFile wldFile, string zoneName, WldType type, ILogger logger
public virtual bool Initialize(string rootFolder, bool exportData = true)
{
RootExportFolder = rootFolder;
- _logger.LogInfo("Extracting WLD archive: " + _wldFile.Name);
- _logger.LogInfo("-----------------------------------");
- _logger.LogInfo("WLD type: " + _wldType);
+ Logger.LogInfo("Extracting WLD archive: " + _wldFile.Name);
+ Logger.LogInfo("-----------------------------------");
+ Logger.LogInfo("WLD type: " + WldType);
- _fragments = new List();
- _fragmentTypeDictionary = new Dictionary>();
- _fragmentNameDictionary = new Dictionary();
- _bspRegions = new List();
+ Fragments = new List();
+ FragmentTypeDictionary = new Dictionary>();
+ FragmentNameDictionary = new Dictionary();
+ BspRegions = new List();
var reader = new BinaryReader(new MemoryStream(_wldFile.Bytes));
@@ -129,7 +120,7 @@ public virtual bool Initialize(string rootFolder, bool exportData = true)
if (identifier != WldIdentifier.WldFileIdentifier)
{
- _logger.LogError("Not a valid WLD file!");
+ Logger.LogError("Not a valid WLD file!");
return false;
}
@@ -141,10 +132,10 @@ public virtual bool Initialize(string rootFolder, bool exportData = true)
break;
case WldIdentifier.WldFormatNewIdentifier:
_isNewWldFormat = true;
- _logger.LogWarning("New WLD format not fully supported.");
+ Logger.LogWarning("New WLD format not fully supported.");
break;
default:
- _logger.LogError("Unrecognized WLD format.");
+ Logger.LogError("Unrecognized WLD format.");
return false;
}
@@ -159,8 +150,6 @@ public virtual bool Initialize(string rootFolder, bool exportData = true)
ParseStringHash(WldStringDecoder.DecodeString(stringHash));
- long readPosition = 0;
-
for (int i = 0; i < fragmentCount; ++i)
{
uint fragSize = reader.ReadUInt32();
@@ -172,31 +161,31 @@ public virtual bool Initialize(string rootFolder, bool exportData = true)
if (newFragment is Generic)
{
- _logger.LogWarning($"WldFile: Unhandled fragment type: {fragId:x}");
+ Logger.LogWarning($"WldFile: Unhandled fragment type: {fragId:x}");
}
- newFragment.Initialize(i, (int)fragSize, reader.ReadBytes((int)fragSize), _fragments, _stringHash,
+ newFragment.Initialize(i, (int)fragSize, reader.ReadBytes((int)fragSize), Fragments, _stringHash,
_isNewWldFormat,
- _logger);
- newFragment.OutputInfo(_logger);
+ Logger);
+ newFragment.OutputInfo(Logger);
- _fragments.Add(newFragment);
+ Fragments.Add(newFragment);
- if (!_fragmentTypeDictionary.ContainsKey(newFragment.GetType()))
+ if (!FragmentTypeDictionary.ContainsKey(newFragment.GetType()))
{
- _fragmentTypeDictionary[newFragment.GetType()] = new List();
+ FragmentTypeDictionary[newFragment.GetType()] = new List();
}
- if (!string.IsNullOrEmpty(newFragment.Name) && !_fragmentNameDictionary.ContainsKey(newFragment.Name))
+ if (!string.IsNullOrEmpty(newFragment.Name) && !FragmentNameDictionary.ContainsKey(newFragment.Name))
{
- _fragmentNameDictionary[newFragment.Name] = newFragment;
+ FragmentNameDictionary[newFragment.Name] = newFragment;
}
- _fragmentTypeDictionary[newFragment.GetType()].Add(newFragment);
+ FragmentTypeDictionary[newFragment.GetType()].Add(newFragment);
}
- _logger.LogInfo("-----------------------------------");
- _logger.LogInfo("WLD extraction complete");
+ Logger.LogInfo("-----------------------------------");
+ Logger.LogInfo("WLD extraction complete");
ProcessData();
@@ -210,22 +199,22 @@ public virtual bool Initialize(string rootFolder, bool exportData = true)
public List GetFragmentsOfType() where T : WldFragment
{
- if (!_fragmentTypeDictionary.ContainsKey(typeof(T)))
+ if (!FragmentTypeDictionary.ContainsKey(typeof(T)))
{
return new List();
}
- return _fragmentTypeDictionary[typeof(T)].Cast().ToList();
+ return FragmentTypeDictionary[typeof(T)].Cast().ToList();
}
public T GetFragmentByName(string fragmentName) where T : WldFragment
{
- if (!_fragmentNameDictionary.ContainsKey(fragmentName))
+ if (!FragmentNameDictionary.ContainsKey(fragmentName))
{
return default(T);
}
- return _fragmentNameDictionary[fragmentName] as T;
+ return FragmentNameDictionary[fragmentName] as T;
}
protected virtual void ProcessData()
@@ -257,7 +246,7 @@ public virtual void ExportData()
{
ExportMeshes();
- if (_settings.ModelExportFormat == ModelExportFormat.Intermediate)
+ if (Settings.ModelExportFormat == ModelExportFormat.Intermediate)
{
ExportActors();
ExportSkeletonAndAnimations();
@@ -274,7 +263,7 @@ public List GetMaskedBitmaps()
if (materialLists.Count == 0)
{
- _logger.LogWarning("Cannot get material types. No texture list found.");
+ Logger.LogWarning("Cannot get material types. No texture list found.");
return null;
}
@@ -311,37 +300,37 @@ public List GetMaskedBitmaps()
private void ExportMeshes()
{
- if (_settings.ModelExportFormat == ModelExportFormat.Intermediate)
+ if (Settings.ModelExportFormat == ModelExportFormat.Intermediate)
{
- MeshExporter.ExportMeshes(this, _settings, _logger);
+ MeshExporter.ExportMeshes(this, Settings, Logger);
}
- else if (_settings.ModelExportFormat == ModelExportFormat.Obj)
+ else if (Settings.ModelExportFormat == ModelExportFormat.Obj)
{
- ActorObjExporter.ExportActors(this, _settings, _logger);
+ ActorObjExporter.ExportActors(this, Settings, Logger);
}
else
{
- ActorGltfExporter.ExportActors(this, _settings, _logger);
+ ActorGltfExporter.ExportActors(this, Settings, Logger);
}
}
public string GetExportFolderForWldType()
{
- switch (_wldType)
+ switch (WldType)
{
- case WldType.Zone:
- case WldType.Lights:
- case WldType.ZoneObjects:
+ case Wld.WldType.Zone:
+ case Wld.WldType.Lights:
+ case Wld.WldType.ZoneObjects:
return GetRootExportFolder() + "/Zone/";
- case WldType.Equipment:
+ case Wld.WldType.Equipment:
return GetRootExportFolder();
- case WldType.Objects:
+ case Wld.WldType.Objects:
return GetRootExportFolder() + "Objects/";
- case WldType.Sky:
+ case Wld.WldType.Sky:
return GetRootExportFolder();
- case WldType.Characters:
- if (_settings.ExportCharactersToSingleFolder &&
- _settings.ModelExportFormat == ModelExportFormat.Intermediate)
+ case Wld.WldType.Characters:
+ if (Settings.ExportCharactersToSingleFolder &&
+ Settings.ModelExportFormat == ModelExportFormat.Intermediate)
{
return GetRootExportFolder();
}
@@ -356,16 +345,16 @@ public string GetExportFolderForWldType()
protected string GetRootExportFolder()
{
- switch (_wldType)
+ switch (WldType)
{
- case WldType.Equipment when _settings.ExportEquipmentToSingleFolder &&
- _settings.ModelExportFormat == ModelExportFormat.Intermediate:
+ case Wld.WldType.Equipment when Settings.ExportEquipmentToSingleFolder &&
+ Settings.ModelExportFormat == ModelExportFormat.Intermediate:
return RootExportFolder + "equipment/";
- case WldType.Characters when (_settings.ExportCharactersToSingleFolder &&
- _settings.ModelExportFormat == ModelExportFormat.Intermediate):
+ case Wld.WldType.Characters when (Settings.ExportCharactersToSingleFolder &&
+ Settings.ModelExportFormat == ModelExportFormat.Intermediate):
return RootExportFolder + "characters/";
default:
- return RootExportFolder + ShortnameHelper.GetCorrectZoneShortname(_zoneName) + "/";
+ return RootExportFolder + ShortnameHelper.GetCorrectZoneShortname(ZoneName) + "/";
}
}
@@ -373,9 +362,9 @@ private void ExportActors()
{
var actors = GetFragmentsOfType();
- if (_wldToInject != null)
+ if (WldToInject != null)
{
- actors.AddRange(_wldToInject.GetFragmentsOfType());
+ actors.AddRange(WldToInject.GetFragmentsOfType());
}
if (actors.Count == 0)
@@ -385,9 +374,9 @@ private void ExportActors()
TextAssetWriter actorWriterStatic, actorWriterSkeletal, actorWriterParticle, actorWriterSprite2d;
- if (_wldType == WldType.Equipment && _settings.ExportEquipmentToSingleFolder || _wldType == WldType.Characters)
+ if (WldType == Wld.WldType.Equipment && Settings.ExportEquipmentToSingleFolder || WldType == Wld.WldType.Characters)
{
- bool isCharacters = _wldType == WldType.Characters;
+ bool isCharacters = WldType == Wld.WldType.Characters;
actorWriterStatic = new ActorWriterNewGlobal(ActorType.Static, GetExportFolderForWldType());
actorWriterSkeletal = new ActorWriterNewGlobal(ActorType.Skeletal, GetExportFolderForWldType());
actorWriterParticle = new ActorWriterNewGlobal(ActorType.Particle, GetExportFolderForWldType());
@@ -425,23 +414,23 @@ protected void ExportSkeletonAndAnimations()
if (skeletons.Count == 0)
{
- if (_wldToInject == null)
+ if (WldToInject == null)
{
- _logger.LogWarning("Cannot export animations. No model references.");
+ Logger.LogWarning("Cannot export animations. No model references.");
return;
}
- skeletons = _wldToInject.GetFragmentsOfType();
+ skeletons = WldToInject.GetFragmentsOfType();
if (skeletons == null)
{
- _logger.LogWarning("Cannot export animations. No model references.");
+ Logger.LogWarning("Cannot export animations 2. No model references.");
return;
}
}
- SkeletonHierarchyWriter skeletonWriter = new SkeletonHierarchyWriter(_wldType == WldType.Characters);
- AnimationWriter animationWriter = new AnimationWriter(_wldType == WldType.Characters);
+ SkeletonHierarchyWriter skeletonWriter = new SkeletonHierarchyWriter(WldType == Wld.WldType.Characters);
+ AnimationWriter animationWriter = new AnimationWriter(WldType == Wld.WldType.Characters);
foreach (var skeleton in skeletons)
{
@@ -450,7 +439,7 @@ protected void ExportSkeletonAndAnimations()
skeletonWriter.AddFragmentData(skeleton);
// TODO: Put this elsewhere - what does this even do?
- if (_wldType == WldType.Characters && _settings.ExportCharactersToSingleFolder)
+ if (WldType == Wld.WldType.Characters && Settings.ExportCharactersToSingleFolder)
{
if (File.Exists(filePath))
{
@@ -510,10 +499,10 @@ private void BuildSkeletonData()
foreach (var skeleton in skeletons)
{
- skeleton.BuildSkeletonData(_wldType == WldType.Characters || _settings.ModelExportFormat == ModelExportFormat.Intermediate);
+ skeleton.BuildSkeletonData(WldType == Wld.WldType.Characters || Settings.ModelExportFormat == ModelExportFormat.Intermediate);
}
- (_wldToInject as WldFileCharacters)?.BuildSkeletonData();
+ (WldToInject as WldFileCharacters)?.BuildSkeletonData();
}
}
}
diff --git a/LanternExtractor/EQ/Wld/WldFileCharacters.cs b/LanternExtractor/EQ/Wld/WldFileCharacters.cs
index 48cb1f2..e7c8b28 100644
--- a/LanternExtractor/EQ/Wld/WldFileCharacters.cs
+++ b/LanternExtractor/EQ/Wld/WldFileCharacters.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
-using LanternExtractor.EQ.Pfs;
+using LanternExtractor.EQ.Archive;
using LanternExtractor.EQ.Wld.Fragments;
using LanternExtractor.EQ.Wld.Helpers;
using LanternExtractor.Infrastructure;
@@ -11,9 +11,9 @@ namespace LanternExtractor.EQ.Wld
{
public class WldFileCharacters : WldFile
{
- public Dictionary AnimationSources = new Dictionary();
-
- public WldFileCharacters(PfsFile wldFile, string zoneName, WldType type, ILogger logger, Settings settings,
+ private readonly Dictionary _animationSources = new Dictionary();
+
+ public WldFileCharacters(ArchiveFile wldFile, string zoneName, WldType type, ILogger logger, Settings settings,
WldFile wldToInject = null) : base(wldFile, zoneName, type, logger, settings, wldToInject)
{
ParseAnimationSources();
@@ -24,10 +24,10 @@ private void ParseAnimationSources()
string filename = "ClientData/animationsources.txt";
if (!File.Exists(filename))
{
- _logger.LogError("WldFileCharacters: No animationsources.txt file found.");
+ Logger.LogError("WldFileCharacters: No animationsources.txt file found.");
return;
}
-
+
string fileText = File.ReadAllText(filename);
List> parsedText = TextParser.ParseTextByDelimitedLines(fileText, ',', '#');
@@ -37,14 +37,14 @@ private void ParseAnimationSources()
{
continue;
}
-
- AnimationSources[line[0].ToLower()] = line[1].ToLower();
- }
+
+ _animationSources[line[0].ToLower()] = line[1].ToLower();
+ }
}
-
+
private string GetAnimationModelLink(string modelName)
{
- return !AnimationSources.ContainsKey(modelName) ? modelName : AnimationSources[modelName];
+ return !_animationSources.ContainsKey(modelName) ? modelName : _animationSources[modelName];
}
protected override void ProcessData()
@@ -54,25 +54,25 @@ protected override void ProcessData()
BuildSlotMapping();
FindMaterialVariants();
- if (_settings.ExportCharactersToSingleFolder)
+ if (Settings.ExportCharactersToSingleFolder)
{
var characterFixer = new CharacterFixer();
characterFixer.Fix(this);
}
-
+
foreach (var skeleton in GetFragmentsOfType())
{
- skeleton.BuildSkeletonData(_wldType == WldType.Characters);
+ skeleton.BuildSkeletonData(WldType == Wld.WldType.Characters);
}
}
-
+
private void BuildSlotMapping()
{
var materialLists = GetFragmentsOfType();
foreach (var list in materialLists)
{
- list.BuildSlotMapping(_logger);
+ list.BuildSlotMapping(Logger);
}
}
@@ -95,7 +95,7 @@ private void FindMaterialVariants()
if (materialName.StartsWith(materialListModelName))
{
- list.AddVariant(material, _logger);
+ list.AddVariant(material, Logger);
}
}
}
@@ -106,8 +106,8 @@ private void FindMaterialVariants()
{
continue;
}
-
- _logger.LogWarning("WldFileCharacters: Material not assigned: " + material.Name);
+
+ Logger.LogWarning("WldFileCharacters: Material not assigned: " + material.Name);
}
}
@@ -122,14 +122,14 @@ private void FindAdditionalAnimationsAndMeshes()
if (skeletons.Count == 0)
{
- if (_wldToInject == null)
+ if (WldToInject == null)
{
return;
}
-
- skeletons = _wldToInject.GetFragmentsOfType();
+
+ skeletons = WldToInject.GetFragmentsOfType();
}
-
+
if (skeletons.Count == 0)
{
return;
@@ -146,7 +146,7 @@ private void FindAdditionalAnimationsAndMeshes()
string modelBase = skeleton.ModelBase;
string alternateModel = GetAnimationModelLink(modelBase);
-
+
// TODO: Alternate model bases
foreach (var track in GetFragmentsOfType())
{
@@ -157,11 +157,11 @@ private void FindAdditionalAnimationsAndMeshes()
if (!track.IsNameParsed)
{
- track.ParseTrackData(_logger);
+ track.ParseTrackData(Logger);
}
-
+
string trackModelBase = track.ModelName;
-
+
if (trackModelBase != modelBase && alternateModel != trackModelBase)
{
continue;
@@ -169,7 +169,7 @@ private void FindAdditionalAnimationsAndMeshes()
skeleton.AddTrackData(track);
}
-
+
// TODO: Split to another function
if(GetFragmentsOfType().Count != 0)
{
@@ -185,7 +185,7 @@ private void FindAdditionalAnimationsAndMeshes()
string basename = cleanedName;
bool endsWithNumber = char.IsDigit(cleanedName[cleanedName.Length - 1]);
-
+
if (endsWithNumber)
{
int id = Convert.ToInt32(cleanedName.Substring(cleanedName.Length - 2));
@@ -199,7 +199,7 @@ private void FindAdditionalAnimationsAndMeshes()
basename = cleanedName;
}
-
+
if (basename == modelBase)
{
skeleton.AddAdditionalMesh(mesh);
@@ -215,8 +215,8 @@ private void FindAdditionalAnimationsAndMeshes()
continue;
}
- _logger.LogWarning("WldFileCharacters: Track not assigned: " + track.Name);
+ Logger.LogWarning("WldFileCharacters: Track not assigned: " + track.Name);
}
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/WldFileEquipment.cs b/LanternExtractor/EQ/Wld/WldFileEquipment.cs
index 82cb248..db9ec11 100644
--- a/LanternExtractor/EQ/Wld/WldFileEquipment.cs
+++ b/LanternExtractor/EQ/Wld/WldFileEquipment.cs
@@ -1,4 +1,4 @@
-using LanternExtractor.EQ.Pfs;
+using LanternExtractor.EQ.Archive;
using LanternExtractor.EQ.Wld.Exporters;
using LanternExtractor.EQ.Wld.Fragments;
using LanternExtractor.EQ.Wld.Helpers;
@@ -8,7 +8,7 @@ namespace LanternExtractor.EQ.Wld
{
public class WldFileEquipment : WldFile
{
- public WldFileEquipment(PfsFile wldFile, string zoneName, WldType type, ILogger logger, Settings settings,
+ public WldFileEquipment(ArchiveFile wldFile, string zoneName, WldType type, ILogger logger, Settings settings,
WldFile wldToInject = null) : base(wldFile, zoneName, type, logger, settings, wldToInject)
{
}
@@ -41,7 +41,7 @@ private void ExportParticleSystems()
private void FindUnhandledSkeletons()
{
var skeletons = GetFragmentsOfType();
-
+
if (skeletons == null)
{
return;
@@ -56,13 +56,13 @@ private void FindUnhandledSkeletons()
string cleanedName = FragmentNameCleaner.CleanName(skeleton, false);
string actorName = cleanedName + "_ACTORDEF";
-
- if (!_fragmentNameDictionary.ContainsKey(actorName))
+
+ if (!FragmentNameDictionary.ContainsKey(actorName))
{
continue;
}
- (_fragmentNameDictionary[actorName] as Actor)?.AssignSkeletonReference(skeleton, _logger);
+ (FragmentNameDictionary[actorName] as Actor)?.AssignSkeletonReference(skeleton, Logger);
}
}
@@ -87,13 +87,13 @@ private void FindAdditionalAnimations()
{
continue;
}
-
+
foreach (var skeleton in skeletons)
{
string boneName = string.Empty;
if (skeleton.IsValidSkeleton(FragmentNameCleaner.CleanName(track), out boneName))
{
- _logger.LogError($"Assigning {track.Name} to {skeleton.Name}");
+ Logger.LogError($"Assigning {track.Name} to {skeleton.Name}");
track.IsProcessed = true;
skeleton.AddTrackDataEquipment(track, boneName.ToLower());
}
@@ -107,8 +107,8 @@ private void FindAdditionalAnimations()
continue;
}
- _logger.LogError("WldFileCharacters: Track not assigned: " + track.Name);
+ Logger.LogError("WldFileCharacters: Track not assigned: " + track.Name);
}
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/WldFileLights.cs b/LanternExtractor/EQ/Wld/WldFileLights.cs
index c118f1e..5da2775 100644
--- a/LanternExtractor/EQ/Wld/WldFileLights.cs
+++ b/LanternExtractor/EQ/Wld/WldFileLights.cs
@@ -1,4 +1,4 @@
-using LanternExtractor.EQ.Pfs;
+using LanternExtractor.EQ.Archive;
using LanternExtractor.EQ.Wld.Exporters;
using LanternExtractor.EQ.Wld.Fragments;
using LanternExtractor.Infrastructure.Logger;
@@ -7,7 +7,7 @@ namespace LanternExtractor.EQ.Wld
{
public class WldFileLights : WldFile
{
- public WldFileLights(PfsFile wldFile, string zoneName, WldType type, ILogger logger, Settings settings,
+ public WldFileLights(ArchiveFile wldFile, string zoneName, WldType type, ILogger logger, Settings settings,
WldFile wldToInject = null) : base(wldFile, zoneName, type, logger, settings, wldToInject)
{
}
@@ -19,7 +19,7 @@ public override void ExportData()
{
ExportLightInstanceList();
}
-
+
///
/// Exports the list of light instances (contains position, colors, radius)
///
@@ -29,7 +29,7 @@ private void ExportLightInstanceList()
if (lightInstances.Count == 0)
{
- _logger.LogWarning("Unable to export light instance list. No instances found.");
+ Logger.LogWarning("Unable to export light instance list. No instances found.");
return;
}
@@ -39,8 +39,8 @@ private void ExportLightInstanceList()
{
writer.AddFragmentData(light);
}
-
+
writer.WriteAssetToFile(GetExportFolderForWldType() + "light_instances.txt");
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/WldFileZone.cs b/LanternExtractor/EQ/Wld/WldFileZone.cs
index 334f0ae..95cecf4 100644
--- a/LanternExtractor/EQ/Wld/WldFileZone.cs
+++ b/LanternExtractor/EQ/Wld/WldFileZone.cs
@@ -1,4 +1,4 @@
-using LanternExtractor.EQ.Pfs;
+using LanternExtractor.EQ.Archive;
using LanternExtractor.EQ.Wld.DataTypes;
using LanternExtractor.EQ.Wld.Exporters;
using LanternExtractor.EQ.Wld.Fragments;
@@ -8,7 +8,7 @@ namespace LanternExtractor.EQ.Wld
{
public class WldFileZone : WldFile
{
- public WldFileZone(PfsFile wldFile, string zoneName, WldType type, ILogger logger, Settings settings,
+ public WldFileZone(ArchiveFile wldFile, string zoneName, WldType type, ILogger logger, Settings settings,
WldFile wldToInject = null) : base(wldFile, zoneName, type, logger, settings, wldToInject)
{
}
@@ -16,7 +16,7 @@ public WldFileZone(PfsFile wldFile, string zoneName, WldType type, ILogger logge
public string BasePath { get; set; } = "";
public string RootFolder { get; set; } = "";
public string ShortName { get; set; } = "";
- public PfsArchive BaseS3DArchive { get; set; } = null;
+ public ArchiveBase BaseS3DArchive { get; set; } = null;
public WldFile WldFileToInject { get; set; } = null;
public override void ExportData()
@@ -31,12 +31,12 @@ protected override void ProcessData()
base.ProcessData();
LinkBspReferences();
- if (_wldToInject != null)
+ if (WldToInject != null)
{
ImportVertexColors();
}
- if (_wldType == WldType.Objects)
+ if (WldType == Wld.WldType.Objects)
{
FixSkeletalObjectCollision();
}
@@ -67,7 +67,7 @@ private void FixSkeletalObjectCollision()
private void ImportVertexColors()
{
- var colors = _wldToInject.GetFragmentsOfType();
+ var colors = WldToInject.GetFragmentsOfType();
if (colors.Count == 0)
{
@@ -137,4 +137,4 @@ private void ExportBspTree()
writer.WriteAssetToFile(GetExportFolderForWldType() + "/bsp_tree.txt");
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/WldFileZoneObjects.cs b/LanternExtractor/EQ/Wld/WldFileZoneObjects.cs
index 0511e2f..742ce28 100644
--- a/LanternExtractor/EQ/Wld/WldFileZoneObjects.cs
+++ b/LanternExtractor/EQ/Wld/WldFileZoneObjects.cs
@@ -1,4 +1,4 @@
-using LanternExtractor.EQ.Pfs;
+using LanternExtractor.EQ.Archive;
using LanternExtractor.EQ.Wld.Exporters;
using LanternExtractor.EQ.Wld.Fragments;
using LanternExtractor.Infrastructure.Logger;
@@ -7,16 +7,16 @@ namespace LanternExtractor.EQ.Wld
{
public class WldFileZoneObjects : WldFile
{
- public WldFileZoneObjects(PfsFile wldFile, string zoneName, WldType type, ILogger logger, Settings settings, WldFile wldToInject = null) : base(
+ public WldFileZoneObjects(ArchiveFile wldFile, string zoneName, WldType type, ILogger logger, Settings settings, WldFile wldToInject = null) : base(
wldFile, zoneName, type, logger, settings, wldToInject)
{
}
-
+
public override void ExportData()
{
ExportObjectInstanceAndVertexColorList();
}
-
+
///
/// Exports the objects instance list (one file per zone) and vertex color lists (one file per object)
///
@@ -26,13 +26,13 @@ private void ExportObjectInstanceAndVertexColorList()
if (instanceList.Count == 0)
{
- _logger.LogWarning("Cannot export object instance list. No object instances found.");
+ Logger.LogWarning("Cannot export object instance list. No object instances found.");
return;
}
-
+
ObjectInstanceWriter instanceWriter = new ObjectInstanceWriter();
VertexColorsWriter colorWriter = new VertexColorsWriter();
-
+
string colorsExportFolder = GetRootExportFolder() + "Objects/VertexColors/";
foreach (var instance in instanceList)
@@ -43,15 +43,15 @@ private void ExportObjectInstanceAndVertexColorList()
{
continue;
}
-
+
colorWriter.AddFragmentData(instance.Colors);
colorWriter.WriteAssetToFile(colorsExportFolder + "vc_" + instance.Colors.Index + ".txt");
colorWriter.ClearExportData();
}
-
- if (_wldToInject != null)
+
+ if (WldToInject != null)
{
- instanceList = _wldToInject.GetFragmentsOfType();
+ instanceList = WldToInject.GetFragmentsOfType();
foreach (var instance in instanceList)
{
@@ -61,14 +61,14 @@ private void ExportObjectInstanceAndVertexColorList()
{
continue;
}
-
+
colorWriter.AddFragmentData(instance.Colors);
colorWriter.WriteAssetToFile(colorsExportFolder + "vc_" + instance.Colors.Index + ".txt");
colorWriter.ClearExportData();
}
}
-
+
instanceWriter.WriteAssetToFile(GetExportFolderForWldType() + "object_instances.txt");
}
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EQ/Wld/WldFragmentBuilder.cs b/LanternExtractor/EQ/Wld/WldFragmentBuilder.cs
index ab842b0..57008dc 100644
--- a/LanternExtractor/EQ/Wld/WldFragmentBuilder.cs
+++ b/LanternExtractor/EQ/Wld/WldFragmentBuilder.cs
@@ -23,6 +23,7 @@ public static class WldFragmentBuilder
// Meshes
{0x36, () => new Mesh()},
{0x37, () => new MeshAnimatedVertices()},
+ {0x2E, () => new LegacyMeshAnimatedVertices()},
{0x2F, () => new MeshAnimatedVerticesReference()},
{0x2D, () => new MeshReference()},
{0x2C, () => new LegacyMesh()},
@@ -57,10 +58,10 @@ public static class WldFragmentBuilder
{0x08, () => new Camera()},
{0x09, () => new CameraReference()},
{0x16, () => new Fragment16()},
- {0x17, () => new Fragment17()},
- {0x18, () => new Fragment18()},
+ {0x17, () => new Polyhedron()},
+ {0x18, () => new PolyhedronReference()},
{0x06, () => new Fragment06()},
{0x07, () => new Fragment07()},
};
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/EqFileHelper.cs b/LanternExtractor/EqFileHelper.cs
index f802037..e5e1e4c 100644
--- a/LanternExtractor/EqFileHelper.cs
+++ b/LanternExtractor/EqFileHelper.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -6,57 +6,76 @@ namespace LanternExtractor
{
public static class EqFileHelper
{
- public static bool IsEquipmentArchive(string archiveName)
+ public static List GetValidEqFilePaths(string directory, string archiveName)
{
- return archiveName.StartsWith("gequip");
+ archiveName = archiveName.ToLower();
+
+ if (!Directory.Exists(directory))
+ {
+ return new List();
+ }
+
+ var eqFiles = Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories);
+ List validFiles;
+
+ switch (archiveName)
+ {
+ case "all":
+ validFiles = GetAllValidFiles(eqFiles);
+ break;
+ case "zones":
+ validFiles = GetValidZoneFiles(eqFiles);
+ break;
+ case "characters":
+ validFiles = GetValidCharacterFiles(eqFiles);
+ break;
+ case "equipment":
+ validFiles = GetValidEquipmentFiles(eqFiles);
+ break;
+ case "sounds":
+ validFiles = GetValidSoundFiles(eqFiles);
+ break;
+ default:
+ {
+ validFiles = GetValidFiles(archiveName, directory);
+ break;
+ }
+ }
+
+ return validFiles;
}
- public static bool IsCharactersArchive(string archiveName)
+ private static List GetValidEquipmentFiles(string[] eqFiles)
{
- return archiveName.Contains("_chr") || archiveName.StartsWith("chequip") || archiveName.Contains("_amr");
+ return eqFiles.Where(x => IsEquipmentArchive(Path.GetFileName(x))).ToList();
}
- public static bool IsObjectsArchive(string archiveName)
+ private static List GetAllValidFiles(string[] eqFiles)
{
- return archiveName.Contains("_obj");
+ return eqFiles.Where(x => IsValidArchive(Path.GetFileName(x))).ToList();
}
- public static bool IsSkyArchive(string archiveName)
+ private static List GetValidZoneFiles(string[] eqFiles)
{
- return archiveName == "sky";
+ return eqFiles.Where(x => IsZoneArchive(Path.GetFileName(x))).ToList();
}
- public static bool IsSoundArchive(string archiveName)
+ private static List GetValidCharacterFiles(string[] eqFiles)
{
- return archiveName.StartsWith("snd");
+ return eqFiles.Where(x => IsCharacterArchive(Path.GetFileName(x))).ToList();
}
- public static bool IsClientDataFile(string archiveName)
+ private static List GetValidSoundFiles(string[] eqFiles)
{
- return archiveName == "clientdata";
+ return eqFiles.Where(x => IsSoundArchive(Path.GetFileName(x))).ToList();
}
- public static List GetValidEqFilePaths(string directory, string archiveName)
+ private static List GetValidFiles(string archiveName, string directory)
{
- archiveName = archiveName.ToLower();
-
var validFiles = new List();
-
- if (archiveName == "all")
- {
- validFiles = Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories)
- .Where(s => (s.EndsWith(".s3d") || s.EndsWith(".pfs")) && !s.Contains("chequip") &&
- !s.EndsWith("_lit.s3d")).ToList();
- }
- else if (archiveName == "equipment")
+ if (archiveName.EndsWith(".s3d") || archiveName.EndsWith(".pfs") || archiveName.EndsWith(".t3d"))
{
- validFiles = Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories)
- .Where(s => (s.EndsWith(".s3d") || s.EndsWith(".pfs")) && s.Contains("gequip")).ToList();
- }
- else if (archiveName.EndsWith(".s3d") || archiveName.EndsWith(".pfs"))
- {
- string archivePath = directory + archiveName;
-
+ string archivePath = Path.Combine(directory, archiveName);
if (File.Exists(archivePath))
{
validFiles.Add(archivePath);
@@ -64,38 +83,47 @@ public static List GetValidEqFilePaths(string directory, string archiveN
}
else
{
- // If it's the shortname of a PFS file, we assume it's standalone - used for sound files
- string archivePath = directory + archiveName + ".pfs";
+ string archivePath =
+ Path.Combine(directory, string.Concat(archiveName, LanternStrings.PfsFormatExtension));
if (File.Exists(archivePath))
{
validFiles.Add(archivePath);
-
return validFiles;
}
- // Try and find all associated files with the shortname - can theoretically be a non zone file
- string mainArchivePath = directory + archiveName + ".s3d";
+ var archiveExtension = LanternStrings.S3dFormatExtension;
+ if (Directory.EnumerateFiles(directory, $"*{LanternStrings.T3dFormatExtension}",
+ SearchOption.AllDirectories).Any())
+ {
+ archiveExtension = LanternStrings.T3dFormatExtension;
+ }
+
+ // Try and find all associated files with the shortname - can theoretically be a non-zone file
+ string mainArchivePath = Path.Combine(directory, string.Concat(archiveName, archiveExtension));
if (File.Exists(mainArchivePath))
{
validFiles.Add(mainArchivePath);
}
// Some zones have additional object archives for things added past their initial release
- // None of them contain fragments that are linked to other related archives.
- string extensionObjectsArchivePath = directory + archiveName + "_2_obj.s3d";
+ // No archives contain cross archive fragment references
+ string extensionObjectsArchivePath =
+ Path.Combine(directory, string.Concat($"{archiveName}_2_obj", archiveExtension));
if (File.Exists(extensionObjectsArchivePath))
{
validFiles.Add(extensionObjectsArchivePath);
}
- string objectsArchivePath = directory + archiveName + "_obj.s3d";
+ string objectsArchivePath =
+ Path.Combine(directory, string.Concat($"{archiveName}_obj", archiveExtension));
if (File.Exists(objectsArchivePath))
{
validFiles.Add(objectsArchivePath);
}
- string charactersArchivePath = directory + archiveName + "_chr.s3d";
+ string charactersArchivePath =
+ Path.Combine(directory, string.Concat($"{archiveName}_chr", archiveExtension));
if (File.Exists(charactersArchivePath))
{
validFiles.Add(charactersArchivePath);
@@ -104,7 +132,8 @@ public static List GetValidEqFilePaths(string directory, string archiveN
// Some zones have additional character archives for things added past their initial release
// None of them contain fragments that are linked to other related archives.
// "qeynos" must be excluded because both qeynos and qeynos2 are used as shortnames
- string extensionCharactersArchivePath = directory + archiveName + "2_chr.s3d";
+ string extensionCharactersArchivePath =
+ Path.Combine(directory, string.Concat($"{archiveName}2_chr", archiveExtension));
if (File.Exists(extensionCharactersArchivePath) && archiveName != "qeynos")
{
validFiles.Add(extensionCharactersArchivePath);
@@ -113,5 +142,104 @@ public static List GetValidEqFilePaths(string directory, string archiveN
return validFiles;
}
+
+ public static string ObjArchivePath(string archivePath)
+ {
+ var baseExt = Path.GetExtension(archivePath);
+ var lastDotIndex = archivePath.LastIndexOf('.');
+
+ if (lastDotIndex >= 0)
+ {
+ return $"{archivePath.Substring(0, lastDotIndex)}_obj{baseExt}";
+ }
+
+ return archivePath;
+ }
+
+ private static bool IsValidArchive(string archiveName)
+ {
+ // chequip contains broken/conflicting data.
+ // _lit archives get injected later during archive extraction
+ if (archiveName.Contains("chequip") || archiveName.EndsWith("_lit.s3d"))
+ {
+ return false;
+ }
+
+ return archiveName.EndsWith(".s3d") || archiveName.EndsWith(".t3d") || archiveName.EndsWith(".pfs");
+ }
+
+ private static bool IsZoneArchive(string archiveName)
+ {
+ return IsValidArchive(archiveName) && !IsEquipmentArchive(archiveName) && !IsSkyArchive(archiveName) &&
+ !IsBitmapArchive(archiveName) && !IsCharacterArchive(archiveName);
+ }
+
+ public static bool IsEquipmentArchive(string archiveName)
+ {
+ return archiveName.StartsWith("gequip");
+ }
+
+ public static bool IsCharacterArchive(string archiveName)
+ {
+ // chequip contains broken/conflicting data.
+ if (archiveName.Contains("chequip"))
+ {
+ return false;
+ }
+
+ return archiveName.Contains("_chr") || archiveName.StartsWith("chequip") ||
+ archiveName.Contains("_amr");
+ }
+
+ public static bool IsObjectsArchive(string archiveName)
+ {
+ return archiveName.Contains("_obj");
+ }
+
+ public static bool IsSkyArchive(string archiveName)
+ {
+ return archiveName == "sky";
+ }
+
+ public static bool IsBitmapArchive(string archiveName)
+ {
+ return archiveName.StartsWith("bmpwad");
+ }
+
+ public static bool IsSoundArchive(string archiveName)
+ {
+ return archiveName.StartsWith("snd");
+ }
+
+ public static bool IsClientDataFile(string archiveName)
+ {
+ return archiveName == "clientdata";
+ }
+
+ public static bool IsMusicFile(string filename)
+ {
+ return filename.EndsWith(".xmi");
+ }
+
+ public static bool IsSpecialCaseExtraction(string archiveName)
+ {
+ return archiveName == "clientdata" || archiveName == "music";
+ }
+
+ public static bool IsUsedSoundArchive(string archiveName)
+ {
+ if (!IsSoundArchive(archiveName))
+ {
+ return false;
+ }
+
+ // Trilogy client does not use archives higher than snd9
+ if (int.TryParse(archiveName.Substring(archiveName.Length - 2), out _))
+ {
+ return false;
+ }
+
+ return true;
+ }
}
}
diff --git a/LanternExtractor/FodyWeavers.xml b/LanternExtractor/FodyWeavers.xml
deleted file mode 100644
index a5dcf04..0000000
--- a/LanternExtractor/FodyWeavers.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/LanternExtractor/FodyWeavers.xsd b/LanternExtractor/FodyWeavers.xsd
deleted file mode 100644
index 05e92c1..0000000
--- a/LanternExtractor/FodyWeavers.xsd
+++ /dev/null
@@ -1,141 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
-
-
-
-
- A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
-
-
-
-
- A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks
-
-
-
-
- A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.
-
-
-
-
- A list of unmanaged 32 bit assembly names to include, delimited with line breaks.
-
-
-
-
- A list of unmanaged 64 bit assembly names to include, delimited with line breaks.
-
-
-
-
- The order of preloaded assemblies, delimited with line breaks.
-
-
-
-
-
- This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.
-
-
-
-
- Controls if .pdbs for reference assemblies are also embedded.
-
-
-
-
- Controls if runtime assemblies are also embedded.
-
-
-
-
- Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.
-
-
-
-
- Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.
-
-
-
-
- As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.
-
-
-
-
- Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.
-
-
-
-
- Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.
-
-
-
-
- A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
-
-
-
-
- A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.
-
-
-
-
- A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |
-
-
-
-
- A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.
-
-
-
-
- A list of unmanaged 32 bit assembly names to include, delimited with |.
-
-
-
-
- A list of unmanaged 64 bit assembly names to include, delimited with |.
-
-
-
-
- The order of preloaded assemblies, delimited with |.
-
-
-
-
-
-
-
- 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
-
-
-
-
- A comma-separated list of error codes that can be safely ignored in assembly verification.
-
-
-
-
- 'false' to turn off automatic generation of the XML Schema file.
-
-
-
-
-
\ No newline at end of file
diff --git a/LanternExtractor/Infrastructure/EqBmp.cs b/LanternExtractor/Infrastructure/EqBmp.cs
new file mode 100644
index 0000000..8b734ba
--- /dev/null
+++ b/LanternExtractor/Infrastructure/EqBmp.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+
+namespace LanternExtractor.Infrastructure
+{
+ ///
+ /// Wrapper over Bitmap with various hacks to make libgdiplus
+ /// consistently create transparent pngs on MacOS & Linux
+ ///
+ public class EqBmp
+ {
+ private static readonly bool NeedsGdipHacks = Environment.OSVersion.Platform == PlatformID.MacOSX ||
+ Environment.OSVersion.Platform == PlatformID.Unix;
+ private static bool _hasCheckedForPaletteFlagsField;
+ private static System.Reflection.FieldInfo _paletteFlagsField = null;
+
+ public PixelFormat PixelFormat => _bitmap.PixelFormat;
+
+ private readonly Bitmap _bitmap;
+ private readonly ColorPalette _palette;
+
+ public EqBmp(Stream stream)
+ {
+ SetPaletteFlagsField();
+
+ _bitmap = new Bitmap(stream);
+ _palette = _bitmap.Palette;
+ }
+
+ public void WritePng(string outputFilePath)
+ {
+ _bitmap.Save(outputFilePath, ImageFormat.Png);
+ }
+
+ public void MakeMagentaTransparent()
+ {
+ _bitmap.MakeTransparent(Color.Magenta);
+ if (NeedsGdipHacks)
+ {
+ // https://github.com/mono/libgdiplus/commit/bf9a1440b7bfea704bf2cb771f5c2b5c09e7bcfa
+ _bitmap.MakeTransparent(Color.FromArgb(0, Color.Magenta));
+ }
+ }
+
+ public void MakePaletteTransparent(int transparentIndex)
+ {
+ if (NeedsGdipHacks)
+ {
+ // https://github.com/mono/libgdiplus/issues/702
+ _paletteFlagsField?.SetValue(_palette, _palette.Flags | (int)PaletteFlags.HasAlpha);
+ }
+
+ var transparentColor = GetTransparentPaletteColor();
+ _palette.Entries[transparentIndex] = transparentColor;
+ _bitmap.Palette = _palette;
+
+ if (NeedsGdipHacks)
+ {
+ // Due to a bug with the libgdiplus implementation of System.Drawing, setting a color palette
+ // entry to transparent does not work. The workaround is to ensure that the transparent
+ // key is unique and then use MakeTransparent()
+ _bitmap.MakeTransparent(transparentColor);
+ }
+ }
+
+ private Color GetTransparentPaletteColor()
+ {
+ var transparencyColor = Color.FromArgb(0, 0, 0, 0);
+
+ if (!NeedsGdipHacks)
+ {
+ return transparencyColor;
+ }
+
+ var random = new Random();
+ var foundUnique = false;
+
+ while (!foundUnique)
+ {
+ foundUnique = _palette.Entries.All(e => e != transparencyColor);
+ transparencyColor = Color.FromArgb(0, random.Next(256), random.Next(256), random.Next(256));
+ }
+
+ return transparencyColor;
+ }
+
+ // https://github.com/Robmaister/SharpFont/blob/422bdab059dd8e594b4b061a3b53152e71342ce2/Source/SharpFont.GDI/FTBitmapExtensions.cs
+ // https://github.com/Robmaister/SharpFont/pull/136
+ private static void SetPaletteFlagsField()
+ {
+ if (!NeedsGdipHacks || _hasCheckedForPaletteFlagsField)
+ {
+ return;
+ }
+
+ _hasCheckedForPaletteFlagsField = true;
+
+ // The field needed may be named "flags" or "_flags", depending on the version of Mono. To be thorough, check for the first Name that contains "lags".
+ var fields = typeof(ColorPalette).GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
+
+ foreach (var t in fields)
+ {
+ if (t.Name.Contains("lags"))
+ {
+ _paletteFlagsField = t;
+ break;
+ }
+ }
+ }
+
+ enum PaletteFlags
+ {
+ HasAlpha = 0x0001,
+ GrayScale = 0x0002,
+ HalfTone = 0x0004,
+ }
+ }
+}
diff --git a/LanternExtractor/Infrastructure/ExtensionMethods.cs b/LanternExtractor/Infrastructure/ExtensionMethods.cs
new file mode 100644
index 0000000..24cf630
--- /dev/null
+++ b/LanternExtractor/Infrastructure/ExtensionMethods.cs
@@ -0,0 +1,19 @@
+using System.IO;
+using System.Text;
+
+namespace LanternExtractor.Infrastructure
+{
+ public static class ExtensionMethods
+ {
+ public static string ReadNullTerminatedString(this BinaryReader reader)
+ {
+ StringBuilder builder = new StringBuilder();
+ char nextCharacter;
+ while ((nextCharacter = reader.ReadChar()) != 0)
+ {
+ builder.Append(nextCharacter);
+ }
+ return builder.ToString();
+ }
+ }
+}
diff --git a/LanternExtractor/Infrastructure/ImageWriter.cs b/LanternExtractor/Infrastructure/ImageWriter.cs
index ac9471f..e3d09ed 100644
--- a/LanternExtractor/Infrastructure/ImageWriter.cs
+++ b/LanternExtractor/Infrastructure/ImageWriter.cs
@@ -31,8 +31,6 @@ public static void WriteImageAsPng(byte[] bytes, string filePath, string fileNam
private static void WriteBmpAsPng(byte[] bytes, string filePath, string fileName, bool isMasked, bool rotate,
ILogger logger)
{
- var byteStream = new MemoryStream(bytes);
-
if (string.IsNullOrEmpty(filePath))
{
return;
@@ -40,11 +38,12 @@ private static void WriteBmpAsPng(byte[] bytes, string filePath, string fileName
Directory.CreateDirectory(filePath);
- Bitmap image;
+ EqBmp image;
+ var byteStream = new MemoryStream(bytes);
try
{
- image = new Bitmap(byteStream);
+ image = new EqBmp(byteStream);
}
catch (Exception e)
{
@@ -60,76 +59,17 @@ private static void WriteBmpAsPng(byte[] bytes, string filePath, string fileName
fileName = "canwall1.png";
}
- Bitmap cloneBitmap;
-
- if (isMasked)
+ if (image.PixelFormat == PixelFormat.Format8bppIndexed && isMasked)
{
- cloneBitmap = image.Clone(new Rectangle(0, 0, image.Width, image.Height),
- PixelFormat.Format8bppIndexed);
-
- int paletteIndex = GetPaletteIndex(fileName);
- var palette = cloneBitmap.Palette;
-
- if (Environment.OSVersion.Platform != PlatformID.MacOSX &&
- Environment.OSVersion.Platform != PlatformID.Unix)
- {
- palette.Entries[paletteIndex] = Color.FromArgb(0, 0, 0, 0);
- cloneBitmap.Palette = palette;
- }
- else
- {
- // Due to a bug with the MacOS implementation of System.Drawing, setting a color palette value to
- // transparent does not work. The workaround is to ensure that the first palette value (the transparent
- // key) is unique and then use MakeTransparent()
- Color transparencyColor = palette.Entries[paletteIndex];
- bool isUnique = false;
-
- while (!isUnique)
- {
- isUnique = true;
-
- for (var i = 1; i < cloneBitmap.Palette.Entries.Length; i++)
- {
- Color paletteValue = cloneBitmap.Palette.Entries[i];
-
- if (paletteValue == transparencyColor)
- {
- Random random = new Random();
- transparencyColor = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256));
- isUnique = false;
- break;
- }
- }
- }
-
- palette.Entries[paletteIndex] = transparencyColor;
- cloneBitmap.Palette = palette;
- cloneBitmap.MakeTransparent(transparencyColor);
-
- // For some reason, this now has to be done to ensure the pixels are actually set to transparent
- // Another head scratching MacOS bug
- for (int i = 0; i < cloneBitmap.Width; ++i)
- {
- for (int j = 0; j < cloneBitmap.Height; ++j)
- {
- if (cloneBitmap.GetPixel(i, j) == transparencyColor)
- {
- cloneBitmap.SetPixel(i, j, Color.FromArgb(0, 0, 0, 0));
- }
- }
- }
- }
+ var paletteIndex = GetPaletteIndex(fileName);
+ image.MakePaletteTransparent(paletteIndex);
}
else
{
- cloneBitmap = image.Clone(new Rectangle(0, 0, image.Width, image.Height), PixelFormat.Format32bppArgb);
- if (image.PixelFormat != PixelFormat.Format8bppIndexed)
- {
- cloneBitmap.MakeTransparent(Color.Magenta);
- }
+ image.MakeMagentaTransparent();
}
- cloneBitmap.Save(Path.Combine(filePath, fileName), ImageFormat.Png);
+ image.WritePng(Path.Combine(filePath, fileName));
}
private static void WriteDdsAsPng(byte[] bytes, string filePath, string fileName)
diff --git a/LanternExtractor/LanternExtractor.cs b/LanternExtractor/LanternExtractor.cs
index 8afceb7..4dacf7b 100644
--- a/LanternExtractor/LanternExtractor.cs
+++ b/LanternExtractor/LanternExtractor.cs
@@ -53,11 +53,10 @@ private static void Main(string[] args)
}
var archiveName = args[0];
-
List eqFiles = EqFileHelper.GetValidEqFilePaths(_settings.EverQuestDirectory, archiveName);
eqFiles.Sort();
- if (eqFiles.Count == 0 && !EqFileHelper.IsClientDataFile(archiveName))
+ if (eqFiles.Count == 0 && !EqFileHelper.IsSpecialCaseExtraction(archiveName))
{
Console.WriteLine("No valid EQ files found for: '" + archiveName + "' at path: " +
_settings.EverQuestDirectory);
@@ -89,7 +88,9 @@ private static void Main(string[] args)
ArchiveExtractor.Extract(file, "Exports/", _logger, _settings);
}
}
+
ClientDataCopier.Copy(archiveName, "Exports/", _logger, _settings);
+ MusicCopier.Copy(archiveName, _logger, _settings);
Console.WriteLine($"Extraction complete ({(DateTime.Now - start).TotalSeconds:.00}s)");
}
diff --git a/LanternExtractor/LanternExtractor.csproj b/LanternExtractor/LanternExtractor.csproj
index e3380e1..67a1e45 100644
--- a/LanternExtractor/LanternExtractor.csproj
+++ b/LanternExtractor/LanternExtractor.csproj
@@ -1,205 +1,46 @@
-
-
-
+
- Debug
- AnyCPU
- {00229605-0107-4EEE-B523-9982A7738572}
Exe
- LanternExtractor
- LanternExtractor
- v4.8
- 512
- true
-
-
+ net6.0
+ true
+ true
+ true
7
+ false
+ CA1416
-
- AnyCPU
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
+
+ true
-
- AnyCPU
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
+
+ $([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture)
+ win-$(Arch.ToLower())
+ linux-$(Arch.ToLower())
+ osx-$(Arch.ToLower())
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
PreserveNewest
-
- PreserveNewest
-
PreserveNewest
-
- 5.1.0
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
- 1.10.1
-
+
0.9.8
+
0.10.0
-
- 1.0.0-alpha0025
-
1.0.0-alpha0025
+
+
+
+
-
-
\ No newline at end of file
+
diff --git a/LanternExtractor/LanternStrings.cs b/LanternExtractor/LanternStrings.cs
index b700550..0517317 100644
--- a/LanternExtractor/LanternStrings.cs
+++ b/LanternExtractor/LanternStrings.cs
@@ -5,9 +5,9 @@ namespace LanternExtractor
///
public static class LanternStrings
{
- public const string ExportHeaderTitle = "# Lantern Extractor 0.2 - ";
+ public const string ExportHeaderTitle = "# Lantern Extractor 0.1.7 - ";
public const string ExportHeaderFormat = "# Format: ";
-
+
public const string ObjMaterialHeader = "mtllib ";
public const string ObjUseMtlPrefix = "usemtl ";
public const string ObjNewMaterialPrefix = "newmtl";
@@ -16,7 +16,9 @@ public static class LanternStrings
public const string WldFormatExtension = ".wld";
public const string S3dFormatExtension = ".s3d";
- public const string PfsFormatExtension = ".s3d";
+ public const string PfsFormatExtension = ".pfs";
+ public const string PakFormatExtension = ".pak";
+ public const string T3dFormatExtension = ".t3d";
public const string SoundFormatExtension = ".eff";
}
-}
\ No newline at end of file
+}
diff --git a/LanternExtractor/ModelExportFormat.cs b/LanternExtractor/ModelExportFormat.cs
new file mode 100644
index 0000000..c13527c
--- /dev/null
+++ b/LanternExtractor/ModelExportFormat.cs
@@ -0,0 +1,9 @@
+namespace LanternExtractor
+{
+ public enum ModelExportFormat
+ {
+ Intermediate = 0,
+ Obj = 1,
+ GlTF = 2
+ }
+}
\ No newline at end of file
diff --git a/LanternExtractor/Settings.cs b/LanternExtractor/Settings.cs
index 7c94159..5aac280 100644
--- a/LanternExtractor/Settings.cs
+++ b/LanternExtractor/Settings.cs
@@ -1,25 +1,15 @@
using System;
-using System.Collections.Generic;
using System.IO;
using LanternExtractor.Infrastructure;
using LanternExtractor.Infrastructure.Logger;
-
namespace LanternExtractor
{
- public enum ModelExportFormat
- {
- Intermediate = 0,
- Obj = 1,
- GlTF = 2
- }
-
///
/// Simple class that parses settings for the extractor
///
public class Settings
{
-
///
/// The logger reference for debug output
///
@@ -55,20 +45,11 @@ public class Settings
/// Sets the desired model export format
///
public ModelExportFormat ModelExportFormat { get; private set; }
-
- ///
- /// Sets the desired model export format
- ///
+
public bool ExportCharactersToSingleFolder { get; private set; }
-
- ///
- /// Sets the desired model export format
- ///
+
public bool ExportEquipmentToSingleFolder { get; private set; }
-
- ///
- /// Export all sound files to a single folder
- ///
+
public bool ExportSoundsToSingleFolder { get; private set; }
///
@@ -101,6 +82,11 @@ public class Settings
/// Additional files that should be copied when extracting with `all` or `clientdata`
///
public string ClientDataToCopy { get; private set; }
+
+ ///
+ /// If enabled, XMI files will be copied to the 'Exports/Music' folder
+ ///
+ public bool CopyMusic { get; private set; }
///
/// The verbosity of the logger
@@ -147,78 +133,88 @@ public void Initialize()
return;
}
- if (parsedSettings.ContainsKey("EverQuestDirectory"))
+ if (parsedSettings.TryGetValue("EverQuestDirectory", out var setting))
{
- EverQuestDirectory = parsedSettings["EverQuestDirectory"];
+ EverQuestDirectory = setting;
// Ensure the path ends with a /
EverQuestDirectory = Path.GetFullPath(EverQuestDirectory + "/");
}
- if (parsedSettings.ContainsKey("RawS3DExtract"))
+ if (parsedSettings.TryGetValue("RawS3DExtract", out var parsedSetting))
{
- RawS3dExtract = Convert.ToBoolean(parsedSettings["RawS3DExtract"]);
+ RawS3dExtract = Convert.ToBoolean(parsedSetting);
}
- if (parsedSettings.ContainsKey("ExportZoneMeshGroups"))
+ if (parsedSettings.TryGetValue("ExportZoneMeshGroups", out var setting1))
{
- ExportZoneMeshGroups = Convert.ToBoolean(parsedSettings["ExportZoneMeshGroups"]);
+ ExportZoneMeshGroups = Convert.ToBoolean(setting1);
}
- if (parsedSettings.ContainsKey("ExportHiddenGeometry"))
+ if (parsedSettings.TryGetValue("ExportHiddenGeometry", out var parsedSetting1))
{
- ExportHiddenGeometry = Convert.ToBoolean(parsedSettings["ExportHiddenGeometry"]);
+ ExportHiddenGeometry = Convert.ToBoolean(parsedSetting1);
}
- if (parsedSettings.ContainsKey("ExportZoneWithObjects"))
+ if (parsedSettings.TryGetValue("ExportZoneWithObjects", out var setting2))
{
- ExportZoneWithObjects = Convert.ToBoolean(parsedSettings["ExportZoneWithObjects"]);
+ ExportZoneWithObjects = Convert.ToBoolean(setting2);
}
- if (parsedSettings.ContainsKey("ModelExportFormat"))
+ if (parsedSettings.TryGetValue("ModelExportFormat", out var parsedSetting2))
{
- var exportFormatSetting = (ModelExportFormat)Convert.ToInt32(parsedSettings["ModelExportFormat"]);
+ var exportFormatSetting = (ModelExportFormat)Convert.ToInt32(parsedSetting2);
ModelExportFormat = exportFormatSetting;
}
- if (parsedSettings.ContainsKey("ExportCharacterToSingleFolder"))
+ if (parsedSettings.TryGetValue("ExportCharacterToSingleFolder", out var setting3))
{
- ExportCharactersToSingleFolder = Convert.ToBoolean(parsedSettings["ExportCharacterToSingleFolder"]);
+ ExportCharactersToSingleFolder = Convert.ToBoolean(setting3);
}
- if (parsedSettings.ContainsKey("ExportEquipmentToSingleFolder"))
+ if (parsedSettings.TryGetValue("ExportEquipmentToSingleFolder", out var parsedSetting3))
{
- ExportEquipmentToSingleFolder = Convert.ToBoolean(parsedSettings["ExportEquipmentToSingleFolder"]);
+ ExportEquipmentToSingleFolder = Convert.ToBoolean(parsedSetting3);
}
-
- if (parsedSettings.ContainsKey("ExportSoundsToSingleFolder"))
+
+ if (parsedSettings.TryGetValue("ExportSoundsToSingleFolder", out var setting4))
{
- ExportSoundsToSingleFolder = Convert.ToBoolean(parsedSettings["ExportSoundsToSingleFolder"]);
+ ExportSoundsToSingleFolder = Convert.ToBoolean(setting4);
}
- if (parsedSettings.ContainsKey("ExportAllAnimationFrames"))
+ if (parsedSettings.TryGetValue("ExportAllAnimationFrames", out var parsedSetting4))
{
- ExportAllAnimationFrames = Convert.ToBoolean(parsedSettings["ExportAllAnimationFrames"]);
+ ExportAllAnimationFrames = Convert.ToBoolean(parsedSetting4);
}
- if (parsedSettings.ContainsKey("ExportGltfVertexColors"))
+ if (parsedSettings.TryGetValue("ExportGltfVertexColors", out var setting5))
{
- ExportGltfVertexColors = Convert.ToBoolean(parsedSettings["ExportGltfVertexColors"]);
+ ExportGltfVertexColors = Convert.ToBoolean(setting5);
}
- if (parsedSettings.ContainsKey("ExportGltfInGlbFormat"))
+ if (parsedSettings.TryGetValue("ExportGltfInGlbFormat", out var parsedSetting5))
{
- ExportGltfInGlbFormat = Convert.ToBoolean(parsedSettings["ExportGltfInGlbFormat"]);
+ ExportGltfInGlbFormat = Convert.ToBoolean(parsedSetting5);
}
- if (parsedSettings.ContainsKey("ClientDataToCopy"))
+ if (parsedSettings.TryGetValue("ClientDataToCopy", out var setting6))
+ {
+ ClientDataToCopy = setting6;
+ }
+
+ if (parsedSettings.TryGetValue("ClientDataToCopy", out var parsedSetting6))
+ {
+ ClientDataToCopy = parsedSetting6;
+ }
+
+ if (parsedSettings.TryGetValue("CopyMusic", out var setting7))
{
- ClientDataToCopy = parsedSettings["ClientDataToCopy"];
+ CopyMusic = Convert.ToBoolean(setting7);
}
- if (parsedSettings.ContainsKey("LoggerVerbosity"))
+ if (parsedSettings.TryGetValue("LoggerVerbosity", out var parsedSetting7))
{
- LoggerVerbosity = Convert.ToInt32(parsedSettings["LoggerVerbosity"]);
+ LoggerVerbosity = Convert.ToInt32(parsedSetting7);
}
}
}
diff --git a/LanternExtractor/runtimeconfig.template.json b/LanternExtractor/runtimeconfig.template.json
new file mode 100644
index 0000000..5c61d62
--- /dev/null
+++ b/LanternExtractor/runtimeconfig.template.json
@@ -0,0 +1,5 @@
+{
+ "configProperties": {
+ "System.Drawing.EnableUnixSupport": true
+ }
+}
diff --git a/LanternExtractor/settings.txt b/LanternExtractor/settings.txt
index 776054a..4de0147 100644
--- a/LanternExtractor/settings.txt
+++ b/LanternExtractor/settings.txt
@@ -1,13 +1,13 @@
# Lantern Extractor Settings
# EverQuest Installation Path
-# Window example: C:/EverQuest
+# Windows example: C:/EverQuest
# MacOS example: /Users/your.name/EQ/
EverQuestDirectory = C:/EverQuest
# Extract the contents of the S3D archive as is?
-# If true, this will export all contents as they exist in the archives to their original formats.
-# If false, this will extract contents, process WLD files and convert images to .png
+# If true, this will export all contents as they exist in the archives in their original formats.
+# If false, this will extract contents, process WLD files and convert images to PNG.
RawS3DExtract = false
# Sets the desired export format for models
@@ -37,7 +37,7 @@ ExportCharacterToSingleFolder = true
ExportEquipmentToSingleFolder = true
# If true, will export all sounds to the 'sounds' folder
-# Note, some sounds with conflicting names will be overwritten
+# Some sounds with conflicting names will be overwritten
ExportSoundsToSingleFolder = true
# If true and using OBJ export format, this will dump an OBJ for each animation frame.
@@ -69,6 +69,10 @@ ExportGltfInGlbFormat = false
# (Intermediate)
ClientDataToCopy = spells.eff,spdat.eff
+# If enabled, client XMI files will be copied to the 'Exports/Music' folder
+# XMI files can be used in the Lantern client
+CopyMusic = true
+
# The minimum verbosity of the logger
# 0 = info, 1 = warnings, 2 = errors
LoggerVerbosity = 2