Skip to content

Commit 1dcc0e9

Browse files
committed
Add import/export support
1 parent e2ff3ce commit 1dcc0e9

File tree

3 files changed

+359
-27
lines changed

3 files changed

+359
-27
lines changed

ImportExport.lua

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
local _, ns = ...
2+
3+
--- @class TalentViewerImportExport
4+
local ImportExport = ns.ImportExport
5+
6+
--- @type TalentViewer
7+
local TalentViewer = ns.TalentViewer
8+
9+
local LOADOUT_SERIALIZATION_VERSION = 1;
10+
11+
local getNodeInfo = function(nodeId) return TalentViewer:GetTalentFrame():GetAndCacheNodeInfo(nodeId) end
12+
13+
function ImportExport:GetConfigID()
14+
return C_ClassTalents.GetActiveConfigID();
15+
end
16+
function ImportExport:GetTreeId()
17+
return TalentViewer.treeId;
18+
end
19+
function ImportExport:GetSpecId()
20+
return TalentViewer.selectedSpecId;
21+
end
22+
23+
----- copied and adapted from Blizzard_ClassTalentImportExport.lua -----
24+
25+
ImportExport.bitWidthHeaderVersion = 8;
26+
ImportExport.bitWidthSpecID = 16;
27+
ImportExport.bitWidthRanksPurchased = 6;
28+
29+
StaticPopupDialogs["TALENT_VIEWER_LOADOUT_IMPORT_ERROR_DIALOG"] = {
30+
text = "%s",
31+
button1 = OKAY,
32+
button2 = nil,
33+
timeout = 0,
34+
OnAccept = function()
35+
end,
36+
OnCancel = function()
37+
end,
38+
whileDead = 1,
39+
hideOnEscape = 1,
40+
};
41+
42+
function ImportExport:WriteLoadoutContent(exportStream, _, treeID)
43+
local treeNodes = C_Traits.GetTreeNodes(treeID);
44+
for _, treeNodeID in ipairs(treeNodes) do
45+
local treeNode = getNodeInfo(treeNodeID);
46+
47+
local isNodeSelected = treeNode.ranksPurchased > 0;
48+
local isPartiallyRanked = treeNode.ranksPurchased ~= treeNode.maxRanks;
49+
local isChoiceNode = treeNode.type == Enum.TraitNodeType.Selection;
50+
51+
exportStream:AddValue(1, isNodeSelected and 1 or 0);
52+
if(isNodeSelected) then
53+
exportStream:AddValue(1, isPartiallyRanked and 1 or 0);
54+
if(isPartiallyRanked) then
55+
exportStream:AddValue(self.bitWidthRanksPurchased, treeNode.ranksPurchased);
56+
end
57+
58+
exportStream:AddValue(1, isChoiceNode and 1 or 0);
59+
if(isChoiceNode) then
60+
local entryIndex = self:GetActiveEntryIndex(treeNode);
61+
if(entryIndex <= 0 or entryIndex > 4) then
62+
error("Error exporting tree node " .. treeNode.ID .. ". The active choice node entry index (" .. entryIndex .. ") is out of bounds. ");
63+
end
64+
65+
-- store entry index as zero-index
66+
exportStream:AddValue(2, entryIndex - 1);
67+
end
68+
end
69+
end
70+
end
71+
72+
function ImportExport:GetActiveEntryIndex(treeNode)
73+
for i, entryID in ipairs(treeNode.entryIDs) do
74+
if(entryID == treeNode.activeEntry.entryID) then
75+
return i;
76+
end
77+
end
78+
79+
return 0;
80+
end
81+
82+
function ImportExport:ReadLoadoutContent(importStream, treeID)
83+
local results = {};
84+
85+
local treeNodes = C_Traits.GetTreeNodes(treeID);
86+
for i, _ in ipairs(treeNodes) do
87+
local nodeSelectedValue = importStream:ExtractValue(1)
88+
local isNodeSelected = nodeSelectedValue == 1;
89+
local isPartiallyRanked = false;
90+
local partialRanksPurchased = 0;
91+
local isChoiceNode = false;
92+
local choiceNodeSelection = 0;
93+
94+
if(isNodeSelected) then
95+
local isPartiallyRankedValue = importStream:ExtractValue(1);
96+
isPartiallyRanked = isPartiallyRankedValue == 1;
97+
if(isPartiallyRanked) then
98+
partialRanksPurchased = importStream:ExtractValue(self.bitWidthRanksPurchased);
99+
end
100+
local isChoiceNodeValue = importStream:ExtractValue(1);
101+
isChoiceNode = isChoiceNodeValue == 1;
102+
if(isChoiceNode) then
103+
choiceNodeSelection = importStream:ExtractValue(2);
104+
end
105+
end
106+
107+
local result = {};
108+
result.isNodeSelected = isNodeSelected;
109+
result.isPartiallyRanked = isPartiallyRanked;
110+
result.partialRanksPurchased = partialRanksPurchased;
111+
result.isChoiceNode = isChoiceNode;
112+
-- entry index is stored as zero-index, so convert back to lua index
113+
result.choiceNodeSelection = choiceNodeSelection + 1;
114+
results[i] = result;
115+
116+
end
117+
118+
return results;
119+
end
120+
121+
122+
function ImportExport:GetLoadoutExportString()
123+
local exportStream = ExportUtil.MakeExportDataStream();
124+
local configID = self:GetConfigID();
125+
local currentSpecID = self:GetSpecId();
126+
local treeId = self:GetTreeId();
127+
local treeHash = C_Traits.GetTreeHash(configID, treeId);
128+
129+
130+
self:WriteLoadoutHeader(exportStream, LOADOUT_SERIALIZATION_VERSION, currentSpecID, treeHash);
131+
self:WriteLoadoutContent(exportStream, configID, treeId);
132+
133+
return exportStream:GetExportString();
134+
end
135+
136+
function ImportExport:ShowImportError(errorString)
137+
StaticPopup_Show("TALENT_VIEWER_LOADOUT_IMPORT_ERROR_DIALOG", errorString);
138+
end
139+
140+
function ImportExport:ImportLoadout(importText)
141+
142+
local importStream = ExportUtil.MakeImportDataStream(importText);
143+
144+
local headerValid, serializationVersion, specID, treeHash = self:ReadLoadoutHeader(importStream);
145+
146+
if(not headerValid) then
147+
self:ShowImportError(LOADOUT_ERROR_BAD_STRING);
148+
return false;
149+
end
150+
151+
if(serializationVersion ~= LOADOUT_SERIALIZATION_VERSION) then
152+
self:ShowImportError(LOADOUT_ERROR_SERIALIZATION_VERSION_MISMATCH);
153+
return false;
154+
end
155+
156+
if(specID ~= self:GetSpecId()) then
157+
TalentViewer:SelectSpec(TalentViewer.cache.specIdToClassIdMap[specID], specID);
158+
end
159+
160+
local treeId = self:GetTreeId();
161+
162+
local loadoutContent = self:ReadLoadoutContent(importStream, treeId);
163+
local loadoutEntryInfo = self:ConvertToImportLoadoutEntryInfo(treeId, loadoutContent);
164+
165+
tmpEntryInfo = loadoutEntryInfo
166+
167+
local success = TalentViewer:GetTalentFrame():ImportLoadout(loadoutEntryInfo);
168+
if(not success) then
169+
self:ShowImportError(LOADOUT_ERROR_IMPORT_FAILED);
170+
return false;
171+
end
172+
173+
return true;
174+
end
175+
176+
function ImportExport:WriteLoadoutHeader(exportStream, serializationVersion, specID, treeHash)
177+
exportStream:AddValue(self.bitWidthHeaderVersion, serializationVersion);
178+
exportStream:AddValue(self.bitWidthSpecID, specID);
179+
-- treeHash is a 128bit hash, passed as an array of 16, 8-bit values
180+
for _, hashVal in ipairs(treeHash) do
181+
exportStream:AddValue(8, hashVal);
182+
end
183+
end
184+
185+
function ImportExport:ReadLoadoutHeader(importStream)
186+
local headerBitWidth = self.bitWidthHeaderVersion + self.bitWidthSpecID + 128;
187+
local importStreamTotalBits = importStream:GetNumberOfBits();
188+
if( importStreamTotalBits < headerBitWidth) then
189+
return false, 0, 0, 0;
190+
end
191+
local serializationVersion = importStream:ExtractValue(self.bitWidthHeaderVersion);
192+
local specID = importStream:ExtractValue(self.bitWidthSpecID);
193+
-- treeHash is a 128bit hash, passed as an array of 16, 8-bit values
194+
local treeHash = {};
195+
for i=1,16,1 do
196+
treeHash[i] = importStream:ExtractValue(8);
197+
end
198+
return true, serializationVersion, specID, treeHash;
199+
end
200+
201+
-- converts from compact bit-packing format to LoadoutEntryInfo format to pass to ImportLoadout API
202+
function ImportExport:ConvertToImportLoadoutEntryInfo(treeID, loadoutContent)
203+
local results = {};
204+
local treeNodes = C_Traits.GetTreeNodes(treeID);
205+
local count = 1;
206+
for i, treeNodeID in ipairs(treeNodes) do
207+
208+
local indexInfo = loadoutContent[i];
209+
210+
if (indexInfo.isNodeSelected) then
211+
local treeNode = getNodeInfo(treeNodeID);
212+
local result = {};
213+
result.nodeID = treeNode.ID;
214+
result.ranksPurchased = indexInfo.isPartiallyRanked and indexInfo.partialRanksPurchased or treeNode.maxRanks;
215+
result.selectionEntryID = indexInfo.isChoiceNode and treeNode.entryIDs[indexInfo.choiceNodeSelection] or treeNode.activeEntry.entryID;
216+
result.isChoiceNode = indexInfo.isChoiceNode;
217+
results[count] = result;
218+
count = count + 1;
219+
end
220+
221+
end
222+
223+
return results;
224+
end

0 commit comments

Comments
 (0)