-
Notifications
You must be signed in to change notification settings - Fork 50
/
Copy pathFormatTable.lua
467 lines (358 loc) · 15.9 KB
/
FormatTable.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
--[[
Report any bugs, issues and detections to me if you don't mind (NoTwistedHere#6703)
]]
local Threading = loadstring(game:HttpGet("https://raw.githubusercontent.com/NoTwistedHere/Roblox/" .. (Branch or "main") .. "/Threading.lua"))()
local HttpService = game:GetService("HttpService")
local ObjectTypes = {
["nil"] = 1;
["boolean"] = 1;
["number"] = 1;
["string"] = 2;
["Instance"] = 3;
["userdata"] = 4;
["table"] = 4;
["function"] = 5;
["EnumItem"] = 6;
["Enum"] = 7;
["NumberSequenceKeypoint"] = 8;
["NumberSequence"] = 9;
}
local function CountTable(Table)
local Count = 0
for _ in next, Table do
Count += 1
end
return Count
end
local function Unrep(String)
local Counts = {}
for i, v in next, String:split("") do
if not Counts[v] then
Counts[v] = 0
end
Counts[v] += 1
end
for i, v in next, Counts do
if v > 100 then
local Subbed = false
String = String:gsub(i, function(C) if not Subbed then Subbed = true return C end return "" end)
continue
end
Counts[i] = nil
end
return String, CountTable(Counts) > 0
end
local function AntiRep(Options, String, ...) --// I'm too lazy
local Unicode = String:gsub("[^%a%c%d%l%p%s%u%x]", "")
if Options.NoAntiRep and #Unicode > 2e3 then
return Unicode, ...
end
return String, ...
end
local function ConvertCodepoints(OriginalString, Modified, Extra)
if OriginalString:match("[^%a%c%d%l%p%s%u%x]") then
--local Utf8String = "utf8.char("
--if not pcall(function() for i, v in utf8.codes(OriginalString) do Utf8String ..= ("%s%s"):format(i > 1 and ", " or "", v) end end) then
local String = ""
for i = 1, #OriginalString do
local Byte = string.byte(OriginalString, i, i)
if Byte <= 126 and Byte >= 33 then
String ..= string.char(Byte)
continue;
end
String ..= "\\" .. Byte
end
return "\"" .. String .. "\"", Extra and true or false
--[[end
if Extra then
return Utf8String .. ")", true
end
return Utf8String ..")"]]
end
return "\""..OriginalString.."\"", Extra and Modified > 0 or nil
end
local function IsService(Object)
local Success, Response = pcall(function()
return game:GetService(Object.ClassName)
end)
return Success and Response
end
local function Tostring(Object, One)
local Metatable = getrawmetatable(Object)
if Metatable and rawget(Metatable, "__tostring") then
local Old, IsReadOnly, Response = rawget(Metatable, "__tostring"), isreadonly(Metatable);
setreadonly(Metatable, false)
rawset(Metatable, "__tostring", nil)
Response = tostring(Object)
rawset(Metatable, "__tostring", Old)
setreadonly(Metatable, IsReadOnly)
if One then
return Response
end
return Response, " [Metatable]"
end
return tostring(Object)
end
local function Stringify(String, Options, Extra, Checked, Root)
local function Add(Message)
if Checked and Root then
Checked[String] = Message or Root
end
end
if type(String) ~= "string" then
return Tostring(String)
elseif Checked and Checked[String] then
return Checked[String]
end
if not Options.LargeStrings and #String > 5e3 then
local Message = ("\"String is larger than 5e3 (Very Large) #%s\""):format(HttpService:GenerateGUID())
Add(Message)
return Message
elseif #String > 2e2 then
Add()
end
if Options.RawStrings then
return "\"" .. String:gsub("\\", "\\\\"):gsub("\"", "\\\""):gsub("%c", function(Char) return "\\"..string.byte(Char) end) .. "\""
end
return ConvertCodepoints(AntiRep(Options, String:gsub("\\", "\\\\"):gsub("\"", "\\\""):gsub("%c", function(Char) return "\\"..string.byte(Char) end), Extra))
end
local function Convert(OriginalString)
local String = ""
local OriginalString = OriginalString:gsub("\\", "\\\\"):gsub("\"", "\\\""):gsub("%c", function(Char) return "\\"..string.byte(Char) end)
for i = 1, #OriginalString do
local Byte = string.byte(OriginalString, i, i)
if Byte <= 126 and Byte >= 33 then
String ..= string.char(Byte)
continue;
end
String ..= "\\" .. Byte
end
return String
end
local function GetName(Name)
if tonumber(Name:sub(1, 1)) or Name:match("([0-9a-zA-Z]*)") ~= Name then
if Name:match("\0") then
return (":FindFirstChild(\"%s\")"):format(Convert(Name))
end
return ("[\"%s\"]"):format(Convert(Name))
end
return (".%s"):format(Name)
end
local function GetPath(Object, Sub)
local Path = GetName(Object.Name):reverse()
local Parent = Object.Parent
if Object == game then
Path = ("game"):reverse()
elseif not Sub and IsService(Object) then
Path = (":GetService(\"%s\")"):format(Object.ClassName):reverse()
Path ..= GetPath(Parent, true)
elseif Parent == game then
Path = ("game"):reverse()
elseif Parent and IsService(Parent) then
Path ..= (":GetService(\"%s\")"):format(Parent.ClassName):reverse()
Path ..= GetPath(Parent, true)
elseif Parent then
Path ..= GetPath(Parent, true)
elseif Object ~= game then
Path ..= ("nil"):reverse()
end
if Sub then
return Path
end
return Path:reverse()
end
local ParseObject; ParseObject = function(Object, DetailedInfo, TypeOf, Checked, Root, Options, Indents)
local Type = typeof(Object)
local ObjectType = ObjectTypes[Type]
local function _Parse()
if ObjectType == 2 then
local String, Modified = Stringify(Object, Options, true, Checked, Root)
return String, Modified
elseif ObjectType == 3 then
--return Stringify(GetPath(Object), false, Checked, Root), " ClassName: "..Object.ClassName
return GetPath(Object), " ClassName: "..Object.ClassName
elseif ObjectType == 5 then
local Info = getinfo(Object)
return ("%s"):format(tostring(Object)), (" source: %s, what: %s, name: %s (currentline: %s, numparams: %s, nups: %s, is_vararg: %s)"):format(Stringify(Info.source, Options), Info.what, Stringify(Info.name, Options), Info.currentline, Info.numparams, Info.nups, Info.is_vararg)
elseif ObjectType == 6 then
return tostring(Object.Name)
elseif ObjectType == 7 then
return ("Enum."):format(tostring(Object))
elseif ObjectType == 8 then
return ("NumberSequenceKeypoint.new(%d, %d)"):format(Object.Time, Object.Value)
elseif ObjectType == 9 then
local Keypoints = "{\n"
for i, v in next, Object.Keypoints do
Keypoints ..= (" "):rep(Indents + 1) .. ParseObject(v, DetailedInfo, false, Checked, Root, Options) .. ("%s\n"):format(i == #Object.Keypoints and "" or ",")
end
return ("NumberSequence.new(%s)"):format(Keypoints .. (" "):rep(Indents) .. "}")
else
return Tostring(Object)
end
end
local Parsed = {_Parse()}
return Parsed[1], (TypeOf and ("[%s]"):format(Type) or "") .. (DetailedInfo and unpack(Parsed, 2) or "")
end
local function CheckForClone(Checked, Table)
local TC = CountTable(Table)
if TC <= 3 then
return false
end
for Value, Count in next, Checked do
if type(Value) == "table" and Count <= 3 or Count ~= TC then
continue;
end
local MatchedCount = 0
for Index, Value2 in next, Table do
if rawequal(rawget(Value, Index), Value2) then
MatchedCount += 1
end
end
if MatchedCount == Count then
return i
end
end
return false
end
local function IncrementalRepeat(ToRepeat, Repeat, Increment)
local Final = ""
for i = 1, Repeat do
Final ..= ToRepeat .. (Increment and i or "") .. (i == Repeat and "" or ", ")
end
return Final
end
local function YieldableFunc(OriginalFunction) --// Used to work, don't know if it still does
if syn and syn.oth then
return OriginalFunction
end
local NoUV = #getupvalues(OriginalFunction) + 1
local Variables, Values = IncrementalRepeat("_", NoUV, true), IncrementalRepeat("game", NoUV)
local New = newcclosure(loadstring(([[
local %s = %s
return function()
return %s
end]]):format(Variables, Values, Variables)))()
hookfunction(New, OriginalFunction)
return New
end
local function CreateComment(Comment, Options, Existing)
if Options.NoComments or Options.NoIndentation then
return ""
end
return (Existing and " // %s" or (Options.NoIndentation and " --[[ %s ]]" or " --// %s")):format(Comment)
end
local _FormatTable; _FormatTable = YieldableFunc(function(Table, Options, Indents, Checked, Root)
local Success, Response = xpcall(function()
if typeof(Table) ~= "table" and typeof(Table) ~= "userdata" then
return;
end
if Checked and Checked[Table] then
return ParseObject(Table, false, false, Checked, Root or "Table", Options, Indents)
end
Root = typeof(Root) == "string" and Root or "Table"
Checked = type(Checked) == "table" and Checked or {}
Indents = Options.NoIndentation and 1 or Indents or 1
local Metatable, IsProxy = getrawmetatable(Table), typeof(Table) == "userdata"
local TableCount, TabWidth, Count = IsProxy and 0 or CountTable(Table), Options.NoIndentation and " " or " ", 1
Checked[Table] = TableCount
if TableCount >= 3e3 and not Options.LargeTables then
return ("{ \"Table is too large\" }; --// Max: 5e3, Got: %d"):format(TableCount)
elseif IsProxy then
local Key, Comment = Options.MetatableKey, "";
if Key and Metatable and rawget(Metatable, Key) ~= nil then
Table = rawget(Metatable, Key)
Metatable = nil
Comment = CreateComment("Defined by caller", Options)
end
return (Metatable and "setmetatable(%s, %s)%s" or "%s%s%s"):format(Tostring(Table), Metatable and (_FormatTable(Metatable, Options, Indents, Checked, Root) or "PrintTable Error: An unexpected error occured") or Comment, Comment), Comment and "DBC" or false, Table
end
local NewTable, Results, Thread = {}, {}, Threading.new()
for i, v in next, Table do
if Options.NumLength and i == "#" then
continue;
end
table.insert(NewTable, {i, v})
end
if Options.NumLength and type(rawget(Table, "#")) == "number" and #Table ~= rawget(Table, "#") then
for i = #Table + 1, rawget(Table, "#") do
table.insert(NewTable, {i, nil})
end
end
for ThreadNum = 0, math.ceil(TableCount / 200) - 1 do
Thread:Add(function()
for TIndex = 1, 200 do
xpcall(function()
local Data = NewTable[TIndex + (200 * ThreadNum)]
if not Data then
return;
end
local Index, Value = Data[1], Data[2]
if Options.NumLength and Index == "#" then
return;
end
local NewRoot = Root..("[%s]"):format(Stringify(Index, Options))
local AlreadyLogged = type(Value) == "table" and (Checked[Value] or CheckForClone(Checked, Value))
local function Format(...)
local Arguments = {...}
if type(Index) == "number" and Options.IgnoreNumberIndex then
table.remove(Arguments, 2)
return ("%s%s%s%s"):format(unpack(Arguments))
end
return ("%s[%s] = %s%s%s"):format(...)
end
if AlreadyLogged then
Results[TIndex] = Format(string.rep(TabWidth, Indents), ParseObject(Index, false, false, Checked, NewRoot, Options, Indents), Tostring(Value), Count < TableCount and "," or "", CreateComment("[[ DUPLICATE ]]", Options))
Count += 1
return;
end
local IsValid = (type(Value) == "table" or typeof(Value) == "userdata") and not Checked[Value]
local ParsedValue, IsComment, ReParse = IsValid and _FormatTable(Value, Options, Indents + 1, Checked, NewRoot);
local Parsed = {ParseObject((IsComment == "DBC" or not ReParse) and Value or ReParse, true, true, Checked, NewRoot, Options, Indents)}
local Comment = ((IsComment == "DBC" or IsValid) and Parsed[1]..(Parsed[2] and " " or "") or "") .. (Parsed[2] and Parsed[2] or "")
Results[TIndex] = Format(string.rep(TabWidth, Indents), ParseObject(Index, false, false, Checked, NewRoot, Options, Indents), ParsedValue or Parsed[1], Count < TableCount and "," or "", #Comment > 0 and CreateComment(Comment or "", Options, IsComment))
Count += 1
end, function(e)
Results[TIndex] = ("FormatTable Error: [[\n%s\n%s]]"):format(e, debug.traceback())
end)
end
end)
end
if TableCount > 0 then
Thread.Ended:Wait("f")
end
if Options.GenerateScript then
if Root == "Table" then
local EndResult = ""
for i, v in next, Results do
EndResult ..= v:gsub("function: 0x([0-9a-f]+)", function(h) return ("FindFunction(\"%s\")"):format(h) end) .. (Options.OneLine and "" or "\n")
end
return {"local function FindFunction(k) for _, v in next, getgc() do if tostring(v) == \"function: 0x\" .. k then return v end end end\n", EndResult .. " "}
else
for i, v in next, Results do
Results[i] = v:gsub("function: 0x([0-9a-f]+)", function(h) return ("FindFunction(\"%s\")"):format(h) end)
end
end
end
return (Metatable and "setmetatable(%s, %s) --// %s" or "%s"):format(TableCount == 0 and "{}" or ("{%s%s%s%s}"):format(Options.OneLine and "" or "\n", table.concat(Results, Options.OneLine and "" or "\n"), Options.OneLine and " " or "\n", string.rep(TabWidth, Indents - 1)), Metatable and _FormatTable(Metatable, Options, Indents, Checked, Root), Tostring(Table) .. " // " .. Tostring(Metatable))
end, function(e)
return ("FormatTable Error: [[\n%s\n%s]]"):format(e, debug.traceback())
end)
return Response
end)
getgenv().FormatTable = YieldableFunc(function(Table, Options)
local Type = typeof(Table)
assert((Type == "table" or Type == "userdata"), "FormatTable Error: Invalid Argument #1 (table or userdata expected)")
assert((type(Options) == "table" or not Options), "FormatTable Error: Invalid Argument #2 (table expected)")
Options = Options or {}
local Success, Response = xpcall(_FormatTable, function(e)
return ("FormatTable Error: [[\n%s\n%s]]"):format(e, debug.traceback())
end, Table, Options, Options.Indents)
return Response
end)
getgenv().FormatArguments = YieldableFunc(function(...)
local Success, Response = xpcall(_FormatTable, function(e)
return ("FormatTable Error: [[\n%s\n%s]]"):format(e, debug.traceback())
end, {...}, {})
return Response
end)
return FormatTable