-
Notifications
You must be signed in to change notification settings - Fork 56
Expand file tree
/
Copy pathrelinker.js
More file actions
109 lines (99 loc) · 4.41 KB
/
relinker.js
File metadata and controls
109 lines (99 loc) · 4.41 KB
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
ngapp.service('relinker', function(scriptsCache, bsaHelpers, pexService, settingsService, mergeService, progressLogger, gameService) {
let {log, warn, progress} = progressLogger,
opcodes = require('pex-parser/src/opcodes.js');
const CALLSTATIC = opcodes.findByKey('name', 'callstatic');
let getMergedPlugins = function(merges) {
let mergedPlugins = {};
merges.forEach(merge => {
merge.plugins.forEach(({filename}) => {
mergedPlugins[filename] = merge.filename;
});
});
return mergedPlugins;
};
let getScriptsToRelink = function(merges) {
progress('Getting scripts to relink', true);
let cache = scriptsCache.update(),
mergedPlugins = getMergedPlugins(merges);
return cache.filter(entry => {
return !!entry.fileRefs.find(file => {
return mergedPlugins.hasOwnProperty(file);
});
});
};
let getScriptFilePath = function(entry) {
let basePath = `scripts\\${entry.filename}`;
return entry.bsa ? bsaHelpers.extractFile(entry.bsa, basePath) :
fh.path(gameService.dataPath, basePath);
};
let buildFormIdMap = function(merges) {
progress('Building Form ID map');
return merges.reduce((fidMap, merge) => {
log(`Loading FormID map for ${merge.name}`);
let mergeFolderPath = mergeService.getMergeFolder(merge),
path = fh.path(mergeFolderPath, 'map.json'),
map = fh.loadJsonFile(path);
if (!map) warn(`Form ID map for ${merge.name} not found at ${path}`);
return Object.assign(fidMap, map || {});
}, {});
};
let resolveString = function(script, arg) {
return script.stringTable[arg.data];
};
let fixGetFormCalls = function(script, fidMap) {
let functions = pexService.getFunctions(script);
functions.forEach(fn => {
fn.instructions.forEach(n => {
if (n.op !== CALLSTATIC.code) return;
let methodName = resolveString(script, n.arguments[1]);
if (methodName !== 'GetFormFromFile') return;
let filename = resolveString(script, n.arguments[5]);
log(`Found GetFormFromFile call targetting ${filename}`, true);
let mapping = cUtils.findValueInMapByLoweredKey(fidMap, filename);
if (!mapping) return;
let oldFormId = xelib.Hex(n.arguments[4].data, 6),
newFormId = mapping[oldFormId];
if (!newFormId) return log(`Form ID ${oldFormId} not renumbered`);
log(`Changing Form ID from ${oldFormId} to ${newFormId}`);
n.arguments[4].data = parseInt(newFormId, 16);
});
});
};
let fixStrings = function(script, merges) {
let mergedPlugins = getMergedPlugins(merges);
script.stringTable.forEach((str, index) => {
let newStr = cUtils.findValueInMapByLoweredKey(mergedPlugins, str);
if (!newStr) return;
log(`Changing string ${index} from ${str} to ${newStr}`);
script.stringTable[index] = newStr;
});
};
let relinkScripts = function(merges, scripts, fidMap, relinkerPath) {
progress(`Relinking ${scripts.length} scripts`);
scripts.forEach(entry => {
let filePath = getScriptFilePath(entry);
if (!filePath || !fh.jetpack.exists(filePath)) return;
log(`Relinking ${filePath}`);
let script = pexService.loadScript(filePath),
newPath = `${relinkerPath}\\scripts\\${entry.filename}`;
fixGetFormCalls(script, fidMap);
fixStrings(script, merges);
log(`Saving script to ${newPath}`);
pexService.saveScript(script, newPath);
});
};
this.run = function(merges) {
let mergePath = settingsService.settings.mergePath,
relinkerPath = `${mergePath}\\Relinker Output`;
pexService.setLogger(progressLogger);
progressLogger.run('relinker', relinkerPath, {
title: 'Relinking Scripts',
max: 3
}, () => {
let scripts = getScriptsToRelink(merges),
fidMap = buildFormIdMap(merges);
fh.jetpack.dir(`${relinkerPath}\\scripts`);
relinkScripts(merges, scripts, fidMap, relinkerPath);
});
};
});