Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change to dealing with MXML files #630

Merged
merged 10 commits into from
Jan 29, 2025
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/-bug-report-.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ _When you're done, then delete any instructions, example text and irrelevant sec
_Please select the project on the sidebar where you encountered the problem._

**Things to check before you raise an issue**
- Make sure that you have looked at the log file and determined that the issue is in fact an issue with MBINCompiler, and not an issue with the exml file being modified incorrect etc.
- Make sure that you have looked at the log file and determined that the issue is in fact an issue with MBINCompiler, and not an issue with the mxml file being modified incorrect etc.
- Check to see that an issue has not already been raised of the same manner.
- If there has been a recent update, check to see for any issues marked `[UPDATE]` to see if the struct you are having issues with is in fact supported yet or not. If not ticked off, please do not post an issue.
**IMPORTANT**: If there was a recent release and there is no issue with the update info, please do not raise an issue about single broken files. It may take some time for this issue to be created, so please be patient. Opening multiple issues will not help and they will just be closed.
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ jobs:
run: |
sed -i '\|<TargetFrameworks>net6.0;net7.0</TargetFrameworks>|a\ <RuntimeFrameworkVersion>${{ matrix.dotnet.version }}</RuntimeFrameworkVersion>' ./MBINCompiler/MBINCompiler.csproj
sed -i '\|<TargetFrameworks>net6.0;net7.0</TargetFrameworks>|a\ <RuntimeFrameworkVersion>${{ matrix.dotnet.version }}</RuntimeFrameworkVersion>' ./libMBIN-DLL/libMBIN-DLL.csproj
dotnet publish libMBIN-DLL --no-self-contained -c Release -f ${{ matrix.dotnet.framework }} -r ${{ matrix.os.runtime }} /nowarn:cs0618
dotnet publish MBINCompiler --no-self-contained -c Release -f ${{ matrix.dotnet.framework }} -r ${{ matrix.os.runtime }} /nowarn:cs0618
dotnet publish libMBIN-DLL --no-self-contained -c Release -f ${{ matrix.dotnet.framework }} -r ${{ matrix.os.runtime }} /nowarn:cs0618 /nowarn:cs0169 /nowarn:cs0414
dotnet publish MBINCompiler --no-self-contained -c Release -f ${{ matrix.dotnet.framework }} -r ${{ matrix.os.runtime }} /nowarn:cs0618 /nowarn:cs0169 /nowarn:cs0414
- name: Move the exe so the tests can find it easier
run: |
cp Build/Release/${{ matrix.dotnet.framework }}/${{ matrix.os.runtime }}/publish/MBINCompiler.exe MBINCompiler.exe
Expand Down Expand Up @@ -80,7 +80,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Build SaveFileMapping binary
run: dotnet publish SaveFileMapping -c Release -f net6.0 -r win-x64 -o Build/Release/net6/ /nowarn:cs0618
run: dotnet publish SaveFileMapping -c Release -f net6.0 -r win-x64 -o Build/Release/net6/ /nowarn:cs0618 /nowarn:cs0169 /nowarn:cs0414
- name: Generate save data mapping
run : Build/Release/net6/SaveFileMapping.exe
shell: bash
Expand Down
17 changes: 9 additions & 8 deletions MBINCompiler/Source/CommandLineOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,34 +117,35 @@ internal set {
"Do not pause for errors.\n" +
"(Any errors will be written to MBINCompiler.log)" },

new Option { longName = "no-version", description = "Hide version info in EXML header." },
new Option { longName = "no-version", description = "Hide version info in MXML header." },

new Option { shortName = 'd', longName = "output-dir", param = "<Directory>",
description = "\nSpecify the directory where files will be written to.\n" +
"If this option is used, only one input <Path> can be specified." },

new Option { shortName = 'i', longName = "input-format", param = "<Type>",
description = "\nSpecify the type of input files to be converted from.\n" +
"<Type> can be either MBIN or EXML." },
"<Type> can be either MBIN or MXML." },

new Option { shortName = 'o', longName = "output-format", param = "<Type>",
description = "\nSpecify the type of output files to be converted to.\n" +
"<Type> can be either MBIN or EXML." },
"<Type> can be either MBIN, MXML or EXML.\n" +
"Note that MBINCompiler will not accept EXML files as input." },

new Option { longName = "include", param = "<Glob Pattern>[;<Glob Pattern>...]",
description = "\nFilter all files to include only those that match the " +
"glob patterns. A glob pattern is a filepath with wildcards." +
"The * and ? wildcard characters can be used.\n" +
"Multiple glob patterns are separated by a semicolon.\n" +
"The default is --include=\"*.MBIN;*.MBIN.PC;*.EXML\" (all).\n" +
"The default is --include=\"*.MBIN;*.MBIN.PC;*.MXML\" (all).\n" +
"The --include filter is applied before --exclude." },

new Option { longName = "exclude", param = "<Glob Pattern>[;<Glob Pattern>...]",
description = "\nFilter all files to exclude any that match the " +
"glob patterns. A glob pattern is a filepath with wildcards." +
"The * and ? wildcard characters can be used.\n" +
"Multiple glob patterns are separated by a semicolon.\n" +
"The default is --exclude=\"LANGUAGE\\*;*.GEOMETRY.*\".\n" +
"The default is --exclude=\"\" (nothing).\n" +
"The --exclude filter is applied after --include." },

new Option { shortName = 'V', longName = "format-version", param = "[0|1|2]", isHidden = true,
Expand All @@ -153,7 +154,7 @@ internal set {

new Option { longName = "no-threads", isHidden = true, description = "Disable multi-threading." },

new Option { longName = "stream", description = "Enable sending EXML to Console." },
new Option { longName = "stream", description = "Enable sending MXML to Console." },
};

public static readonly List<Option> OPTIONS_LIST = new List<Option> {
Expand Down Expand Up @@ -190,7 +191,7 @@ public static string GetHelpInfo()
sb.Append( "\n\nModes:\n\n" +
FormatWrapped( " help", 20, "Show this help info.", true ) +
FormatWrapped( " version", 20, "Show version info.", true ) +
FormatWrapped( " convert", 20, "Convert files between MBIN and EXML formats.", true ) +
FormatWrapped( " convert", 20, "Convert files between MBIN and MXML formats.", true ) +
FormatWrapped( " register", 20, "Add MBINCompiler to your systems PATH variable.", true) );

if ( OPTIONS_GENERAL.Count > 0 ) {
Expand All @@ -211,7 +212,7 @@ public static string GetHelpInfo()

sb.Append( FormatWrapped( "\n\n[convert] [<Option>...] <Path> [<Path>...]\n\n", 4,
" This mode is the default. The convert keyword is optional.\n" +
" For each <Path>, convert all files between MBIN and EXML formats." ) );
" For each <Path>, convert all files between MBIN and MXML formats." ) );

if ( OPTIONS_CONVERT.Count > 0 ) {
sb.Append( "\nconvert Options:\n" );
Expand Down
30 changes: 15 additions & 15 deletions MBINCompiler/Source/Commands/Convert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ public static void ConvertFile( string inputPath, string fileIn, string fileOut,

if ( inputFormat == FormatType.MBIN ) {
fileOut = ConvertMBIN( inputPath, fIn, ms, fileOut );
} else if ( inputFormat == FormatType.EXML ) {
fileOut = ConvertEXML( inputPath, fIn, ms, fileOut );
} else if ( inputFormat == FormatType.MXML ) {
fileOut = ConvertMXML( inputPath, fIn, ms, fileOut );
}
if (!(StreamToConsole && inputFormat == FormatType.MBIN)) {
ms.Flush();
Expand All @@ -179,7 +179,7 @@ public static void ConvertFile( string inputPath, string fileIn, string fileOut,

}

/// <summary>Convert MBIN to EXML</summary>
/// <summary>Convert MBIN to MXML</summary>
/// <param name="fIn">Source file</param>
/// <param name="msOut">Output stream</param>
/// <param name="fileOut">Output file path. Passed through as the return value. Not actually used.</param>
Expand Down Expand Up @@ -217,18 +217,18 @@ private static string ConvertMBIN( string inputPath, FileStream fIn, MemoryStrea
data = mbin.GetData();
if ( data is null ) throw new InvalidDataException( "Invalid MBIN data." );

msg = $"Failed serializing {mbin.Header.GetXMLTemplateName()} to EXML.";
string exml = EXmlFile.WriteTemplate(data, HideVersionInfo);
msg = $"Failed serializing {mbin.Header.GetXMLTemplateName()} to MXML.";
string mxml = MXmlFile.WriteTemplate(data, HideVersionInfo);

if ( StreamToConsole ) {
EmitInfo($"");
EmitInfo($"[INPUT]: {inputPath}");
EmitInfo($"{exml}");
EmitInfo($"{mxml}");
}
else {
sw.Write(exml);
sw.Write(mxml);
sw.Flush();
if ( msOut.Length == 0 ) throw new InvalidDataException( "Invalid EXML data." );
if ( msOut.Length == 0 ) throw new InvalidDataException( "Invalid MXML data." );
}
} catch ( Exception e ) {
throw new MbinException( msg, e, fIn.Name, mbin );
Expand All @@ -237,22 +237,22 @@ private static string ConvertMBIN( string inputPath, FileStream fIn, MemoryStrea
return fileOut;
}

/// <summary>Convert EXML to MBIN</summary>
/// <summary>Convert MXML to MBIN</summary>
/// <param name="fIn">Source file</param>
/// <param name="msOut">Output stream</param>
/// <param name="fileOut">Output file path. Passed through as the return value. For geometry files, ".PC" will be appended.</param>
/// <returns>fileOut</returns>
private static string ConvertEXML( string inputPath, FileStream fIn, MemoryStream msOut, string fileOut ) {
private static string ConvertMXML( string inputPath, FileStream fIn, MemoryStream msOut, string fileOut ) {
string templateName;
NMSTemplate data = null;
try {
data = EXmlFile.ReadTemplateFromStream( fIn, out templateName );
data = MXmlFile.ReadTemplateFromStream( fIn, out templateName );

Type type = NMSTemplate.GetTemplateType( templateName );
var nms = (NMSAttribute) (data.GetType().GetCustomAttributes( typeof( NMSAttribute ), false )?[0] ?? null);
if ( nms.Broken ) FileIsBroken( inputPath, data );

if ( data is null ) throw new InvalidDataException( $"Failed to deserialize EXML." );
if ( data is null ) throw new InvalidDataException( $"Failed to deserialize MXML." );
if ( data is libMBIN.NMS.Toolkit.TkGeometryData | data is libMBIN.NMS.Toolkit.TkGeometryStreamData ) fileOut += ".PC";

var mbin = new MBINFile( msOut ) { Header = new MBINHeader() };
Expand All @@ -261,7 +261,7 @@ private static string ConvertEXML( string inputPath, FileStream fIn, MemoryStrea
mbin.Save();
} catch ( Exception e ) {
Console.WriteLine($"ERR INFO: {e.StackTrace}");
throw new ExmlException( e, fIn.Name, data );
throw new MxmlException( e, fIn.Name, data );
}

return fileOut;
Expand Down Expand Up @@ -292,7 +292,7 @@ private static void _FileIsBroken( string filePath, MBINFile mbin, NMSTemplate d
private static void WarnBroken( string msg, string filePath, MBINFile mbin, NMSTemplate data = null, ulong expectedGUID = 0L ) {
#if ERROR_ON_BROKEN
if (mbin != null) throw new MbinException( msg, filePath, mbin );
throw new ExmlException( msg, filePath, data );
throw new MxmlException( msg, filePath, data );
#endif

Async.SynchronizeTask( errorLock, ref errorTask, () => {
Expand Down Expand Up @@ -323,7 +323,7 @@ private static string ChangeFileExtension( string file, FormatType format ) {
if ( x == ".PC" ) file = Path.ChangeExtension( file, null );
x = Path.GetExtension( file ).ToUpper();
if ( x == ".MBIN" ) return Path.ChangeExtension( file, ext );
if ( x == ".EXML" ) return Path.ChangeExtension( file, ext );
if ( x == ".MXML" ) return Path.ChangeExtension( file, ext );
}
return file + $".{ext}";
}
Expand Down
39 changes: 20 additions & 19 deletions MBINCompiler/Source/Commands/ConvertMode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,13 @@ public override int ExecuteCommand( CommandLineParser options ) {
var defaultInclude = "*";
bool autoFormat = (formatI == null) && (formatO == null);
if ( autoFormat ) {
defaultInclude = "*.MBIN;*.MBIN.PC;*.EXML";
defaultInclude = "*.MBIN;*.MBIN.PC;*.MXML";
} else {
if ( !SetFormatOptions( formatI, formatO ) ) return (int) ErrorCode.CommandLine;
defaultInclude = (InputFormat == FormatType.MBIN) ? "*.MBIN;*.MBIN.PC" : "*.EXML";
defaultInclude = (InputFormat == FormatType.MBIN) ? "*.MBIN;*.MBIN.PC" : "*.MXML";
}

var defaultExclude = @"LANGUAGE\*;*.GEOMETRY.*";
var defaultExclude = @"";

//#if DEBUG
//defaultExclude = "";
Expand Down Expand Up @@ -101,7 +101,7 @@ private static bool GetOverwriteOption( CommandLineParser options ) {
private static bool SetFormatOptions( string formatI, string formatO ) {
if ( formatI != null ) {
InputFormat = (formatI == "MBIN") ? FormatType.MBIN : InputFormat;
InputFormat = (formatI == "EXML") ? FormatType.EXML : InputFormat;
InputFormat = (formatI == "MXML") ? FormatType.MXML : InputFormat;
if ( InputFormat == FormatType.Unknown ) {
CommandLine.ShowCommandLineError( $"Invalid format specified: {formatI}" );
return false;
Expand All @@ -110,15 +110,16 @@ private static bool SetFormatOptions( string formatI, string formatO ) {

if ( formatO != null ) {
OutputFormat = (formatO == "MBIN") ? FormatType.MBIN : OutputFormat;
OutputFormat = (formatO == "MXML") ? FormatType.MXML : OutputFormat;
OutputFormat = (formatO == "EXML") ? FormatType.EXML : OutputFormat;
if ( OutputFormat == FormatType.Unknown ) {
CommandLine.ShowCommandLineError( $"Invalid format specified: {formatO}" );
return false;
}
}

if ( formatI == null ) InputFormat = (OutputFormat == FormatType.MBIN) ? FormatType.EXML : FormatType.MBIN;
if ( formatO == null ) OutputFormat = (InputFormat == FormatType.MBIN) ? FormatType.EXML : FormatType.MBIN;
if ( formatI == null ) InputFormat = (OutputFormat == FormatType.MBIN) ? FormatType.MXML : FormatType.MBIN;
if ( formatO == null ) OutputFormat = (InputFormat == FormatType.MBIN) ? FormatType.MXML : FormatType.MBIN;

if ( InputFormat == OutputFormat ) {
CommandLine.ShowCommandLineError( "--input-format and --output-format cannot be the same type!" );
Expand Down Expand Up @@ -172,60 +173,60 @@ private static string[] GetDirectoryFiles( string path, string filter ) {
return new string[] { };
}

private static string DetectFormat( string file, ref bool foundMBIN, ref bool foundEXML ) {
private static string DetectFormat( string file, ref bool foundMBIN, ref bool foundMXML ) {
if ( Path.HasExtension( file ) ) {
var ext = Path.GetExtension( file ).ToUpper();
foundMBIN |= (ext == ".MBIN") || (ext == ".PC");
foundEXML |= (ext == ".EXML");
foundMXML |= (ext == ".MXML");
}
return file;
}

private static bool AutoDetectFormat( ref List<string> fileList ) {
// detect what types of file formats are found
bool foundMBIN = false;
bool foundEXML = false;
bool foundMXML = false;

foreach ( var file in fileList ) DetectFormat( file, ref foundMBIN, ref foundEXML );
foreach ( var file in fileList ) DetectFormat( file, ref foundMBIN, ref foundMXML );

// TODO: this should be handled better
if ( !foundMBIN && !foundEXML ) {
if ( !foundMBIN && !foundMXML ) {
if ( (fileList.Count == 1) && File.Exists( fileList[0] ) ) {
using ( var fIn = new FileStream( fileList[0], FileMode.Open ) ) {
// possibly MBIN? check for a valid header
using ( var mbin = new MBINFile( fIn, true ) ) foundMBIN = (mbin.Load() && mbin.Header.IsValid);
if ( !foundMBIN ) { // possibly EXML? check for a valid xml tag
if ( !foundMBIN ) { // possibly MXML? check for a valid xml tag
var xmlTag = "<?xml version=\"1.0\" encoding=\"utf-8\"?>".ToLower();
var bytes = new byte[xmlTag.Length];
// TODO: handle potential leading whitespace?
if ( fIn.Read( bytes, 0, xmlTag.Length ) == xmlTag.Length ) {
var txt = System.Text.Encoding.ASCII.GetString( bytes ).ToLower();
foundEXML = (txt == xmlTag);
foundMXML = (txt == xmlTag);
}
}
}
}
}

if ( foundMBIN && foundEXML ) {
if ( foundMBIN && foundMXML ) {
const string msg = "Unable to automatically determine the --input-format type.";
if ( Quiet ) return (CommandLine.ShowError( msg ) == (int) ErrorCode.Success);
CommandLine.ShowWarning( msg );
Console.Out.WriteLine( "Both MBIN and EXML file types were detected!\n" );
Console.Out.WriteLine( "Both MBIN and MXML file types were detected!\n" );
InputFormat = Utils.PromptInputFormat();
Console.WriteLine();
} else if ( foundMBIN ) {
if (!StreamToConsole) Logger.LogInfo( "Auto-Detected --input-format=MBIN" );
InputFormat = FormatType.MBIN;
} else if ( foundEXML ) {
if (!StreamToConsole) Logger.LogInfo( "Auto-Detected --input-format=EXML" );
InputFormat = FormatType.EXML;
} else if ( foundMXML ) {
if (!StreamToConsole) Logger.LogInfo( "Auto-Detected --input-format=MXML" );
InputFormat = FormatType.MXML;
} else {
CommandLine.ShowError( "No valid files found!" );
return false;
}

OutputFormat = (InputFormat == FormatType.MBIN) ? FormatType.EXML : FormatType.MBIN;
OutputFormat = (InputFormat == FormatType.MBIN) ? FormatType.MXML : FormatType.MBIN;
Logger.LogMessage( "INFO", $"--input-format={InputFormat} --output-format={OutputFormat}" );

// exclude any files that don't match InputFormat
Expand Down
Loading