Skip to content

Commit

Permalink
initial commit. Program is tested and working as intended.
Browse files Browse the repository at this point in the history
  • Loading branch information
DropTheSquid committed Nov 3, 2021
0 parents commit 132b3e3
Show file tree
Hide file tree
Showing 17 changed files with 2,034 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**/.vs
**/bin
**/obj
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
This command line application converts a Mass Effect 3 (Legendary Edition; Original is untested) Headmorph file exported from the save editor in the .ron format into a fomrat that can be used by the Appearance Modification Menu.

Simply run the program on the command line with the following arguments:
Path to input ron file
Path to output file (optional, defaults to input with extension changed to txt)
gender in the format of --gender=Male or --gender=Female


It will run and output the converted text file.
This program may require that you install .Net 5.

To add your converted headmorph into the Appearance Modification Menu, add the following text to your BioUI file in your DLC mod:
<Section name="sfxgamecontent.sfxguidata_appearance_presets">
<Property name="appearanceitemarray">
<!-- your text here -->
</Property>
</Section>


Be sure to update the string reference that appears on the first line to a short description of your headmorph, which willl appear in the store.


This program makes use of code from the ME3Tweaks Mod Manager repository for parsing ron files, and I am grateful that I can build on their work.
25 changes: 25 additions & 0 deletions RonConverter.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31729.503
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RonConverter", "RonConverter\RonConverter.csproj", "{5456E134-8EDB-4137-9000-61A301DD064F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5456E134-8EDB-4137-9000-61A301DD064F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5456E134-8EDB-4137-9000-61A301DD064F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5456E134-8EDB-4137-9000-61A301DD064F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5456E134-8EDB-4137-9000-61A301DD064F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4E49D2E4-71CD-4C08-A37E-EB57274E75D3}
EndGlobalSection
EndGlobal
24 changes: 24 additions & 0 deletions RonConverter/CommandLineOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using CommandLine;

namespace RonConverter
{
public class CommandLineOptions
{
[Value(index: 0, Required = true, HelpText = "input ron file is required")]
public string Input { get; set; }

[Value(index: 1, Required = false, HelpText = "output filename")]
public string output { get; set; }

[Option(Required = true)]
public Gender Gender { get; set; }
}

public enum Gender
{
Either,
Female,
Male
}
}

115 changes: 115 additions & 0 deletions RonConverter/Converter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System.IO;
using System.Text;
using System.Threading.Tasks;
using MassEffectModManagerCore.modmanager.save.game3;

namespace RonConverter
{
public static class Converter
{
public static async Task Convert(string inputFile, string outputFile, Gender gender)
{
var morphHead = RONConverter.ConvertRON(inputFile);

var outputString = ConvertToCoalescedFormat(morphHead, gender);

if (string.IsNullOrWhiteSpace(outputFile))
{
outputFile = Path.ChangeExtension(inputFile, "txt");
}

await File.WriteAllTextAsync(outputFile, outputString);
}

private static string ConvertToCoalescedFormat(MorphHead morphHead, Gender gender)
{
var sb = new StringBuilder();

sb.AppendLine(@$"<Value type=""3"">(Gender=Gender_{gender},ChoiceEntry=(srChoiceName=614208),Delta=(");

// Hair Mesh
sb.AppendLine(@$"HairMesh=""{(string.IsNullOrWhiteSpace(morphHead.HairMesh) ? "None" : morphHead.HairMesh)}"",");

// accessory meshes
sb.AppendLine(@"AccessoryMeshDeltas[0]=(Name=""*"",Remove=true),");
for (int i = 0; i < morphHead.AccessoryMeshes.Count; i++)
{
var accessory = morphHead.AccessoryMeshes[i];
sb.AppendLine(@$"AccessoryMeshDeltas[{i+1}]=(Name=""{accessory}""),");
}

// Morph Features
sb.AppendLine(@"MorphFeatureDeltas[0]=(Feature=""*"",Remove=true),");
for (int i = 0; i < morphHead.MorphFeatures.Count; i++)
{
var morphFeature = morphHead.MorphFeatures[i];
sb.AppendLine(@$"MorphFeatureDeltas[{i+1}]=(Feature=""{morphFeature.Feature}"",Offset=""{morphFeature.Offset}""),");
}

// Offset Bones
sb.AppendLine(@"OffsetBoneDeltas[0]=(Name=""*"",Remove=true),");
for (int i = 0; i < morphHead.OffsetBones.Count; i++)
{
var bone = morphHead.OffsetBones[i];
sb.AppendLine(@$"OffsetBoneDeltas[{i+1}]=(Name=""{bone.Name}"",Offset=(X=""{bone.Offset.X:F15}"",Y=""{bone.Offset.Y:F15}"",Z=""{bone.Offset.Z:F15}"")),");
}

// vertices
for (int i = 0; i < morphHead.Lod0Vertices.Count; i++)
{
var vertex = morphHead.Lod0Vertices[i];
sb.Append(@$"LOD0Vertices[{i}]=(X={vertex.X:F15},Y={vertex.Y:F15},Z={vertex.Z:F15}),");
}
sb.AppendLine();

for (int i = 0; i < morphHead.Lod1Vertices.Count; i++)
{
var vertex = morphHead.Lod1Vertices[i];
sb.Append(@$"LOD1Vertices[{i}]=(X={vertex.X:F15},Y={vertex.Y:F15},Z={vertex.Z:F15}),");
}
sb.AppendLine();

for (int i = 0; i < morphHead.Lod2Vertices.Count; i++)
{
var vertex = morphHead.Lod2Vertices[i];
sb.Append(@$"LOD2Vertices[{i}]=(X={vertex.X:F15},Y={vertex.Y:F15},Z={vertex.Z:F15}),");
}
sb.AppendLine();

for (int i = 0; i < morphHead.Lod3Vertices.Count; i++)
{
var vertex = morphHead.Lod3Vertices[i];
sb.Append(@$"LOD3Vertices[{i}]=(X={vertex.X:F15},Y={vertex.Y:F15},Z={vertex.Z:F15}),");
}
sb.AppendLine();

// scalar params
sb.AppendLine(@"ScalarParameterDeltas[0]=(Name=""*"",Remove=true),");
for (int i = 0; i < morphHead.ScalarParameters.Count; i++)
{
var scalar = morphHead.ScalarParameters[i];
sb.AppendLine(@$"ScalarParameterDeltas[{i+1}]=(Name=""{scalar.Name}"",Value=""{scalar.Value}""),");
}

// Vector Params
sb.AppendLine(@"VectorParameterDeltas[0]=(Name=""*"",Remove=true),");
for (int i = 0; i < morphHead.VectorParameters.Count; i++)
{
var vector = morphHead.VectorParameters[i];
sb.AppendLine(@$"VectorParameterDeltas[{i+1}]=(Name=""{vector.Name}"",Value=(R=""{vector.Value.R * 255}"",G=""{vector.Value.G * 255}"",B=""{vector.Value.B * 255}"",A=""{vector.Value.A * 255}"")),");
}

// Texture params
sb.AppendLine(@"TextureParameterDeltas[0]=(Name=""*"",Remove=true),");
for (int i = 0; i < morphHead.TextureParameters.Count; i++)
{
var tex = morphHead.TextureParameters[i];
sb.AppendLine(@$"TextureParameterDeltas[{i+1}]=(Name=""{tex.Name}"",Texture={tex.Value}),");
}

sb.Append("))\r\n</Value>");

return sb.ToString();
}
}
}
20 changes: 20 additions & 0 deletions RonConverter/M3/IPlayerRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MassEffectModManagerCore.modmanager.save
{
public interface IPlayerRecord
{
public bool Proxy_IsFemale { get; set; }
public string Proxy_FirstName { get; set; }

public void SetMorphHead(IMorphHead morphHead);
}

public interface IMorphHead
{
}
}
7 changes: 7 additions & 0 deletions RonConverter/M3/IUnrealSerializable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace MassEffectModManagerCore.modmanager.save.game2.FileFormats
{
public interface IUnrealSerializable
{
void Serialize(IUnrealStream stream);
}
}
75 changes: 75 additions & 0 deletions RonConverter/M3/IUnrealStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;

namespace MassEffectModManagerCore.modmanager.save.game2.FileFormats
{
public interface IUnrealStream
{
Stream Stream { get; }
bool Loading { get; }
uint Version { get; }

// Basic
void Serialize(ref bool value);
void Serialize(ref byte value);
void Serialize(ref int value);
void Serialize(ref uint value);
void Serialize(ref float value);
void Serialize(ref string value);
void Serialize(ref Guid value);

// Lists
void Serialize(ref List<bool> values);
void Serialize(ref List<int> values);
void Serialize(ref List<uint> values);
void Serialize(ref List<float> values);
void Serialize(ref List<string> values);
void Serialize(ref List<Guid> values);
void Serialize(ref BitArray values);

// Serializables
void Serialize<TFormat>(ref TFormat value)
where TFormat : IUnrealSerializable, new();

// Serializable List
void Serialize<TFormat>(ref List<TFormat> values)
where TFormat : IUnrealSerializable, new();

void Serialize<TType>(ref BindingList<TType> list) where TType : class, IUnrealSerializable, new();
// void Serialize(ref IUnrealSerializable value);

// Enum
void SerializeEnum<TEnum>(ref TEnum value);
void SerializeEnum<TEnum>(ref TEnum value, Func<IUnrealStream, bool> condition, Func<TEnum> defaultValue);


// Conditional serialization
void Serialize(ref bool value, Func<IUnrealStream, bool> condition, Func<bool> defaultValue);
void Serialize(ref byte value, Func<IUnrealStream, bool> condition, Func<byte> defaultValue);
void Serialize(ref int value, Func<IUnrealStream, bool> condition, Func<int> defaultValue);
void Serialize(ref uint value, Func<IUnrealStream, bool> condition, Func<uint> defaultValue);
void Serialize(ref float value, Func<IUnrealStream, bool> condition, Func<float> defaultValue);
void Serialize(ref string value, Func<IUnrealStream, bool> condition, Func<string> defaultValue);
void Serialize(ref Guid value, Func<IUnrealStream, bool> condition, Func<Guid> defaultValue);


void Serialize<TType>(ref TType value, Func<IUnrealStream, bool> condition, Func<TType> defaultValue)
where TType : class, IUnrealSerializable, new();
void Serialize(ref BitArray list, Func<IUnrealStream, bool> condition, Func<BitArray> defaultList);
void Serialize(ref List<byte> list, Func<IUnrealStream, bool> condition, Func<List<byte>> defaultList);
void Serialize(ref List<int> list, Func<IUnrealStream, bool> condition, Func<List<int>> defaultList);
void Serialize(ref List<float> list, Func<IUnrealStream, bool> condition, Func<List<float>> defaultList);
void Serialize(ref List<string> list, Func<IUnrealStream, bool> condition, Func<List<string>> defaultList);
void Serialize(ref List<Guid> list, Func<IUnrealStream, bool> condition, Func<List<Guid>> defaultList);
void SerializeEnum<TEnum>(ref List<TEnum> list, Func<IUnrealStream, bool> condition, Func<List<TEnum>> defaultList);
void Serialize<TType>(ref List<TType> list, Func<IUnrealStream, bool> condition, Func<List<TType>> defaultList)
where TType : class, IUnrealSerializable, new();
void Serialize<TType>(ref BindingList<TType> list,
Func<IUnrealStream, bool> condition,
Func<BindingList<TType>> defaultList)
where TType : class, IUnrealSerializable, new();
}
}
Loading

0 comments on commit 132b3e3

Please sign in to comment.