diff --git a/Documentation/images/editors/epicumx.jpg b/Documentation/images/editors/epicumx.jpg
new file mode 100644
index 00000000..c8f1ea8a
--- /dev/null
+++ b/Documentation/images/editors/epicumx.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:91a288801c3eaab1fcfcc55f65c73303bf72d7d5303bd9456141141063cf0ed8
+size 31943
diff --git a/Documentation/images/editors/epicumx_t.png b/Documentation/images/editors/epicumx_t.png
new file mode 100644
index 00000000..28c4c6f2
--- /dev/null
+++ b/Documentation/images/editors/epicumx_t.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d1065ae642b5d5043520fcee9043d1e124cf1fca76c23e7a12300974a75b43a7
+size 8610
diff --git a/Documentation/images/editors/unreal.png b/Documentation/images/editors/unreal.png
deleted file mode 100644
index b29b7e0b..00000000
--- a/Documentation/images/editors/unreal.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:56a56a2b261bd5cfc0a8d17373b3f2ce7c5d702f9b6f14b8439bd2211c1b4b6b
-size 7356
diff --git a/Documentation/images/editors/unreal_t.png b/Documentation/images/editors/unreal_t.png
deleted file mode 100644
index c8cd6300..00000000
--- a/Documentation/images/editors/unreal_t.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:5ca5858bcef7bd48f7d801f44af64395f23b59ef097708d6be86ddf6d7803aa1
-size 5709
diff --git a/Documentation/moduletypes.html b/Documentation/moduletypes.html
index c46e6b5f..ea5f692b 100644
--- a/Documentation/moduletypes.html
+++ b/Documentation/moduletypes.html
@@ -483,6 +483,22 @@
+
diff --git a/README.md b/README.md
index 21ad4138..d64a33a9 100644
--- a/README.md
+++ b/README.md
@@ -66,6 +66,7 @@ Modules in all supported formats can be found on my homepage at https://nostalgi
| D.O.C. SoundTracker II | .mod | | ModTracker |
| D.O.C. SoundTracker VI | .mod | | ModTracker |
| D.O.C. SoundTracker IX | .mod | | ModTracker |
+| Epic Games UMX | .umx | Module Converter | ModTracker / Mpg123 / Xmp |
| Eureka Packer | .eureka / .eu | ProWizard | ModTracker |
| Farandole Composer | .far | | Xmp |
| Fast/TakeTracker | .mod | | ModTracker |
@@ -184,7 +185,6 @@ Modules in all supported formats can be found on my homepage at https://nostalgi
| Unic Tracker | .unic | ProWizard | ModTracker |
| UniMod | .uni | MikMod Converter | MikMod |
| Unis 669 | .669 | | Xmp |
-| Unreal Music File | .umx | MikMod Converter | MikMod |
| Wanton Packer | .wnp | ProWizard | ModTracker |
| Xann Packer | .xann | ProWizard | ModTracker |
| Zen Packer | .zen | ProWizard | ModTracker |
diff --git a/Source/Agents/ModuleConverters/MikModConverter/MikModConverter.cs b/Source/Agents/ModuleConverters/MikModConverter/MikModConverter.cs
index d9b6792c..1363d3bf 100644
--- a/Source/Agents/ModuleConverters/MikModConverter/MikModConverter.cs
+++ b/Source/Agents/ModuleConverters/MikModConverter/MikModConverter.cs
@@ -81,7 +81,7 @@ public override AgentSupportInfo[] AgentInformation
// new AgentSupportInfo(Resources.IDS_MIKCONV_NAME_AGENT12, Resources.IDS_MIKCONV_DESCRIPTION_AGENT12, agent12Id),
// new AgentSupportInfo(Resources.IDS_MIKCONV_NAME_AGENT13, Resources.IDS_MIKCONV_DESCRIPTION_AGENT13, agent13Id),
new AgentSupportInfo(Resources.IDS_MIKCONV_NAME_AGENT14, Resources.IDS_MIKCONV_DESCRIPTION_AGENT14, agent14Id),
- new AgentSupportInfo(Resources.IDS_MIKCONV_NAME_AGENT15, Resources.IDS_MIKCONV_DESCRIPTION_AGENT15, agent15Id),
+// new AgentSupportInfo(Resources.IDS_MIKCONV_NAME_AGENT15, Resources.IDS_MIKCONV_DESCRIPTION_AGENT15, agent15Id),
// new AgentSupportInfo(Resources.IDS_MIKCONV_NAME_AGENT16, Resources.IDS_MIKCONV_DESCRIPTION_AGENT16, agent16Id)
};
}
diff --git a/Source/Agents/ModuleConverters/ModuleConverter/Formats/UmxFormat.cs b/Source/Agents/ModuleConverters/ModuleConverter/Formats/UmxFormat.cs
new file mode 100644
index 00000000..323afca7
--- /dev/null
+++ b/Source/Agents/ModuleConverters/ModuleConverter/Formats/UmxFormat.cs
@@ -0,0 +1,492 @@
+/******************************************************************************/
+/* This source, or parts thereof, may be used in any software as long the */
+/* license of NostalgicPlayer is keep. See the LICENSE file for more */
+/* information. */
+/******************************************************************************/
+using System;
+using System.IO;
+using System.Text;
+using Polycode.NostalgicPlayer.Kit;
+using Polycode.NostalgicPlayer.Kit.Bases;
+using Polycode.NostalgicPlayer.Kit.Containers;
+using Polycode.NostalgicPlayer.Kit.Streams;
+using Polycode.NostalgicPlayer.Kit.Utility;
+
+namespace Polycode.NostalgicPlayer.Agent.ModuleConverter.ModuleConverter.Formats
+{
+ ///
+ /// Can convert Epic Games UMX container format and extract the module inside
+ ///
+ internal class UmxFormat : ModuleConverterAgentBase
+ {
+ #pragma warning disable 649
+ #region GenHist struct
+ ///
+ /// For UPkg versions >= 68
+ ///
+ private struct GenHist
+ {
+ public int ExportCount;
+ public int NameCount;
+ }
+ #endregion
+
+ #region UPkgHdr class
+ private class UPkgHdr
+ {
+ public uint Tag; // UPkgHdrTag
+ public int FileVersion;
+ public uint PkgFlags;
+ public int NameCount; // Number of names in name table (>= 0)
+ public int NameOffset; // Offset to name table (>= 0)
+ public int ExportCount; // Num. exports in export table (>= 0)
+ public int ExportOffset; // Offset to export table (>= 0)
+ public int ImportCount; // Num. imports in export table (>= 0)
+ public int ImportOffset; // Offset to import table (>= 0)
+
+ // Number of GUIDs in heritage table (>= 1) and table's offset:
+ // only with versions < 68
+ public int HeritageCount;
+ public int HeritageOffset;
+
+ // With versions >= 68: a GUID, a dword for generation count
+ // and export_count and name_count dwords for each generation
+ public readonly uint[] Guid = new uint[4];
+ public int GenerationCount;
+
+ public GenHist[] Gen;
+ }
+ #endregion
+
+ #region UmxInfo class
+ private class UmxInfo
+ {
+ public UMusic Type;
+ public int Ofs;
+ public int Size;
+ }
+ #endregion
+ #pragma warning restore 649
+
+ private enum UMusic
+ {
+ It = 0,
+ S3M,
+ Xm,
+ Mod,
+ Wav,
+ Mp2
+ }
+
+ private static readonly string[] musType =
+ {
+ "IT", "S3M", "XM", "MOD",
+ "WAV", "MP2", null
+ };
+
+ private const uint UPkgHdrTag = 0x9e2a83c1;
+ private const int UPkgHdrSize = 64;
+
+ private UmxInfo umxData;
+
+ #region IModuleConverterAgent implementation
+ /********************************************************************/
+ ///
+ /// Test the file to see if it could be identified
+ ///
+ /********************************************************************/
+ public override AgentResult Identify(PlayerFileInfo fileInfo)
+ {
+ ModuleStream moduleStream = fileInfo.ModuleStream;
+
+ // First check the length
+ long fileSize = moduleStream.Length;
+ if (fileSize < UPkgHdrSize) // Size of header
+ return AgentResult.Unknown;
+
+ // Parse the structure and find the type of the module
+ moduleStream.Seek(0, SeekOrigin.Begin);
+
+ int type = ProcessUPkg(moduleStream, out int ofs, out int size);
+ if (type < 0)
+ return AgentResult.Unknown;
+
+ umxData = new UmxInfo();
+
+ umxData.Type = (UMusic)type;
+ umxData.Ofs = ofs;
+ umxData.Size = size;
+
+ return AgentResult.Ok;
+ }
+
+
+
+ /********************************************************************/
+ ///
+ /// Convert the module and store the result in the stream given
+ ///
+ /********************************************************************/
+ public override AgentResult Convert(PlayerFileInfo fileInfo, ConverterStream converterStream, out string errorMessage)
+ {
+ ModuleStream moduleStream = fileInfo.ModuleStream;
+
+ errorMessage = string.Empty;
+
+ // Try to read the module
+ moduleStream.Seek(umxData.Ofs, SeekOrigin.Begin);
+
+ // We just copy the whole module into the output stream
+ Helpers.CopyData(moduleStream, converterStream, umxData.Size);
+
+ return AgentResult.Ok;
+ }
+ #endregion
+
+ #region Private methods
+ /********************************************************************/
+ ///
+ /// Decode an FCompactIndex.
+ ///
+ /// Original documentation by Tim Sweeney was at
+ /// http://unreal.epicgames.com/Packages.htm
+ ///
+ /// Also see Unreal Wiki:
+ /// http://wiki.beyondunreal.com/Legacy:Package_File_Format/Data_Details
+ ///
+ /********************************************************************/
+ private int GetFci(Span inBuf, ref int pos)
+ {
+ int size = 1;
+ int a = inBuf[0] & 0x3f;
+
+ if ((inBuf[0] & 0x40) != 0)
+ {
+ size++;
+ a |= (inBuf[1] & 0x7f) << 6;
+
+ if ((inBuf[1] & 0x80) != 0)
+ {
+ size++;
+ a |= (inBuf[2] & 0x7f) << 13;
+
+ if ((inBuf[2] & 0x80) != 0)
+ {
+ size++;
+ a |= (inBuf[3] & 0x7f) << 20;
+
+ if ((inBuf[3] & 0x80) != 0)
+ {
+ size++;
+ a |= (inBuf[4] & 0x3f) << 27;
+ }
+ }
+ }
+ }
+
+ if ((inBuf[0] & 0x80) != 0)
+ a = -a;
+
+ pos += size;
+
+ return a;
+ }
+
+
+
+ /********************************************************************/
+ ///
+ /// Return the object type
+ ///
+ /********************************************************************/
+ private int GetObjType(ModuleStream moduleStream, int ofs, UMusic type)
+ {
+ byte[] sig = new byte[16];
+
+ retry:
+ Array.Clear(sig);
+
+ moduleStream.Seek(ofs, SeekOrigin.Begin);
+ moduleStream.Read(sig, 0, 16);
+
+ if (type == UMusic.It)
+ {
+ if (Encoding.ASCII.GetString(sig, 0, 4) == "IMPM")
+ return (int)UMusic.It;
+
+ return -1;
+ }
+
+ if (type == UMusic.Xm)
+ {
+ if (Encoding.ASCII.GetString(sig, 0, 16) != "Extended Module:")
+ return -1;
+
+ moduleStream.Read(sig, 0, 16);
+ if (sig[0] != ' ')
+ return -1;
+
+ moduleStream.Read(sig, 0, 16);
+ if (sig[5] != 0x1a)
+ return -1;
+
+ return (int)UMusic.Xm;
+ }
+
+ if (type == UMusic.Mp2)
+ {
+ ushort u = (ushort)(((sig[0] << 8) | sig[1]) & 0xfffe);
+ if ((u == 0xfffc) || (u == 0xfff4))
+ return (int)UMusic.Mp2;
+
+ return -1;
+ }
+
+ if (type == UMusic.Wav)
+ {
+ if ((Encoding.ASCII.GetString(sig, 0, 4) == "RIFF") && (Encoding.ASCII.GetString(sig, 8, 4) == "WAVE"))
+ return (int)UMusic.Wav;
+
+ return -1;
+ }
+
+ moduleStream.Seek(ofs + 44, SeekOrigin.Begin);
+ moduleStream.Read(sig, 0, 4);
+
+ if (type == UMusic.S3M)
+ {
+ if (Encoding.ASCII.GetString(sig, 0, 4) == "SCRM")
+ return (int)UMusic.S3M;
+
+ // SpaceMarines.umx and Starseek.umx from Return to NaPali
+ // reports as "s3m" whereas the actual music format is "it"
+ type = UMusic.It;
+ goto retry;
+ }
+
+ moduleStream.Seek(ofs + 1080, SeekOrigin.Begin);
+ moduleStream.Read(sig, 0, 4);
+
+ if (type == UMusic.Mod)
+ {
+ if ((Encoding.ASCII.GetString(sig, 0, 4) == "M.K.") || (Encoding.ASCII.GetString(sig, 0, 4) == "M!K!"))
+ return (int)UMusic.Mod;
+
+ return -1;
+ }
+
+ return -1;
+ }
+
+
+
+ /********************************************************************/
+ ///
+ /// Read export table
+ ///
+ /********************************************************************/
+ private int ReadExport(ModuleStream moduleStream, UPkgHdr hdr, ref int ofs, ref int objSize)
+ {
+ byte[] buf = new byte[40];
+
+ moduleStream.Seek(ofs, SeekOrigin.Begin);
+ if (moduleStream.Read(buf, 0, 40) < 40)
+ return -1;
+
+ int idx = 0;
+
+ if (hdr.FileVersion < 40)
+ idx += 8; // 00 00 00 00 00 00 00 00
+
+ if (hdr.FileVersion < 60)
+ idx += 16; // 81 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 00
+
+ GetFci(buf.AsSpan(idx), ref idx); // Skip junk
+ int t = GetFci(buf.AsSpan(idx), ref idx); // Type name
+
+ if (hdr.FileVersion > 61)
+ idx += 4; // Skip export size
+
+ objSize = GetFci(buf.AsSpan(idx), ref idx);
+ ofs += idx; // Offset for real data
+
+ return t; // Return type name index
+ }
+
+
+
+ /********************************************************************/
+ ///
+ /// Read type name
+ ///
+ /********************************************************************/
+ private int ReadTypeName(ModuleStream moduleStream, UPkgHdr hdr, int idx, byte[] outBuf)
+ {
+ if (idx >= hdr.NameCount)
+ return -1;
+
+ byte[] buf = new byte[64];
+
+ long l = 0;
+ for (int i = 0; i <= idx; i++)
+ {
+ moduleStream.Seek(hdr.NameOffset + l, SeekOrigin.Begin);
+ if (moduleStream.Read(buf, 0, 63) == 0)
+ return -1;
+
+ if (hdr.FileVersion >= 64)
+ {
+ sbyte s = (sbyte)buf[0]; // numchars *including* terminator
+ if (s <= 0)
+ return -1;
+
+ l += s + 5; // 1 for buf[0], 4 for int32 name_flags
+ }
+ else
+ {
+ l += EncoderCollection.Dos.GetCharCount(buf);
+ l += 5; // 1 for terminator, 4 for int32 name_flags
+ }
+ }
+
+ int off = hdr.FileVersion >= 64 ? 1 : 0;
+ Array.Copy(buf, off, outBuf, 0, 64 - off);
+
+ return 0;
+ }
+
+
+
+ /********************************************************************/
+ ///
+ /// Probe container
+ ///
+ /********************************************************************/
+ private int ProbeUmx(ModuleStream moduleStream, UPkgHdr hdr, out int ofs, out int objSize)
+ {
+ int idx = 0;
+ long fSiz = moduleStream.Length;
+
+ ofs = -1;
+ objSize = -1;
+
+ if ((hdr.NameOffset >= fSiz) || (hdr.ExportOffset >= fSiz) || (hdr.ImportOffset >= fSiz))
+ return -1;
+
+ // Find the offset and size of the first IT, S3M or XM
+ // by parsing the exports table. The umx files should
+ // have only one export. Kran32.umx from Unreal has two,
+ // but both pointing to the same music
+ if (hdr.ExportOffset >= fSiz)
+ return -1;
+
+ byte[] buf = new byte[64];
+
+ moduleStream.Seek(hdr.ExportOffset, SeekOrigin.Begin);
+ moduleStream.Read(buf, 0, 64);
+
+ GetFci(buf.AsSpan(idx), ref idx); // Skip class index
+ GetFci(buf.AsSpan(idx), ref idx); // Skip super index
+
+ if (hdr.FileVersion >= 60)
+ idx += 4; // Skip int32 package index
+
+ GetFci(buf.AsSpan(idx), ref idx); // Skip object name
+ idx += 4; // Skip int32 object flags
+
+ int s = GetFci(buf.AsSpan(idx), ref idx); // Get serial size
+ if (s <= 0)
+ return -1;
+
+ int pos = GetFci(buf.AsSpan(idx), ref idx);// Get serial offset
+ if ((pos < 0) || (pos > (fSiz - 40)))
+ return -1;
+
+ int t = ReadExport(moduleStream, hdr, ref pos, ref s);
+ if (t < 0)
+ return -1;
+
+ if ((s <= 0) || (s > (fSiz - pos)))
+ return -1;
+
+ if (ReadTypeName(moduleStream, hdr, t, buf) < 0)
+ return -1;
+
+ int i;
+ for (i = 0; musType[i] != null; i++)
+ {
+ if (EncoderCollection.Dos.GetString(buf).ToUpper() == musType[i])
+ {
+ t = i;
+ break;
+ }
+ }
+
+ if (musType[i] == null)
+ return -1;
+
+ t = GetObjType(moduleStream, pos, (UMusic)t);
+ if (t < 0)
+ return -1;
+
+ ofs = pos;
+ objSize = s;
+
+ return t;
+ }
+
+
+
+ /********************************************************************/
+ ///
+ /// Probe header
+ ///
+ /********************************************************************/
+ private int ProbeHeader(ModuleStream moduleStream, UPkgHdr hdr)
+ {
+ hdr.Tag = moduleStream.Read_L_UINT32();
+ hdr.FileVersion = (int)moduleStream.Read_L_UINT32();
+ hdr.PkgFlags = moduleStream.Read_L_UINT32();
+ hdr.NameCount = (int)moduleStream.Read_L_UINT32();
+ hdr.NameOffset = (int)moduleStream.Read_L_UINT32();
+ hdr.ExportCount = (int)moduleStream.Read_L_UINT32();
+ hdr.ExportOffset = (int)moduleStream.Read_L_UINT32();
+ hdr.ImportCount = (int)moduleStream.Read_L_UINT32();
+ hdr.ImportOffset = (int)moduleStream.Read_L_UINT32();
+
+ if (moduleStream.EndOfStream)
+ return -1;
+
+ if (hdr.Tag != UPkgHdrTag)
+ return -1;
+
+ if ((hdr.NameCount < 0) || (hdr.ExportCount < 0) || (hdr.ImportCount < 0) || (hdr.NameOffset < 36) || (hdr.ExportOffset < 36) || (hdr.ImportOffset < 36))
+ return -1;
+
+ return 0;
+ }
+
+
+
+ /********************************************************************/
+ ///
+ /// Process the package
+ ///
+ /********************************************************************/
+ private int ProcessUPkg(ModuleStream moduleStream, out int ofs, out int objSize)
+ {
+ UPkgHdr header = new UPkgHdr();
+
+ if (ProbeHeader(moduleStream, header) < 0)
+ {
+ ofs = -1;
+ objSize = -1;
+
+ return -1;
+ }
+
+ return ProbeUmx(moduleStream, header, out ofs, out objSize);
+ }
+ #endregion
+ }
+}
diff --git a/Source/Agents/ModuleConverters/ModuleConverter/ModuleConverter.cs b/Source/Agents/ModuleConverters/ModuleConverter/ModuleConverter.cs
index d06cadcb..85dfef44 100644
--- a/Source/Agents/ModuleConverters/ModuleConverter/ModuleConverter.cs
+++ b/Source/Agents/ModuleConverters/ModuleConverter/ModuleConverter.cs
@@ -24,6 +24,7 @@ public class ModuleConverter : AgentBase
private static readonly Guid agent2Id = Guid.Parse("0C8D0CEE-EA9D-4132-95ED-DFE72D5D8FB6");
private static readonly Guid agent3Id = Guid.Parse("44F2292A-BBA1-42C7-B9EC-1ACED9A42BF8");
private static readonly Guid agent4Id = Guid.Parse("CED4726A-EF3B-40C6-8F93-865F0CE5321E");
+ private static readonly Guid agent5Id = Guid.Parse("E03F718C-8FA9-4843-9EBE-6D69EC3A421D");
#region IAgent implementation
/********************************************************************/
@@ -58,7 +59,8 @@ public override AgentSupportInfo[] AgentInformation
new AgentSupportInfo(Resources.IDS_MODCONV_NAME_AGENT1, Resources.IDS_MODCONV_DESCRIPTION_AGENT1, agent1Id),
new AgentSupportInfo(Resources.IDS_MODCONV_NAME_AGENT2, Resources.IDS_MODCONV_DESCRIPTION_AGENT2, agent2Id),
new AgentSupportInfo(Resources.IDS_MODCONV_NAME_AGENT3, Resources.IDS_MODCONV_DESCRIPTION_AGENT3, agent3Id),
- new AgentSupportInfo(Resources.IDS_MODCONV_NAME_AGENT4, Resources.IDS_MODCONV_DESCRIPTION_AGENT4, agent4Id)
+ new AgentSupportInfo(Resources.IDS_MODCONV_NAME_AGENT4, Resources.IDS_MODCONV_DESCRIPTION_AGENT4, agent4Id),
+ new AgentSupportInfo(Resources.IDS_MODCONV_NAME_AGENT5, Resources.IDS_MODCONV_DESCRIPTION_AGENT5, agent5Id)
};
}
}
@@ -84,6 +86,9 @@ public override IAgentWorker CreateInstance(Guid typeId)
if (typeId == agent4Id)
return new Med4Format();
+ if (typeId == agent5Id)
+ return new UmxFormat();
+
return null;
}
#endregion
diff --git a/Source/Agents/ModuleConverters/ModuleConverter/ModuleConverter.csproj b/Source/Agents/ModuleConverters/ModuleConverter/ModuleConverter.csproj
index 39d22443..d42fa151 100644
--- a/Source/Agents/ModuleConverters/ModuleConverter/ModuleConverter.csproj
+++ b/Source/Agents/ModuleConverters/ModuleConverter/ModuleConverter.csproj
@@ -7,7 +7,7 @@
Polycode
NostalgicPlayer
Can convert different module formats to other formats where a player is available
- 2.1.0
+ 2.2.0
diff --git a/Source/Agents/ModuleConverters/ModuleConverter/Resources.Designer.cs b/Source/Agents/ModuleConverters/ModuleConverter/Resources.Designer.cs
index 0a3aa9d8..45b5f070 100644
--- a/Source/Agents/ModuleConverters/ModuleConverter/Resources.Designer.cs
+++ b/Source/Agents/ModuleConverters/ModuleConverter/Resources.Designer.cs
@@ -130,10 +130,11 @@ internal static string IDS_ERR_LOADING_TRACKS {
///
///Current version can convert these formats:
///
- ///Fred Editor (Final) -> Fred Editor
- ///Future Composer 1.0 - 1.3 -> Future Composer 1.4
- ///SoundFX 1.x > SoundFX 2.0
- ///MED 2.10 (MED4) -> MED 2.10 (MMD0).
+ ///Epic Games UMX ➜ Whatever format inside
+ ///Fred Editor (Final) ➜ Fred Editor
+ ///Future Composer 1.0 - 1.3 ➜ Future Composer 1.4
+ ///MED 2.10 (MED4) ➜ MED 2.10 (MMD0)
+ ///SoundFX 1.x ➜ SoundFX 2.0.
///
internal static string IDS_MODCONV_DESCRIPTION {
get {
@@ -191,6 +192,18 @@ internal static string IDS_MODCONV_DESCRIPTION_AGENT4 {
}
}
+ ///
+ /// Looks up a localized string similar to This converter is based on the code for LibXmp.
+ ///Converted to C# by Thomas Neumann.
+ ///
+ ///This converter recognizes the modules in “umx” files from games like “Unreal”, “DeusEx”, etc. To NostalgicPlayer, UMX is just a container and the real music format may be one of “ScreamTracker 3”, “Impulse Tracker”, “FastTracker 2”, or possibly a “ProTracker” compatible one..
+ ///
+ internal static string IDS_MODCONV_DESCRIPTION_AGENT5 {
+ get {
+ return ResourceManager.GetString("IDS_MODCONV_DESCRIPTION_AGENT5", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Module Converter.
///
@@ -235,5 +248,14 @@ internal static string IDS_MODCONV_NAME_AGENT4 {
return ResourceManager.GetString("IDS_MODCONV_NAME_AGENT4", resourceCulture);
}
}
+
+ ///
+ /// Looks up a localized string similar to Epic Games UMX.
+ ///
+ internal static string IDS_MODCONV_NAME_AGENT5 {
+ get {
+ return ResourceManager.GetString("IDS_MODCONV_NAME_AGENT5", resourceCulture);
+ }
+ }
}
}
diff --git a/Source/Agents/ModuleConverters/ModuleConverter/Resources.resx b/Source/Agents/ModuleConverters/ModuleConverter/Resources.resx
index 1b1d2508..07b79def 100644
--- a/Source/Agents/ModuleConverters/ModuleConverter/Resources.resx
+++ b/Source/Agents/ModuleConverters/ModuleConverter/Resources.resx
@@ -145,10 +145,11 @@ Converts different module formats (mostly Amiga formats) to another format Nosta
Current version can convert these formats:
-Fred Editor (Final) -> Fred Editor
-Future Composer 1.0 - 1.3 -> Future Composer 1.4
-SoundFX 1.x > SoundFX 2.0
-MED 2.10 (MED4) -> MED 2.10 (MMD0)
+Epic Games UMX ➜ Whatever format inside
+Fred Editor (Final) ➜ Fred Editor
+Future Composer 1.0 - 1.3 ➜ Future Composer 1.4
+MED 2.10 (MED4) ➜ MED 2.10 (MMD0)
+SoundFX 1.x ➜ SoundFX 2.0
Original player by SuperSero.
@@ -175,6 +176,12 @@ The modules contain the player in 68000 assembler in the beginning of the files,
Converted to C# by Thomas Neumann.
This player plays modules created with MED v2.10 to MED v3.22. This format have both a real module format, where song data and samples are combined into a single file and song+sample format. The player can load both types of files. For song+sample format, you need to have the samples beside the song files. The player will load the samples from a folder named "Instruments", synth sounds from a folder named "Synthsounds" and hybrid samples in a folder named "Hybrids". All folders should be placed in the same folder as the song files.
+
+
+ This converter is based on the code for LibXmp.
+Converted to C# by Thomas Neumann.
+
+This converter recognizes the modules in “umx” files from games like “Unreal”, “DeusEx”, etc. To NostalgicPlayer, UMX is just a container and the real music format may be one of “ScreamTracker 3”, “Impulse Tracker”, “FastTracker 2”, or possibly a “ProTracker” compatible one.
Module Converter
@@ -191,4 +198,7 @@ This player plays modules created with MED v2.10 to MED v3.22. This format have
MED 2.10 (MED4)
+
+ Epic Games UMX
+
\ No newline at end of file
diff --git a/Source/Ports/LibXmp/Player.cs b/Source/Ports/LibXmp/Player.cs
index 8b8ecb1e..3b504c9b 100644
--- a/Source/Ports/LibXmp/Player.cs
+++ b/Source/Ports/LibXmp/Player.cs
@@ -574,7 +574,10 @@ public void Xmp_Get_Frame_Info(out Xmp_Frame_Info info)
}
}
- Array.Copy(mod.Xxp[info.Pattern].Index, info.Playing_Tracks, chn);
+ if (info.Pattern < mod.Pat)
+ Array.Copy(mod.Xxp[info.Pattern].Index, info.Playing_Tracks, chn);
+ else
+ Array.Clear(info.Playing_Tracks, 0, chn);
}
#region Private methods
diff --git a/Source/Ports/README.md b/Source/Ports/README.md
index 0ab5a1dc..52443372 100644
--- a/Source/Ports/README.md
+++ b/Source/Ports/README.md
@@ -1,4 +1,4 @@
-# Clients
+# Ports
In this directory, all 3rd party ports I have made are stored. They can have different licenses, so look after the LICENSE file inside each directory.
-Note that the port may not be a 100% port, because I have only added the stuff that I need in my player. Therefore you will see some API functions can be missing.
+Note that the port may not be a 100% port, because I have only added the stuff that I need in my player. Therefore, you will see some API functions may be missing.
|