Reaperxxxx commited on
Commit
a94f438
Β·
verified Β·
1 Parent(s): 143064c

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +67 -36
index.js CHANGED
@@ -61,8 +61,9 @@ function checkTools() {
61
  }
62
 
63
  // ── CHARACTER FILE HELPERS ────────────────────────────────────────────────────
64
- // characters.txt is one line: records separated by `3`, fields by `|`
65
- // We never send the raw blob to the frontend β€” all edits happen server-side.
 
66
 
67
  function sessionDir(token) {
68
  return token === 'persistent'
@@ -81,19 +82,63 @@ function readChars(token) {
81
  return fs.readFileSync(p, 'utf8');
82
  }
83
 
84
- // Write back to disk β€” never goes through the browser
85
  function writeChars(token, raw) {
86
  fs.writeFileSync(charsPath(token), raw, 'utf8');
87
  }
88
 
89
- // Parse raw string into array of field arrays
 
 
 
 
 
 
90
  function parseChars(raw) {
91
- return raw.split('3').map(r => r.split('|'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  }
93
 
94
- // Serialize back β€” exact inverse of parse, no newlines ever added
95
- function serializeChars(records) {
96
- return records.map(r => r.join('|')).join('3');
 
 
 
 
 
 
 
 
 
 
97
  }
98
 
99
  // Extract characters.txt from an APK and save it to the session dir
@@ -223,15 +268,9 @@ app.post('/upload-url', async (req, res) => {
223
  // The frontend NEVER sees the raw blob β€” only structured data it needs to render.
224
  app.get('/characters/:token', (req, res) => {
225
  try {
226
- const raw = readChars(req.params.token);
227
- const records = parseChars(raw);
228
- const list = records.map((fields, index) => ({
229
- index,
230
- name: fields[0] || '',
231
- alias: fields[1] || '',
232
- fields, // full field array so the UI can render all editable values
233
- }));
234
- res.json({ count: list.length, characters: list });
235
  } catch (err) {
236
  res.status(404).json({ error: err.message });
237
  }
@@ -245,15 +284,10 @@ app.post('/edit', (req, res) => {
245
  return res.status(400).json({ error: 'Missing token, characterIndex, fieldIndex, or value' });
246
  }
247
  try {
248
- const raw = readChars(token);
249
- const records = parseChars(raw);
250
- const ci = Number(characterIndex);
251
- const fi = Number(fieldIndex);
252
- if (!records[ci]) return res.status(400).json({ error: `Character index ${ci} out of range` });
253
- if (fi < 0 || fi >= records[ci].length) return res.status(400).json({ error: `Field index ${fi} out of range` });
254
- records[ci][fi] = String(value);
255
- writeChars(token, serializeChars(records));
256
- res.json({ ok: true, characterIndex: ci, fieldIndex: fi, value: records[ci][fi] });
257
  } catch (err) {
258
  res.status(500).json({ error: err.message });
259
  }
@@ -265,16 +299,13 @@ app.post('/edit-bulk', (req, res) => {
265
  const { token, edits } = req.body;
266
  if (!token || !Array.isArray(edits)) return res.status(400).json({ error: 'Missing token or edits array' });
267
  try {
268
- const raw = readChars(token);
269
- const records = parseChars(raw);
270
- for (const { characterIndex, fieldIndex, value } of edits) {
271
- const ci = Number(characterIndex);
272
- const fi = Number(fieldIndex);
273
- if (!records[ci]) throw new Error(`Character index ${ci} out of range`);
274
- if (fi < 0 || fi >= records[ci].length) throw new Error(`Field index ${fi} out of range`);
275
- records[ci][fi] = String(value);
276
- }
277
- writeChars(token, serializeChars(records));
278
  res.json({ ok: true, applied: edits.length });
279
  } catch (err) {
280
  res.status(500).json({ error: err.message });
 
61
  }
62
 
63
  // ── CHARACTER FILE HELPERS ────────────────────────────────────────────────────
64
+ // characters.txt is a single line of pipe-delimited fields.
65
+ // Character boundaries are detected by the pattern: name | alias | number
66
+ // (same logic as the frontend parseCharacters function).
67
 
68
  function sessionDir(token) {
69
  return token === 'persistent'
 
82
  return fs.readFileSync(p, 'utf8');
83
  }
84
 
85
+ // Write back to disk β€” raw flat pipe-delimited string, no newlines added
86
  function writeChars(token, raw) {
87
  fs.writeFileSync(charsPath(token), raw, 'utf8');
88
  }
89
 
90
+ // Mirror of the HTML's parseCharacters():
91
+ // Splits on '|', detects boundaries where: allFields[i-1]=name, allFields[i]=alias, allFields[i+1]=number
92
+ // Returns array of: { index, name, alias, fields, startIdx }
93
+ // - name: allFields[start - 1] (the token just before the boundary)
94
+ // - alias: fields[0] (first field inside the boundary = allFields[start])
95
+ // - fields: allFields[start .. next_boundary-2] (the character's own pipe segment)
96
+ // - startIdx: index into allFields where this character's fields begin
97
  function parseChars(raw) {
98
+ const allFields = raw.split('|');
99
+
100
+ function isNameToken(s) {
101
+ s = s.trim();
102
+ return s.length > 1 && /[A-Za-z]/.test(s) && !/^-?[\d.]+$/.test(s);
103
+ }
104
+ function isNumeric(s) { return /^-?[\d.]+$/.test(s.trim()); }
105
+
106
+ const boundaries = [];
107
+ for (let i = 1; i < allFields.length - 1; i++) {
108
+ if (isNameToken(allFields[i]) && isNumeric(allFields[i + 1]) && isNameToken(allFields[i - 1])) {
109
+ boundaries.push(i);
110
+ }
111
+ }
112
+
113
+ const characters = [];
114
+ for (let b = 0; b < boundaries.length; b++) {
115
+ const start = boundaries[b];
116
+ const end = b + 1 < boundaries.length ? boundaries[b + 1] - 1 : allFields.length;
117
+ const fields = allFields.slice(start, end);
118
+ characters.push({
119
+ index: b,
120
+ name: allFields[start - 1].trim(),
121
+ alias: fields[0].trim(),
122
+ fields, // fields[0]=alias, fields[1..]=numeric stats
123
+ startIdx: start, // position in allFields β€” needed for writeBack
124
+ });
125
+ }
126
+ return { allFields, characters };
127
  }
128
 
129
+ // Write edited characters back into the original allFields array and rejoin with '|'
130
+ // edits: [{ characterIndex, fieldIndex, value }]
131
+ function applyEdits(raw, edits) {
132
+ const { allFields, characters } = parseChars(raw);
133
+ for (const { characterIndex, fieldIndex, value } of edits) {
134
+ const ch = characters[characterIndex];
135
+ if (!ch) throw new Error(`Character index ${characterIndex} out of range`);
136
+ if (fieldIndex < 0 || fieldIndex >= ch.fields.length)
137
+ throw new Error(`Field index ${fieldIndex} out of range for character ${characterIndex}`);
138
+ // Write back into allFields at the correct absolute position
139
+ allFields[ch.startIdx + fieldIndex] = String(value);
140
+ }
141
+ return allFields.join('|');
142
  }
143
 
144
  // Extract characters.txt from an APK and save it to the session dir
 
268
  // The frontend NEVER sees the raw blob β€” only structured data it needs to render.
269
  app.get('/characters/:token', (req, res) => {
270
  try {
271
+ const raw = readChars(req.params.token);
272
+ const { characters } = parseChars(raw);
273
+ res.json({ count: characters.length, characters });
 
 
 
 
 
 
274
  } catch (err) {
275
  res.status(404).json({ error: err.message });
276
  }
 
284
  return res.status(400).json({ error: 'Missing token, characterIndex, fieldIndex, or value' });
285
  }
286
  try {
287
+ const raw = readChars(token);
288
+ const edited = applyEdits(raw, [{ characterIndex: Number(characterIndex), fieldIndex: Number(fieldIndex), value }]);
289
+ writeChars(token, edited);
290
+ res.json({ ok: true, characterIndex: Number(characterIndex), fieldIndex: Number(fieldIndex), value: String(value) });
 
 
 
 
 
291
  } catch (err) {
292
  res.status(500).json({ error: err.message });
293
  }
 
299
  const { token, edits } = req.body;
300
  if (!token || !Array.isArray(edits)) return res.status(400).json({ error: 'Missing token or edits array' });
301
  try {
302
+ const raw = readChars(token);
303
+ const edited = applyEdits(raw, edits.map(e => ({
304
+ characterIndex: Number(e.characterIndex),
305
+ fieldIndex: Number(e.fieldIndex),
306
+ value: e.value,
307
+ })));
308
+ writeChars(token, edited);
 
 
 
309
  res.json({ ok: true, applied: edits.length });
310
  } catch (err) {
311
  res.status(500).json({ error: err.message });