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