Program.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. using System;
  2. using System.IO;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using Ionic.Zlib;
  6. using ExIni;
  7. namespace Converter
  8. {
  9. public class Program
  10. {
  11. private static StreamWriter writer;
  12. public static void Main(string[] args)
  13. {
  14. string writerPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log.txt");
  15. IniFile mmIniFile = IniFile.FromFile(args[0]);
  16. IniSection sceneSection = mmIniFile.GetSection("scene");
  17. using (writer = new StreamWriter(writerPath))
  18. {
  19. if (sceneSection != null)
  20. {
  21. foreach (IniKey key in sceneSection.Keys)
  22. {
  23. ProcessScene(key);
  24. }
  25. }
  26. }
  27. }
  28. public static void ProcessScene(IniKey sceneKey)
  29. {
  30. if (sceneKey.Key.StartsWith("ss")) return;
  31. string sceneData = sceneKey.Value;
  32. if (string.IsNullOrEmpty(sceneData)) return;
  33. writer.WriteLine($"Deserialize {sceneKey.Key}");
  34. string[] strArray1 = sceneData.Split('_');
  35. string[] strArray2 = strArray1[1].Split(';');
  36. string[] strArray3 = strArray1[0].Split(',');
  37. string[] strArray4 = null;
  38. string[] strArray5 = null;
  39. string[] strArray6 = null;
  40. string[] strArray7 = null;
  41. if (strArray1.Length >= 5)
  42. {
  43. strArray4 = strArray1[2].Split(',');
  44. strArray5 = strArray1[3].Split(';');
  45. strArray6 = strArray1[4].Split(';');
  46. }
  47. if (strArray1.Length >= 6)
  48. {
  49. strArray7 = strArray1[5].Split(';');
  50. }
  51. // Environment
  52. int bgIndex;
  53. string bgAsset = "Theater";
  54. if (!int.TryParse(strArray3[2], out bgIndex))
  55. {
  56. bgAsset = strArray3[2].Replace(" ", "_");
  57. }
  58. else
  59. {
  60. writer.WriteLine($"No BG string: {bgIndex}");
  61. }
  62. Quaternion bgRotation = Quaternion.Euler(
  63. float.Parse(strArray3[3]), float.Parse(strArray3[4]), float.Parse(strArray3[5])
  64. );
  65. Vector3 bgPosition = new Vector3(
  66. float.Parse(strArray3[6]), float.Parse(strArray3[7]), float.Parse(strArray3[8])
  67. );
  68. Vector3 bgLocalScale = new Vector3(
  69. float.Parse(strArray3[9]), float.Parse(strArray3[10]), float.Parse(strArray3[11])
  70. );
  71. if (strArray3.Length > 16)
  72. {
  73. Vector3 cameraTargetPos = new Vector3(
  74. float.Parse(strArray3[27]), float.Parse(strArray3[28]), float.Parse(strArray3[29])
  75. );
  76. float cameraDistance = float.Parse(strArray3[30]);
  77. Quaternion cameraRotation = Quaternion.Euler(
  78. float.Parse(strArray3[31]), float.Parse(strArray3[32]), float.Parse(strArray3[33])
  79. );
  80. }
  81. // Lights
  82. if (strArray3.Length > 16)
  83. {
  84. // Main Light
  85. /*
  86. 0 = Directional
  87. 1 = Spot
  88. 2 = Point
  89. 3 = Directional (Colour Mode)
  90. */
  91. int lightType = int.Parse(strArray3[17]);
  92. Color lightColor = new Color(
  93. float.Parse(strArray3[18]), float.Parse(strArray3[19]), float.Parse(strArray3[20]), 1f
  94. );
  95. Quaternion lightRotation = Quaternion.Euler(
  96. float.Parse(strArray3[21]), float.Parse(strArray3[22]), float.Parse(strArray3[23])
  97. );
  98. // MM uses spotAngle for both range and spotAngle based on which light type is used
  99. // TODO: assign value from spot angle appropriately
  100. float intensity = float.Parse(strArray3[24]);
  101. float spotAngle = float.Parse(strArray3[25]);
  102. float range = float.Parse(strArray3[26]);
  103. float shadowStrength = 0.098f;
  104. if (strArray4 != null) shadowStrength = float.Parse(strArray4[0]);
  105. // lightKage[0] is the only value that's serialized
  106. }
  107. int lights = 1;
  108. if (strArray5 != null)
  109. {
  110. int numberOfLights = strArray5.Length - 1;
  111. lights += numberOfLights;
  112. for (int i = 0; i < numberOfLights; i++)
  113. {
  114. string[] lightProperties = strArray5[i].Split(',');
  115. int lightType = int.Parse(lightProperties[0]);
  116. Color lightColor = new Color(
  117. float.Parse(lightProperties[1]), float.Parse(lightProperties[1]),
  118. float.Parse(lightProperties[1]), 1f
  119. );
  120. Quaternion lightAngle = Quaternion.Euler(
  121. float.Parse(lightProperties[4]), float.Parse(lightProperties[5]), 18f
  122. );
  123. float intensity = float.Parse(lightProperties[6]);
  124. float spotAngle = float.Parse(lightProperties[7]);
  125. float range = spotAngle / 5f;
  126. float shadowStrength = 0.098f;
  127. }
  128. }
  129. if (strArray7 != null)
  130. {
  131. for (int i = 0; i < lights; i++)
  132. {
  133. string[] lightPosString = strArray7[i].Split(',');
  134. Vector3 lightPosition = new Vector3(
  135. float.Parse(lightPosString[0]), float.Parse(lightPosString[1]), float.Parse(lightPosString[2])
  136. );
  137. }
  138. }
  139. // Message
  140. if (strArray3.Length > 16)
  141. {
  142. bool showingMessage = int.Parse(strArray3[34]) == 1;
  143. string name = strArray3[35];
  144. string message = strArray3[36].Replace("&kaigyo", "\n");
  145. // MM does not serialize message font size
  146. }
  147. // effect
  148. if (strArray4 != null)
  149. {
  150. // bloom
  151. bool bloomActive = int.Parse(strArray4[1]) == 1;
  152. float bloomIntensity = float.Parse(strArray4[2]);
  153. float bloomBlurIterations = float.Parse(strArray4[3]);
  154. Color bloomColour = new Color(
  155. float.Parse(strArray4[4]), float.Parse(strArray4[5]), float.Parse(strArray4[6]), 1f
  156. );
  157. bool bloomHdr = int.Parse(strArray4[7]) == 1;
  158. // vignetting
  159. bool vignetteActive = int.Parse(strArray4[8]) == 1;
  160. float vignetteIntensity = float.Parse(strArray4[9]);
  161. float vignetteBlur = float.Parse(strArray4[10]);
  162. float vignetteBlurSpread = float.Parse(strArray4[11]);
  163. float vignetteChromaticAberration = float.Parse(strArray4[12]);
  164. // bokashi (TODO: implement in MPS)
  165. float bokashi = float.Parse(strArray4[13]);
  166. if (strArray4.Length > 15)
  167. {
  168. bool dofActive = int.Parse(strArray4[15]) == 1;
  169. float dofFocalLength = float.Parse(strArray4[16]);
  170. float dofFocalSize = float.Parse(strArray4[17]);
  171. float dofAperture = float.Parse(strArray4[18]);
  172. float dofMaxBlurSize = float.Parse(strArray4[19]);
  173. bool dofVisualizeFocus = int.Parse(strArray4[20]) == 1;
  174. bool fogActive = int.Parse(strArray4[21]) == 1;
  175. float fogStartDistance = float.Parse(strArray4[22]);
  176. float fogDensity = float.Parse(strArray4[23]);
  177. float fogHeightScale = float.Parse(strArray4[24]);
  178. float fogHeight = float.Parse(strArray4[25]);
  179. Color fogColor = new Color(
  180. float.Parse(strArray4[26]), float.Parse(strArray4[27]), float.Parse(strArray4[28]), 1f
  181. );
  182. }
  183. }
  184. // prop
  185. if (strArray3.Length > 37 && !string.IsNullOrEmpty(strArray3[37]))
  186. {
  187. // For the prop that spawns when you push (shift +) W
  188. string assetName = strArray3[37].Replace(' ', '_');
  189. Vector3 position = new Vector3(
  190. float.Parse(strArray3[41]), float.Parse(strArray3[42]), float.Parse(strArray3[43])
  191. );
  192. Quaternion rotation = Quaternion.Euler(
  193. float.Parse(strArray3[38]), float.Parse(strArray3[39]), float.Parse(strArray3[40])
  194. );
  195. Vector3 localScale = new Vector3(
  196. float.Parse(strArray3[44]), float.Parse(strArray3[45]), float.Parse(strArray3[46])
  197. );
  198. }
  199. if (strArray6 != null)
  200. {
  201. for (int i = 0; i < strArray6.Length - 1; i++)
  202. {
  203. string[] assetParts = strArray6[i].Split(',');
  204. string assetName = assetParts[0].Replace(' ', '_');
  205. if (assetName.StartsWith("creative_"))
  206. {
  207. // modifiedMM my room creative prop
  208. // modifiedMM serializes the prefabName rather than the ID.
  209. // TODO: Either write a special case for MPS or rewrite for use in game
  210. assetName.Replace("creative_", String.Empty);
  211. assetName = $"MYR_#{assetName}";
  212. }
  213. // else if (assetName.StartsWith("MYR_"))
  214. // {
  215. // // MM 23.0+ my room creative prop
  216. // assetName = assetName + "#";
  217. // }
  218. else if (assetName.Contains('#'))
  219. {
  220. if (assetName.Contains(".menu"))
  221. {
  222. // modifiedMM official mod prop
  223. string[] modComponents = assetParts[0].Split('#');
  224. string baseMenuFile = modComponents[0].Replace(' ', '_');
  225. string modItem = modComponents[1].Replace(' ', '_');
  226. assetName = $"{modComponents[0]}#{modComponents[1]}";
  227. }
  228. else
  229. {
  230. assetName = assetName.Split('#')[1].Replace(' ', '_');
  231. }
  232. }
  233. writer.WriteLine(assetName);
  234. Vector3 position = new Vector3(
  235. float.Parse(assetParts[4]), float.Parse(assetParts[5]), float.Parse(assetParts[6])
  236. );
  237. Quaternion rotation = Quaternion.Euler(
  238. float.Parse(assetParts[1]), float.Parse(assetParts[2]), float.Parse(assetParts[3])
  239. );
  240. Vector3 scale = new Vector3(
  241. float.Parse(assetParts[7]), float.Parse(assetParts[8]), float.Parse(assetParts[9])
  242. );
  243. }
  244. }
  245. // meido
  246. int numberOfMaids = strArray2.Length;
  247. for (int i = 0; i < numberOfMaids; i++)
  248. {
  249. List<Quaternion> fingerRotation = new List<Quaternion>();
  250. string[] maidData = strArray2[i].Split(':');
  251. for (int j = 0; j < 40; j++)
  252. {
  253. string fingerString = maidData[j];
  254. fingerRotation.Add(Quaternion.FromEulerString(fingerString));
  255. }
  256. // TODO: Other maid related things
  257. }
  258. }
  259. }
  260. public static class BinaryExtensions
  261. {
  262. public static void WriteVector3(this BinaryWriter binaryWriter, Vector3 vector3)
  263. {
  264. binaryWriter.Write(vector3.x);
  265. binaryWriter.Write(vector3.y);
  266. binaryWriter.Write(vector3.z);
  267. }
  268. public static void WriteQuaternion(this BinaryWriter binaryWriter, Quaternion quaternion)
  269. {
  270. binaryWriter.Write(quaternion.x);
  271. binaryWriter.Write(quaternion.y);
  272. binaryWriter.Write(quaternion.z);
  273. binaryWriter.Write(quaternion.w);
  274. }
  275. }
  276. public struct Vector3
  277. {
  278. public float x, y, z;
  279. public Vector3(float x, float y, float z)
  280. {
  281. this.x = x;
  282. this.y = y;
  283. this.z = z;
  284. }
  285. public static Vector3 FromString(string vector3)
  286. {
  287. string[] data = vector3.Split(',');
  288. return new Vector3(
  289. float.Parse(data[0]), float.Parse(data[1]), float.Parse(data[2])
  290. );
  291. }
  292. }
  293. public struct Quaternion
  294. {
  295. public const float DegToRad = MathF.PI / 180f;
  296. public float x, y, z, w;
  297. public Quaternion(float x, float y, float z, float w)
  298. {
  299. this.x = x;
  300. this.y = y;
  301. this.z = z;
  302. this.w = w;
  303. }
  304. public static Quaternion Euler(float x, float y, float z)
  305. {
  306. System.Numerics.Quaternion q = System.Numerics.Quaternion.CreateFromYawPitchRoll(
  307. y * DegToRad, x * DegToRad, z * DegToRad
  308. );
  309. return new Quaternion(q.X, q.Y, q.Z, q.W);
  310. }
  311. public static Quaternion FromEulerString(string euler)
  312. {
  313. Vector3 components = Vector3.FromString(euler);
  314. return Euler(components.x, components.y, components.z);
  315. }
  316. }
  317. public struct Color
  318. {
  319. public float r, g, b, a;
  320. public Color(float r, float g, float b, float a)
  321. {
  322. this.r = r;
  323. this.g = g;
  324. this.b = b;
  325. this.a = a;
  326. }
  327. }
  328. }