Skip to content

Commit 1954dbe

Browse files
committed
Introduction of IMC Saving Backend Functions (Not available via UI)
1 parent 74d81aa commit 1954dbe

File tree

3 files changed

+184
-5
lines changed

3 files changed

+184
-5
lines changed

xivModdingFramework/Cache/XivCache.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ public string GetSql(bool includeWhere = true, bool skipJoin = true)
977977

978978

979979
// [AND/OR] [COLUMN] [=/LIKE]
980-
if (Comparer == ComparisonType.Like)
980+
if (Comparer == ComparisonType.Equal)
981981
{
982982
result += " = ";
983983
}

xivModdingFramework/Variants/DataContainers/XivImc.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
// You should have received a copy of the GNU General Public License
1515
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1616

17+
using HelixToolkit.SharpDX.Core;
18+
using System;
19+
using System.Collections.Generic;
20+
using System.Linq;
21+
using xivModdingFramework.Helpers;
22+
1723
namespace xivModdingFramework.Variants.DataContainers
1824
{
1925
/// <summary>
@@ -43,5 +49,62 @@ public class XivImc
4349
/// Some examples would be any of the Lux weapons
4450
/// </remarks>
4551
public ushort Vfx { get; set; }
52+
53+
/// <summary>
54+
/// Returns the raw bytes that make up this IMC entry.
55+
/// </summary>
56+
/// <returns></returns>
57+
public byte[] GetBytes()
58+
{
59+
var bytes = new List<byte>();
60+
bytes.AddRange(BitConverter.GetBytes(Variant));
61+
bytes.AddRange(BitConverter.GetBytes(Mask));
62+
bytes.AddRange(BitConverter.GetBytes(Vfx));
63+
return bytes.ToArray();
64+
}
65+
66+
/// <summary>
67+
/// Shows a given attribute part
68+
/// </summary>
69+
/// <param name="part"></param>
70+
public void ShowPart(char part)
71+
{
72+
var index = Helpers.Constants.Alphabet.ToList().IndexOf(part);
73+
if(index < 0 || index > 9)
74+
{
75+
throw new NotSupportedException("Invalid IMC Part Letter.");
76+
}
77+
78+
var bit = 1;
79+
for(var i = 0; i < index; i++)
80+
{
81+
bit = bit << 1;
82+
}
83+
84+
Mask = (ushort)(Mask | bit);
85+
}
86+
87+
/// <summary>
88+
/// Hides a given attribute part
89+
/// </summary>
90+
/// <param name="part"></param>
91+
public void HidePart(char part)
92+
{
93+
var index = Helpers.Constants.Alphabet.ToList().IndexOf(part);
94+
if (index < 0 || index > 9)
95+
{
96+
throw new NotSupportedException("Invalid IMC Part Letter.");
97+
}
98+
99+
var bit = 1;
100+
for (var i = 0; i < index; i++)
101+
{
102+
bit = bit << 1;
103+
}
104+
105+
Mask = (ushort)(Mask & (~bit));
106+
107+
}
108+
46109
}
47110
}

xivModdingFramework/Variants/FileTypes/Imc.cs

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ public async Task<XivImc> GetImcInfo(IItemModel item)
6464
var info = await GetFullImcInfo(item);
6565
var slot = item.GetItemSlotAbbreviation();
6666

67-
return info.GetEntry(item.ModelInfo.ImcSubsetID, slot);
67+
var result = info.GetEntry(item.ModelInfo.ImcSubsetID, slot);
68+
return result;
6869
}
6970

7071
public async Task<FullImcInfo> GetFullImcInfo(IItemModel item)
@@ -79,14 +80,19 @@ public async Task<FullImcInfo> GetFullImcInfo(IItemModel item)
7980
{
8081
// Some dual wield items don't have a second IMC, and just default to the first.
8182
var gear = (XivGear)item;
82-
if (gear != null && gear.PairedItem != null) {
83-
return await (GetFullImcInfo(gear.PairedItem));
83+
if (gear != null && gear.PairedItem != null)
84+
{
85+
var pair = gear.PairedItem;
86+
var imcPath = GetImcPath(pair);
87+
var path = imcPath.Folder + "/" + imcPath.File;
88+
return await (GetFullImcInfo(path));
8489
}
8590
}
8691

8792
return info;
8893
}
8994

95+
9096
/// <summary>
9197
/// Gets the full IMC information for a given item
9298
/// </summary>
@@ -185,6 +191,87 @@ public async Task<FullImcInfo> GetFullImcInfo(string path)
185191
});
186192
}
187193

194+
public async Task SaveImcInfo(XivImc info, IItemModel item)
195+
{
196+
var full = await GetFullImcInfo(item);
197+
full.SetEntry(info, item.ModelInfo.ImcSubsetID, item.GetItemSlotAbbreviation());
198+
await SaveFullImcInfo(full, item);
199+
}
200+
201+
public async Task SaveImcInfo(XivImc info, string path, int subsetId = -1, string slot = "")
202+
{
203+
var full = await GetFullImcInfo(path);
204+
full.SetEntry(info, subsetId, slot);
205+
await SaveFullImcInfo(full, path);
206+
}
207+
208+
public async Task SaveFullImcInfo(FullImcInfo info, IItemModel item)
209+
{
210+
try
211+
{
212+
var imcPath = GetImcPath(item);
213+
var path = imcPath.Folder + "/" + imcPath.File;
214+
await SaveFullImcInfo(info, path);
215+
}
216+
catch
217+
{
218+
// Some dual wield items don't have a second IMC, and just default to the first.
219+
var gear = (XivGear)item;
220+
if (gear != null && gear.PairedItem != null)
221+
{
222+
var pair = gear.PairedItem;
223+
var imcPath = GetImcPath(pair);
224+
var path = imcPath.Folder + "/" + imcPath.File;
225+
await (SaveFullImcInfo(info, path));
226+
}
227+
}
228+
return;
229+
230+
}
231+
232+
public async Task SaveFullImcInfo(FullImcInfo info, string path, string itemName = null, string category = null, string source = null)
233+
{
234+
var index = new Index(_gameDirectory);
235+
var dat = new Dat(_gameDirectory);
236+
237+
238+
var imcOffset = await index.GetDataOffset(path);
239+
240+
// No writing new IMC files.
241+
if (imcOffset == 0)
242+
{
243+
throw new InvalidDataException($"Could not find offset for {path}");
244+
}
245+
246+
var data = new List<byte>();
247+
248+
// 4 Header bytes.
249+
data.AddRange(BitConverter.GetBytes(info.SubsetCount));
250+
data.AddRange(BitConverter.GetBytes((short) info.TypeIdentifier));
251+
252+
// The rest of this is easy, it's literally just post all the sets in order.
253+
foreach(var entry in info.DefaultSubset)
254+
{
255+
data.AddRange(entry.GetBytes());
256+
}
257+
258+
foreach(var set in info.SubsetList)
259+
{
260+
foreach (var entry in set)
261+
{
262+
data.AddRange(entry.GetBytes());
263+
}
264+
}
265+
266+
// That's it.
267+
268+
itemName ??= Path.GetFileName(path);
269+
category ??= "Meta";
270+
source ??= "Internal";
271+
272+
await dat.ImportType2Data(data.ToArray(), itemName, path, category, source);
273+
}
274+
188275
/// <summary>
189276
/// Gets the IMC internal path for the given model info
190277
/// </summary>
@@ -320,13 +407,42 @@ public XivImc GetEntry(int subsetID = -1, string slot = "")
320407

321408
// Get which offset the slot uses.
322409
var idx = 0;
323-
if(_slotOffsetDictionary.ContainsKey(slot))
410+
if(_slotOffsetDictionary.ContainsKey(slot) && _slotOffsetDictionary[slot] < subset.Count)
324411
{
325412
idx = _slotOffsetDictionary[slot];
326413
}
327414

328415
return subset[idx];
329416
}
417+
418+
public void SetEntry(XivImc info, int subsetID = -1, string slot = "")
419+
{
420+
// Variant IDs are 1 based, not 0 based.
421+
var index = subsetID - 1;
422+
423+
// Invalid Index, return default.
424+
if (index >= SubsetCount || index < 0)
425+
{
426+
index = -1;
427+
}
428+
429+
// Test for getting default set.
430+
var subset = DefaultSubset;
431+
if (index >= 0)
432+
{
433+
subset = SubsetList[index];
434+
}
435+
436+
// Get which offset the slot uses.
437+
var idx = 0;
438+
if (_slotOffsetDictionary.ContainsKey(slot) && _slotOffsetDictionary[slot] < subset.Count)
439+
{
440+
idx = _slotOffsetDictionary[slot];
441+
}
442+
443+
// Assign info.
444+
subset[idx] = info;
445+
}
330446
}
331447

332448
}

0 commit comments

Comments
 (0)