rl-item-mod 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ async function runInteractiveWizard() {
24
24
  while (active) {
25
25
  console.clear();
26
26
  console.log('Welcome to RLItemMod, What would you like to do?');
27
- console.log('-----------------------------------------------------');
27
+ console.log('-------------------------------------------------');
28
28
  const { action } = await inquirer.prompt([{
29
29
  type: 'rawlist',
30
30
  name: 'action',
@@ -49,7 +49,7 @@ async function runInteractiveWizard() {
49
49
  default: global.COOKED_DIR || DEFAULT_COOKED_DIR
50
50
  }]);
51
51
  global.COOKED_DIR = newDir;
52
- console.log(` Game directory updated to: ${newDir}`);
52
+ console.log(`Game directory updated to: ${newDir}`);
53
53
  await inquirer.prompt([{ type: 'input', name: 'pause', message: 'Press Enter to continue...' }]);
54
54
  continue;
55
55
  }
@@ -78,18 +78,18 @@ async function runInteractiveWizard() {
78
78
  await inquirer.prompt([{ type: 'input', name: 'pause', message: 'Press Enter to continue...' }]);
79
79
  continue;
80
80
  }
81
- console.log(`\n Swapping ${target.item.name} for ${source.item.name}...`);
81
+ console.log(`\nSwapping ${target.item.name} for ${source.item.name}...`);
82
82
  const targetUpk = new UPKFile(target.path);
83
83
  try {
84
84
  const pythonScriptPath = path.resolve(__dirname, '../python/rl_asset_swapper.py');
85
85
  backupFile(target.path);
86
86
  const cmd = `python "${pythonScriptPath}" --no-gui --target "${target.item.name}" --donor "${source.item.name}" --no-preserve-header-offsets --overwrite --donor-dir "${cookedDir}" --output-dir "${cookedDir}"`;
87
- console.log(`\n Executing advanced Python offset-shifter...`);
87
+ console.log(`\nExecuting Python offset-shifter...`);
88
88
  execSync(cmd, { stdio: 'inherit' });
89
- console.log(' SUCCESS: Visual Swap complete! please restart your game and view your new item!');
89
+ console.log('SUCCESS: Visual Swap complete! Restart your game to see your new item.');
90
90
  }
91
91
  catch (e) {
92
- console.error(` Failed: ${e.message}`);
92
+ console.error(`Failed: ${e.message}`);
93
93
  }
94
94
  await inquirer.prompt([{ type: 'input', name: 'pause', message: 'Press Enter to continue...' }]);
95
95
  continue;
@@ -101,22 +101,22 @@ async function runInteractiveWizard() {
101
101
  const files = fs.readdirSync(cookedDir);
102
102
  const backups = files.filter(f => f.endsWith('.bak'));
103
103
  if (backups.length === 0) {
104
- console.log(' No backups found.');
104
+ console.log('No backups found.');
105
105
  }
106
106
  else {
107
107
  for (const bak of backups) {
108
108
  const original = bak.replace('.bak', '');
109
109
  const bakPath = path.join(cookedDir, bak);
110
110
  const originalPath = path.join(cookedDir, original);
111
- console.log(` Restoring ${original}...`);
111
+ console.log(`Restoring ${original}...`);
112
112
  fs.copyFileSync(bakPath, originalPath);
113
113
  fs.unlinkSync(bakPath);
114
114
  }
115
- console.log(' SUCCESS: All backups restored!');
115
+ console.log('SUCCESS: All backups restored.');
116
116
  }
117
117
  }
118
118
  catch (e) {
119
- console.error(` Failed: ${e.message}`);
119
+ console.error(`Failed: ${e.message}`);
120
120
  }
121
121
  await inquirer.prompt([{ type: 'input', name: 'pause', message: 'Press Enter to continue...' }]);
122
122
  continue;
@@ -134,7 +134,7 @@ async function promptForItemAndUPK(message, cookedDir) {
134
134
  }]);
135
135
  const matches = searchAssets(searchTerm);
136
136
  if (matches.length === 0) {
137
- console.error(` No items found matching "${searchTerm}".`);
137
+ console.error(`No items found matching "${searchTerm}".`);
138
138
  return null;
139
139
  }
140
140
  const { selectedItem } = await inquirer.prompt([{
@@ -149,14 +149,14 @@ async function promptForItemAndUPK(message, cookedDir) {
149
149
  const owned = selectedItem.productId.toString();
150
150
  const result = await resolvePackagePath(owned, cookedDir);
151
151
  if (!result) {
152
- console.error(` Could not resolve "${owned}".`);
152
+ console.error(`Could not resolve "${owned}".`);
153
153
  return null;
154
154
  }
155
155
  let finalUpkPath = '';
156
156
  if ('candidates' in result) {
157
157
  let currentCandidates = result.candidates;
158
158
  while (currentCandidates.length > 20) {
159
- console.log(` Found ${currentCandidates.length} potential matches.`);
159
+ console.log(`Found ${currentCandidates.length} potential matches.`);
160
160
  const { refine } = await inquirer.prompt([{
161
161
  type: 'input',
162
162
  name: 'refine',
@@ -183,27 +183,27 @@ async function promptForItemAndUPK(message, cookedDir) {
183
183
  }
184
184
  async function executePatch(upkPath, data, exportIndex) {
185
185
  try {
186
- console.log(` Patching ${upkPath}...`);
186
+ console.log(`Patching ${upkPath}...`);
187
187
  backupFile(upkPath);
188
188
  const upk = new UPKFile(upkPath);
189
189
  upk.readSummary();
190
190
  upk.readExportMap();
191
191
  if (exportIndex === -1) {
192
192
  exportIndex = upk.exports.reduce((maxIdx, curr, idx, arr) => curr.serialSize > arr[maxIdx].serialSize ? idx : maxIdx, 0);
193
- console.log(` Auto-selected Export[${exportIndex}] (Size: ${upk.exports[exportIndex].serialSize} bytes)`);
193
+ console.log(`Auto-selected Export[${exportIndex}] (Size: ${upk.exports[exportIndex].serialSize} bytes)`);
194
194
  }
195
195
  const newHex = (typeof data === 'string') ? fs.readFileSync(data) : data;
196
196
  upk.patchExport(exportIndex, newHex);
197
- console.log(' SUCCESS: Patch complete!');
197
+ console.log('SUCCESS: Patch complete.');
198
198
  }
199
199
  catch (error) {
200
- console.error(' CRITICAL FAILURE:', error.message);
200
+ console.error('CRITICAL FAILURE:', error.message);
201
201
  }
202
202
  }
203
203
  function backupFile(filePath) {
204
204
  const backupPath = `${filePath}.bak`;
205
205
  if (!fs.existsSync(backupPath)) {
206
- console.log(` Creating backup: ${path.basename(backupPath)}`);
206
+ console.log(`Creating backup: ${path.basename(backupPath)}`);
207
207
  fs.copyFileSync(filePath, backupPath);
208
208
  }
209
209
  }
@@ -234,7 +234,7 @@ program
234
234
  let upkPath = options.upk;
235
235
  let cookedDir = options.dir;
236
236
  if (!fs.existsSync(cookedDir)) {
237
- console.error(` Error: CookedPCConsole directory not found at: ${cookedDir}`);
237
+ console.error(`Error: CookedPCConsole directory not found at: ${cookedDir}`);
238
238
  console.log('Use --dir <path> to specify the correct game directory.');
239
239
  return;
240
240
  }
@@ -244,14 +244,14 @@ program
244
244
  upkPath = res.path;
245
245
  }
246
246
  if (!upkPath) {
247
- console.error(` Error: Could not resolve item "${options.owned}" in ${cookedDir}.`);
247
+ console.error(`Error: Could not resolve item "${options.owned}" in ${cookedDir}.`);
248
248
  return;
249
249
  }
250
250
  await executePatch(upkPath, options.target, options.export ? parseInt(options.export) : -1);
251
251
  });
252
252
  // Handle Ctrl+C gracefully
253
253
  process.on('SIGINT', () => {
254
- console.log('\n Exiting RLItemMod. See you next time!');
254
+ console.log('\nExiting RLItemMod.');
255
255
  process.exit(0);
256
256
  });
257
257
  async function runSafeWizard() {
@@ -260,10 +260,10 @@ async function runSafeWizard() {
260
260
  }
261
261
  catch (e) {
262
262
  if (e.name === 'ExitPromptError') {
263
- console.log('\n Goodbye!');
263
+ console.log('\nGoodbye!');
264
264
  }
265
265
  else {
266
- console.error('\n An unexpected error occurred:', e.message);
266
+ console.error('\nAn unexpected error occurred:', e.message);
267
267
  }
268
268
  process.exit(0);
269
269
  }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "rl-item-mod",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "description": "A comprehensive CLI tool for safely applying visual asset swaps to Rocket League UPK files with full encryption and binary offset handling.",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
- "rl-item-mod": "./dist/index.js"
8
+ "rl-item-mod": "dist/index.js"
9
9
  },
10
10
  "files": [
11
11
  "dist",
@@ -13,7 +13,7 @@
13
13
  ],
14
14
  "scripts": {
15
15
  "build": "tsc",
16
- "start": "ts-node index.ts",
16
+ "start": "ts-node src/index.ts",
17
17
  "test": "echo \"Error: no test specified\" && exit 1"
18
18
  },
19
19
  "keywords": [
@@ -1,33 +0,0 @@
1
- import { UPKFile } from '../dist/upk.js';
2
- const upk = new UPKFile('E:\\games\\rocketleague\\TAGame\\CookedPCConsole\\explosion_badaboom_SF.upk');
3
- try {
4
- upk.readSummary();
5
- const r = upk.dataBuffer ? new (require('../dist/upk.js').BinaryReader)(upk.dataBuffer) : null;
6
- if (r) {
7
- r.pos = upk.summary.exportOffset;
8
- for (let i = 0; i < upk.summary.exportCount; i++) {
9
- const classIndex = r.readI32();
10
- const superIndex = r.readI32();
11
- const outerIndex = r.readI32();
12
- const objectNameIndex = r.readI32();
13
- const objectNameNumber = r.readI32();
14
- const archetypeIndex = r.readI32();
15
- const objectFlags = r.readU64();
16
- const serialSize = r.readI32();
17
- const serialOffset = r.readI32();
18
- const exportFlags = r.readI32();
19
- const netObjCount = r.readI32();
20
- console.log(`Export ${i}: NetObjCount = ${netObjCount}, pos before skip: ${r.pos}`);
21
- if (netObjCount < 0 || netObjCount > 1000) {
22
- console.log(`Abnormal netObjCount: ${netObjCount} at export ${i}`);
23
- break;
24
- }
25
- r.skip(netObjCount * 4);
26
- r.skip(16); // PackageGuid
27
- r.readI32(); // PackageFlags
28
- }
29
- }
30
- }
31
- catch (e) {
32
- console.error(e);
33
- }
package/dist/swapper.js DELETED
@@ -1,81 +0,0 @@
1
- /**
2
- * Implements the "Fixed Rename" logic from RLUPKTools.
3
- * This renames a name table entry IN-PLACE by padding with nulls.
4
- * This ensures the header size and all subsequent offsets remain identical.
5
- */
6
- export class UPKSwapper {
7
- upk;
8
- constructor(upk) {
9
- this.upk = upk;
10
- }
11
- /**
12
- * Renames an existing name entry to a new name.
13
- * The new name must be shorter than or equal to the original name's space.
14
- */
15
- fixedRename(oldName, newName) {
16
- if (!this.upk.summary || !this.upk.getData())
17
- return false;
18
- const data = this.upk.getData();
19
- const nameOffset = this.upk.summary.nameOffset;
20
- let pos = nameOffset;
21
- for (let i = 0; i < this.upk.summary.nameCount; i++) {
22
- const entryStart = pos;
23
- const length = data.readInt32LE(pos);
24
- pos += 4;
25
- let currentName;
26
- let totalBytes;
27
- if (length > 0) {
28
- // ANSI
29
- currentName = data.toString('utf8', pos, pos + length - 1);
30
- totalBytes = length;
31
- pos += length;
32
- }
33
- else if (length < 0) {
34
- // UTF-16
35
- const absLen = Math.abs(length) * 2;
36
- currentName = data.toString('utf16le', pos, pos + absLen - 2);
37
- totalBytes = absLen;
38
- pos += absLen;
39
- }
40
- else {
41
- pos += 8; // Skip flags
42
- continue;
43
- }
44
- // Skip ObjectFlags (8 bytes)
45
- pos += 8;
46
- if (currentName === oldName) {
47
- console.log(` Found name entry "${oldName}" at index ${i}. Renaming to "${newName}"...`);
48
- // Prepare the new string
49
- const newBuf = Buffer.alloc(totalBytes, 0);
50
- if (length > 0) {
51
- newBuf.write(newName, 'utf8');
52
- }
53
- else {
54
- newBuf.write(newName, 'utf16le');
55
- }
56
- // Write it back
57
- newBuf.copy(data, entryStart + 4);
58
- return true;
59
- }
60
- }
61
- return false;
62
- }
63
- /**
64
- * Swaps all references of donorName to targetName in the package.
65
- * This is useful for "tricking" the engine into loading one asset instead of another.
66
- */
67
- swapAssetReferences(targetAssetName, donorAssetName) {
68
- console.log(` Swapping references: ${targetAssetName} <-> ${donorAssetName}`);
69
- // We actually want to rename the TARGET name to something else,
70
- // and the DONOR name (which we might have imported) to the TARGET name.
71
- // But for a simple swap where we just patch the file, we can just rename
72
- // the internal package name.
73
- const success = this.fixedRename(targetAssetName, donorAssetName);
74
- if (success) {
75
- console.log(` Successfully swapped ${targetAssetName} for ${donorAssetName} references.`);
76
- }
77
- else {
78
- console.warn(` Failed to find name entry for ${targetAssetName}.`);
79
- }
80
- }
81
- }