C50BARZ commited on
Commit
cbe02bc
·
verified ·
1 Parent(s): e8b5ffa

please make a dark gray theme with pixel texture and please add more .nes rom editing features - Follow Up Deployment

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +768 -19
  3. prompts.txt +2 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Nes Rom Editor
3
- emoji: 📊
4
- colorFrom: blue
5
- colorTo: blue
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: nes-rom-editor
3
+ emoji: 🐳
4
+ colorFrom: yellow
5
+ colorTo: yellow
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,768 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>NES ROM Editor Tool</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js"></script>
9
+ <style>
10
+ .hex-cell {
11
+ cursor: pointer;
12
+ transition: all 0.2s;
13
+ }
14
+ .hex-cell:hover {
15
+ background-color: rgba(59, 130, 246, 0.2);
16
+ }
17
+ .hex-cell.selected {
18
+ background-color: rgba(59, 130, 246, 0.5);
19
+ color: white;
20
+ }
21
+ .ascii-cell {
22
+ font-family: monospace;
23
+ cursor: pointer;
24
+ transition: all 0.2s;
25
+ }
26
+ .ascii-cell:hover {
27
+ background-color: rgba(59, 130, 246, 0.2);
28
+ }
29
+ .ascii-cell.selected {
30
+ background-color: rgba(59, 130, 246, 0.5);
31
+ color: white;
32
+ }
33
+ .hex-editor {
34
+ height: 500px;
35
+ overflow-y: auto;
36
+ font-family: monospace;
37
+ }
38
+ /* Custom scrollbar */
39
+ .hex-editor::-webkit-scrollbar {
40
+ width: 8px;
41
+ }
42
+ .hex-editor::-webkit-scrollbar-track {
43
+ background: #f1f1f1;
44
+ }
45
+ .hex-editor::-webkit-scrollbar-thumb {
46
+ background: #888;
47
+ border-radius: 4px;
48
+ }
49
+ .hex-editor::-webkit-scrollbar-thumb:hover {
50
+ background: #555;
51
+ }
52
+ </style>
53
+ </head>
54
+ <body class="bg-gray-100">
55
+ <div class="container mx-auto px-4 py-8">
56
+ <header class="mb-8 text-center">
57
+ <h1 class="text-4xl font-bold text-blue-600 mb-2">
58
+ <i class="fas fa-gamepad mr-2"></i> NES ROM Editor
59
+ </h1>
60
+ <p class="text-gray-600">Edit NES ROM files directly in your browser</p>
61
+ </header>
62
+
63
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
64
+ <!-- File Upload Section -->
65
+ <div class="bg-white p-6 rounded-lg shadow-md lg:col-span-1">
66
+ <h2 class="text-xl font-semibold mb-4 flex items-center">
67
+ <i class="fas fa-file-upload mr-2 text-blue-500"></i> ROM File
68
+ </h2>
69
+ <div class="mb-4">
70
+ <label class="block text-sm font-medium text-gray-700 mb-2">Upload NES ROM</label>
71
+ <div class="flex items-center justify-center w-full">
72
+ <label class="flex flex-col w-full h-32 border-2 border-dashed hover:border-blue-500 transition-all rounded-lg cursor-pointer">
73
+ <div class="flex flex-col items-center justify-center pt-7">
74
+ <i class="fas fa-cloud-upload-alt text-3xl text-gray-400 mb-2"></i>
75
+ <p class="text-sm text-gray-500">Drag & drop your ROM file here</p>
76
+ <p class="text-xs text-gray-400">or click to browse</p>
77
+ </div>
78
+ <input type="file" id="romFile" class="opacity-0" accept=".nes" />
79
+ </label>
80
+ </div>
81
+ </div>
82
+
83
+ <div id="fileInfo" class="hidden mt-4 p-3 bg-blue-50 rounded-lg">
84
+ <div class="flex justify-between mb-2">
85
+ <span class="font-medium">Filename:</span>
86
+ <span id="fileName" class="text-blue-600"></span>
87
+ </div>
88
+ <div class="flex justify-between mb-2">
89
+ <span class="font-medium">Size:</span>
90
+ <span id="fileSize" class="text-blue-600"></span>
91
+ </div>
92
+ <div class="flex justify-between">
93
+ <span class="font-medium">ROM Type:</span>
94
+ <span id="romType" class="text-blue-600"></span>
95
+ </div>
96
+ </div>
97
+
98
+ <div class="mt-6">
99
+ <button id="downloadBtn" disabled class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-lg transition-colors disabled:bg-gray-300 disabled:cursor-not-allowed">
100
+ <i class="fas fa-download mr-2"></i> Save Edited ROM
101
+ </button>
102
+ </div>
103
+
104
+ <div class="mt-4 border-t pt-4">
105
+ <h3 class="text-sm font-medium text-gray-700 mb-2">Quick Actions</h3>
106
+ <button id="findTextBtn" disabled class="w-full mb-2 bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4 rounded transition-colors disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed">
107
+ <i class="fas fa-search mr-2"></i> Find Text
108
+ </button>
109
+ <button id="exportPRGBtn" disabled class="w-full mb-2 bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4 rounded transition-colors disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed">
110
+ <i class="fas fa-code mr-2"></i> Export PRG ROM
111
+ </button>
112
+ <button id="exportCHRBtn" disabled class="w-full bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4 rounded transition-colors disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed">
113
+ <i class="fas fa-palette mr-2"></i> Export CHR ROM
114
+ </button>
115
+ </div>
116
+ </div>
117
+
118
+ <!-- Hex Editor Section -->
119
+ <div class="bg-white p-6 rounded-lg shadow-md lg:col-span-2">
120
+ <h2 class="text-xl font-semibold mb-4 flex items-center">
121
+ <i class="fas fa-memory mr-2 text-blue-500"></i> Hex Editor
122
+ </h2>
123
+ <div class="flex justify-between items-center mb-4">
124
+ <div class="flex">
125
+ <button id="gotoBtn" disabled class="mr-2 bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-1 px-3 rounded text-sm transition-colors disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed">
126
+ <i class="fas fa-location-arrow mr-1"></i> Go to...
127
+ </button>
128
+ <input type="text" id="gotoInput" placeholder="0x8000" disabled class="w-24 px-2 py-1 border rounded text-sm disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed">
129
+ </div>
130
+ <div class="text-sm text-gray-500">
131
+ <span id="selectionInfo">No selection</span>
132
+ </div>
133
+ </div>
134
+
135
+ <div class="hex-editor border rounded-lg p-2 bg-gray-50" id="hexEditorContainer">
136
+ <div class="text-center text-gray-400 py-20">
137
+ <i class="fas fa-file-alt text-4xl mb-4"></i>
138
+ <p>Upload an NES ROM file to begin editing</p>
139
+ </div>
140
+ </div>
141
+
142
+ <div class="mt-4 grid grid-cols-1 md:grid-cols-2 gap-4">
143
+ <div>
144
+ <label class="block text-sm font-medium text-gray-700 mb-1">Selected Value (Hex)</label>
145
+ <input type="text" id="hexValueInput" disabled class="w-full px-3 py-2 border rounded bg-gray-100 text-gray-400 cursor-not-allowed">
146
+ </div>
147
+ <div>
148
+ <label class="block text-sm font-medium text-gray-700 mb-1">Selected Value (Dec)</label>
149
+ <input type="text" id="decValueInput" disabled class="w-full px-3 py-2 border rounded bg-gray-100 text-gray-400 cursor-not-allowed">
150
+ </div>
151
+ </div>
152
+ </div>
153
+ </div>
154
+
155
+ <!-- Text Editor Section -->
156
+ <div class="bg-white p-6 rounded-lg shadow-md mt-6">
157
+ <h2 class="text-xl font-semibold mb-4 flex items-center">
158
+ <i class="fas fa-font mr-2 text-blue-500"></i> Text Editor
159
+ </h2>
160
+ <div class="mb-4">
161
+ <label class="block text-sm font-medium text-gray-700 mb-1">String to Find</label>
162
+ <div class="flex">
163
+ <input type="text" id="searchText" disabled class="flex-grow px-3 py-2 border rounded-l disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed" placeholder="Enter text to search">
164
+ <button id="searchBtn" disabled class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-r transition-colors disabled:bg-gray-300 disabled:cursor-not-allowed">
165
+ <i class="fas fa-search"></i>
166
+ </button>
167
+ </div>
168
+ </div>
169
+ <div class="mb-4">
170
+ <label class="block text-sm font-medium text-gray-700 mb-1">Replacement Text</label>
171
+ <div class="flex">
172
+ <input type="text" id="replaceText" disabled class="flex-grow px-3 py-2 border rounded-l disabled:bg-gray-100 disabled:text-gray-400 disabled:cursor-not-allowed" placeholder="Enter replacement text">
173
+ <button id="replaceBtn" disabled class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-r transition-colors disabled:bg-gray-300 disabled:cursor-not-allowed">
174
+ <i class="fas fa-exchange-alt"></i>
175
+ </button>
176
+ </div>
177
+ </div>
178
+ <div class="border rounded-lg p-3 bg-gray-50 h-40 overflow-auto" id="textPreview">
179
+ <div class="text-center text-gray-400 py-10">
180
+ <i class="fas fa-file-alt text-2xl mb-2"></i>
181
+ <p>No ROM loaded to display text</p>
182
+ </div>
183
+ </div>
184
+ </div>
185
+
186
+ <!-- Status Bar -->
187
+ <div class="bg-gray-800 text-white p-2 rounded-b-lg text-sm mt-6 flex justify-between items-center">
188
+ <div class="flex items-center">
189
+ <i class="fas fa-info-circle mr-2 text-blue-300"></i>
190
+ <span id="statusMessage">Ready to upload NES ROM file</span>
191
+ </div>
192
+ <div id="loadingSpinner" class="hidden">
193
+ <i class="fas fa-spinner fa-spin mr-2"></i>
194
+ <span>Processing...</span>
195
+ </div>
196
+ </div>
197
+ </div>
198
+
199
+ <script>
200
+ // Global variables
201
+ let romData = null;
202
+ let selectedOffset = null;
203
+ let searchResults = [];
204
+
205
+ // DOM elements
206
+ const romFileInput = document.getElementById('romFile');
207
+ const downloadBtn = document.getElementById('downloadBtn');
208
+ const hexEditorContainer = document.getElementById('hexEditorContainer');
209
+ const fileInfo = document.getElementById('fileInfo');
210
+ const fileName = document.getElementById('fileName');
211
+ const fileSize = document.getElementById('fileSize');
212
+ const romType = document.getElementById('romType');
213
+ const gotoBtn = document.getElementById('gotoBtn');
214
+ const gotoInput = document.getElementById('gotoInput');
215
+ const selectionInfo = document.getElementById('selectionInfo');
216
+ const hexValueInput = document.getElementById('hexValueInput');
217
+ const decValueInput = document.getElementById('decValueInput');
218
+ const searchText = document.getElementById('searchText');
219
+ const searchBtn = document.getElementById('searchBtn');
220
+ const replaceText = document.getElementById('replaceText');
221
+ const replaceBtn = document.getElementById('replaceBtn');
222
+ const textPreview = document.getElementById('textPreview');
223
+ const statusMessage = document.getElementById('statusMessage');
224
+ const loadingSpinner = document.getElementById('loadingSpinner');
225
+ const findTextBtn = document.getElementById('findTextBtn');
226
+ const exportPRGBtn = document.getElementById('exportPRGBtn');
227
+ const exportCHRBtn = document.getElementById('exportCHRBtn');
228
+
229
+ // Helper functions
230
+ function formatHex(value, length) {
231
+ return value.toString(16).toUpperCase().padStart(length, '0');
232
+ }
233
+
234
+ function parseHex(hexString) {
235
+ return parseInt(hexString, 16);
236
+ }
237
+
238
+ function formatFileSize(bytes) {
239
+ if (bytes < 1024) return bytes + ' bytes';
240
+ else if (bytes < 1048576) return (bytes / 1024).toFixed(2) + ' KB';
241
+ else return (bytes / 1048576).toFixed(2) + ' MB';
242
+ }
243
+
244
+ function updateStatus(message) {
245
+ statusMessage.textContent = message;
246
+ }
247
+
248
+ function showLoading(show) {
249
+ if (show) {
250
+ loadingSpinner.classList.remove('hidden');
251
+ } else {
252
+ loadingSpinner.classList.add('hidden');
253
+ }
254
+ }
255
+
256
+ function enableControls(enable) {
257
+ const controls = [
258
+ downloadBtn, gotoBtn, gotoInput, searchText,
259
+ searchBtn, replaceText, replaceBtn, findTextBtn,
260
+ exportPRGBtn, exportCHRBtn
261
+ ];
262
+
263
+ controls.forEach(control => {
264
+ control.disabled = !enable;
265
+ });
266
+
267
+ if (enable) {
268
+ hexValueInput.disabled = false;
269
+ decValueInput.disabled = false;
270
+ hexValueInput.classList.remove('bg-gray-100', 'text-gray-400', 'cursor-not-allowed');
271
+ decValueInput.classList.remove('bg-gray-100', 'text-gray-400', 'cursor-not-allowed');
272
+ } else {
273
+ hexValueInput.disabled = true;
274
+ decValueInput.disabled = true;
275
+ hexValueInput.classList.add('bg-gray-100', 'text-gray-400', 'cursor-not-allowed');
276
+ decValueInput.classList.add('bg-gray-100', 'text-gray-400', 'cursor-not-allowed');
277
+ }
278
+ }
279
+
280
+ function renderHexEditor(data) {
281
+ if (!data) return;
282
+
283
+ showLoading(true);
284
+ updateStatus("Rendering hex editor...");
285
+
286
+ // Start rendering in chunks to avoid freezing the UI
287
+ setTimeout(() => {
288
+ const chunkSize = 4096; // bytes per chunk
289
+ let offset = 0;
290
+
291
+ hexEditorContainer.innerHTML = '';
292
+
293
+ // Create header
294
+ const header = document.createElement('div');
295
+ header.className = 'flex text-sm font-mono mb-1 sticky top-0 bg-gray-100 z-10';
296
+
297
+ // Address header
298
+ const addrHeader = document.createElement('div');
299
+ addrHeader.className = 'w-20 font-semibold text-gray-600';
300
+ addrHeader.textContent = 'Offset';
301
+ header.appendChild(addrHeader);
302
+
303
+ // Hex values header
304
+ for (let i = 0; i < 16; i++) {
305
+ const hexHeader = document.createElement('div');
306
+ hexHeader.className = 'w-6 text-center font-semibold text-gray-600';
307
+ hexHeader.textContent = formatHex(i, 2);
308
+ header.appendChild(hexHeader);
309
+ }
310
+
311
+ // ASCII header
312
+ const asciiHeader = document.createElement('div');
313
+ asciiHeader.className = 'w-64 ml-4 font-semibold text-gray-600';
314
+ asciiHeader.textContent = 'ASCII';
315
+ header.appendChild(asciiHeader);
316
+
317
+ hexEditorContainer.appendChild(header);
318
+
319
+ // Render chunks
320
+ function renderChunk() {
321
+ const fragment = document.createDocumentFragment();
322
+ const end = Math.min(offset + chunkSize, data.byteLength);
323
+
324
+ for (; offset < end; offset += 16) {
325
+ const row = document.createElement('div');
326
+ row.className = 'flex text-sm font-mono hover:bg-gray-50';
327
+
328
+ // Address
329
+ const addrCell = document.createElement('div');
330
+ addrCell.className = 'w-20 text-gray-500';
331
+ addrCell.textContent = formatHex(offset, 6);
332
+ row.appendChild(addrCell);
333
+
334
+ // Hex values
335
+ const hexRow = document.createElement('div');
336
+ hexRow.className = 'flex';
337
+
338
+ for (let i = 0; i < 16; i++) {
339
+ const pos = offset + i;
340
+ if (pos >= data.byteLength) break;
341
+
342
+ const value = data[pos];
343
+ const hexCell = document.createElement('div');
344
+ hexCell.className = 'hex-cell w-6 text-center';
345
+ hexCell.textContent = formatHex(value, 2);
346
+ hexCell.dataset.offset = pos;
347
+
348
+ hexCell.addEventListener('click', () => {
349
+ selectCell(pos);
350
+ });
351
+
352
+ hexRow.appendChild(hexCell);
353
+ }
354
+
355
+ row.appendChild(hexRow);
356
+
357
+ // ASCII representation
358
+ const asciiRow = document.createElement('div');
359
+ asciiRow.className = 'flex ml-4';
360
+
361
+ for (let i = 0; i < 16; i++) {
362
+ const pos = offset + i;
363
+ if (pos >= data.byteLength) break;
364
+
365
+ const value = data[pos];
366
+ let char = value >= 32 && value <= 126 ? String.fromCharCode(value) : '.';
367
+
368
+ const asciiCell = document.createElement('div');
369
+ asciiCell.className = 'ascii-cell w-4 text-center';
370
+ asciiCell.textContent = char;
371
+ asciiCell.dataset.offset = pos;
372
+
373
+ asciiCell.addEventListener('click', () => {
374
+ selectCell(pos);
375
+ });
376
+
377
+ asciiRow.appendChild(asciiCell);
378
+ }
379
+
380
+ row.appendChild(asciiRow);
381
+ fragment.appendChild(row);
382
+ }
383
+
384
+ hexEditorContainer.appendChild(fragment);
385
+
386
+ if (offset < data.byteLength) {
387
+ // Continue rendering in next animation frame
388
+ requestAnimationFrame(renderChunk);
389
+ } else {
390
+ showLoading(false);
391
+ updateStatus("ROM loaded successfully");
392
+
393
+ // Analyze ROM header
394
+ analyzeRomHeader(data);
395
+
396
+ // Update text preview
397
+ updateTextPreview(data);
398
+ }
399
+ }
400
+
401
+ requestAnimationFrame(renderChunk);
402
+ }, 100);
403
+ }
404
+
405
+ function selectCell(offset) {
406
+ // Clear previous selection
407
+ const prevSelected = document.querySelectorAll('.hex-cell.selected, .ascii-cell.selected');
408
+ prevSelected.forEach(el => el.classList.remove('selected'));
409
+
410
+ // Find and highlight new selection
411
+ const hexCells = document.querySelectorAll(`.hex-cell[data-offset="${offset}"]`);
412
+ const asciiCells = document.querySelectorAll(`.ascii-cell[data-offset="${offset}"]`);
413
+
414
+ hexCells.forEach(el => el.classList.add('selected'));
415
+ asciiCells.forEach(el => el.classList.add('selected'));
416
+
417
+ // Scroll into view
418
+ if (hexCells.length > 0) {
419
+ hexCells[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
420
+ }
421
+
422
+ // Update selection info
423
+ selectedOffset = offset;
424
+ selectionInfo.textContent = `Selected: 0x${formatHex(offset, 6)}`;
425
+
426
+ // Update value inputs
427
+ const value = romData[offset];
428
+ hexValueInput.value = formatHex(value, 2);
429
+ decValueInput.value = value.toString();
430
+
431
+ // Enable editing
432
+ hexValueInput.addEventListener('change', handleHexValueChange);
433
+ decValueInput.addEventListener('change', handleDecValueChange);
434
+ }
435
+
436
+ function handleHexValueChange(e) {
437
+ const hexValue = e.target.value.trim();
438
+ if (!hexValue) return;
439
+
440
+ const intValue = parseInt(hexValue, 16);
441
+ if (isNaN(intValue) || intValue < 0 || intValue > 255) {
442
+ alert('Please enter a valid hex value (00-FF)');
443
+ e.target.value = formatHex(romData[selectedOffset], 2);
444
+ return;
445
+ }
446
+
447
+ // Update ROM data
448
+ romData[selectedOffset] = intValue;
449
+
450
+ // Update dec value
451
+ decValueInput.value = intValue.toString();
452
+
453
+ // Update UI
454
+ const hexCells = document.querySelectorAll(`.hex-cell[data-offset="${selectedOffset}"]`);
455
+ const asciiCells = document.querySelectorAll(`.ascii-cell[data-offset="${selectedOffset}"]`);
456
+
457
+ hexCells.forEach(el => el.textContent = formatHex(intValue, 2));
458
+ asciiCells.forEach(el => el.textContent = intValue >= 32 && intValue <= 126 ? String.fromCharCode(intValue) : '.');
459
+
460
+ updateStatus(`Changed byte at 0x${formatHex(selectedOffset, 6)} to 0x${formatHex(intValue, 2)}`);
461
+ }
462
+
463
+ function handleDecValueChange(e) {
464
+ const decValue = e.target.value.trim();
465
+ if (!decValue) return;
466
+
467
+ const intValue = parseInt(decValue, 10);
468
+ if (isNaN(intValue) || intValue < 0 || intValue > 255) {
469
+ alert('Please enter a valid decimal value (0-255)');
470
+ e.target.value = romData[selectedOffset].toString();
471
+ return;
472
+ }
473
+
474
+ // Update ROM data
475
+ romData[selectedOffset] = intValue;
476
+
477
+ // Update hex value
478
+ hexValueInput.value = formatHex(intValue, 2);
479
+
480
+ // Update UI
481
+ const hexCells = document.querySelectorAll(`.hex-cell[data-offset="${selectedOffset}"]`);
482
+ const asciiCells = document.querySelectorAll(`.ascii-cell[data-offset="${selectedOffset}"]`);
483
+
484
+ hexCells.forEach(el => el.textContent = formatHex(intValue, 2));
485
+ asciiCells.forEach(el => el.textContent = intValue >= 32 && intValue <= 126 ? String.fromCharCode(intValue) : '.');
486
+
487
+ updateStatus(`Changed byte at 0x${formatHex(selectedOffset, 6)} to ${intValue}`);
488
+ }
489
+
490
+ function analyzeRomHeader(data) {
491
+ if (data.byteLength < 16) return;
492
+
493
+ // Check NES header
494
+ if (data[0] !== 0x4E || data[1] !== 0x45 || data[2] !== 0x53 || data[3] !== 0x1A) {
495
+ updateStatus("Warning: File may not be a valid NES ROM (missing header magic)", 'warning');
496
+ return;
497
+ }
498
+
499
+ // Get ROM info
500
+ const prgSize = data[4] * 16; // 16KB units
501
+ const chrSize = data[5] * 8; // 8KB units
502
+ const flags6 = data[6];
503
+ const flags7 = data[7];
504
+
505
+ // Determine mapper number
506
+ const mapper = (flags6 >> 4) | (flags7 & 0xF0);
507
+
508
+ // Determine mirroring type
509
+ const mirroring = (flags6 & 0x1) ? 'Vertical' : 'Horizontal';
510
+ const fourScreen = (flags6 & 0x8) ? ' (Four-screen)' : '';
511
+
512
+ // Determine console type
513
+ const nes2Format = (flags7 & 0x0C) === 0x08;
514
+ const consoleType = nes2Format ? 'NES 2.0' : 'iNES';
515
+
516
+ // Update info display
517
+ fileInfo.classList.remove('hidden');
518
+ romType.textContent = `${consoleType} ROM - Mapper ${mapper}`;
519
+
520
+ updateStatus(`Analyzed ROM: ${prgSize}KB PRG, ${chrSize}KB CHR, ${mirroring}${fourScreen} mirroring`);
521
+ }
522
+
523
+ function updateTextPreview(data) {
524
+ if (!data) return;
525
+
526
+ // Extract text from ROM (simple ASCII)
527
+ let text = '';
528
+ let inString = false;
529
+
530
+ for (let i = 16; i < data.byteLength; i++) { // Skip header
531
+ const byte = data[i];
532
+
533
+ if (byte >= 32 && byte <= 126) {
534
+ if (!inString) {
535
+ text += '\n'; // New line for new string
536
+ inString = true;
537
+ }
538
+ text += String.fromCharCode(byte);
539
+ } else {
540
+ inString = false;
541
+ }
542
+ }
543
+
544
+ // Display text
545
+ textPreview.innerHTML = '';
546
+ const pre = document.createElement('pre');
547
+ pre.className = 'whitespace-pre-wrap font-mono text-sm';
548
+ pre.textContent = text.trim();
549
+ textPreview.appendChild(pre);
550
+ }
551
+
552
+ function searchTextInRom() {
553
+ const searchString = searchText.value.trim();
554
+ if (!searchString || !romData) return;
555
+
556
+ showLoading(true);
557
+ updateStatus(`Searching for "${searchString}"...`);
558
+
559
+ searchResults = [];
560
+ const searchBytes = [];
561
+
562
+ // Convert search string to bytes
563
+ for (let i = 0; i < searchString.length; i++) {
564
+ searchBytes.push(searchString.charCodeAt(i));
565
+ }
566
+
567
+ // Search through ROM data
568
+ for (let i = 0; i <= romData.byteLength - searchBytes.length; i++) {
569
+ let match = true;
570
+
571
+ for (let j = 0; j < searchBytes.length; j++) {
572
+ if (romData[i + j] !== searchBytes[j]) {
573
+ match = false;
574
+ break;
575
+ }
576
+ }
577
+
578
+ if (match) {
579
+ searchResults.push(i);
580
+ }
581
+ }
582
+
583
+ showLoading(false);
584
+
585
+ if (searchResults.length > 0) {
586
+ // Highlight first match
587
+ selectCell(searchResults[0]);
588
+
589
+ updateStatus(`Found ${searchResults.length} occurrence(s) of "${searchString}"`);
590
+ } else {
591
+ updateStatus(`Text "${searchString}" not found in ROM`);
592
+ }
593
+ }
594
+
595
+ function replaceTextInRom() {
596
+ const searchStr = searchText.value.trim();
597
+ const replaceStr = replaceText.value.trim();
598
+
599
+ if (!searchStr || !replaceStr || searchResults.length === 0 || !romData) return;
600
+
601
+ if (searchStr.length !== replaceStr.length) {
602
+ alert('Search and replace strings must be the same length');
603
+ return;
604
+ }
605
+
606
+ showLoading(true);
607
+ updateStatus(`Replacing ${searchResults.length} occurrence(s)...`);
608
+
609
+ const replaceBytes = [];
610
+
611
+ // Convert replace string to bytes
612
+ for (let i = 0; i < replaceStr.length; i++) {
613
+ replaceBytes.push(replaceStr.charCodeAt(i));
614
+ }
615
+
616
+ // Perform replacements
617
+ searchResults.forEach(offset => {
618
+ for (let i = 0; i < replaceBytes.length; i++) {
619
+ romData[offset + i] = replaceBytes[i];
620
+ }
621
+ });
622
+
623
+ // Update UI
624
+ setTimeout(() => {
625
+ renderHexEditor(romData);
626
+ updateTextPreview(romData);
627
+ showLoading(false);
628
+ updateStatus(`Replaced ${searchResults.length} occurrence(s) of "${searchStr}" with "${replaceStr}"`);
629
+ }, 100);
630
+ }
631
+
632
+ function exportPRG() {
633
+ if (!romData || romData.byteLength < 16) return;
634
+
635
+ const prgSize = romData[4] * 16384; // 16KB pages
636
+ if (prgSize <= 0) return;
637
+
638
+ const prgStart = 16; // Skip header
639
+ const prgEnd = prgStart + prgSize;
640
+
641
+ const prgData = new Uint8Array(romData.slice(prgStart, prgEnd));
642
+ downloadData(prgData, 'prg_rom.bin');
643
+
644
+ updateStatus(`Exported ${formatFileSize(prgSize)} PRG ROM`);
645
+ }
646
+
647
+ function exportCHR() {
648
+ if (!romData || romData.byteLength < 16) return;
649
+
650
+ const prgSize = romData[4] * 16384; // 16KB pages
651
+ const chrSize = romData[5] * 8192; // 8KB pages
652
+ if (chrSize <= 0) return;
653
+
654
+ const chrStart = 16 + prgSize; // Skip header and PRG ROM
655
+ const chrEnd = chrStart + chrSize;
656
+
657
+ const chrData = new Uint8Array(romData.slice(chrStart, chrEnd));
658
+ downloadData(chrData, 'chr_rom.bin');
659
+
660
+ updateStatus(`Exported ${formatFileSize(chrSize)} CHR ROM`);
661
+ }
662
+
663
+ function downloadData(data, filename) {
664
+ const blob = new Blob([data], { type: 'application/octet-stream' });
665
+ const url = URL.createObjectURL(blob);
666
+
667
+ const a = document.createElement('a');
668
+ a.href = url;
669
+ a.download = filename;
670
+ document.body.appendChild(a);
671
+ a.click();
672
+
673
+ setTimeout(() => {
674
+ document.body.removeChild(a);
675
+ URL.revokeObjectURL(url);
676
+ }, 100);
677
+ }
678
+
679
+ // Event listeners
680
+ romFileInput.addEventListener('change', function(e) {
681
+ const file = e.target.files[0];
682
+ if (!file) return;
683
+
684
+ showLoading(true);
685
+ updateStatus(`Loading ${file.name}...`);
686
+
687
+ const reader = new FileReader();
688
+
689
+ reader.onload = function(e) {
690
+ try {
691
+ const arrayBuffer = e.target.result;
692
+ romData = new Uint8Array(arrayBuffer);
693
+
694
+ // Update file info
695
+ fileName.textContent = file.name;
696
+ fileSize.textContent = formatFileSize(file.size);
697
+
698
+ // Render editor
699
+ renderHexEditor(romData);
700
+ enableControls(true);
701
+ } catch (err) {
702
+ console.error(err);
703
+ updateStatus('Error loading ROM file', 'error');
704
+ showLoading(false);
705
+ }
706
+ };
707
+
708
+ reader.onerror = function() {
709
+ updateStatus('Error reading file', 'error');
710
+ showLoading(false);
711
+ };
712
+
713
+ reader.readAsArrayBuffer(file);
714
+ });
715
+
716
+ downloadBtn.addEventListener('click', function() {
717
+ if (!romData) return;
718
+
719
+ showLoading(true);
720
+ updateStatus("Preparing download...");
721
+
722
+ setTimeout(() => {
723
+ // Get filename from input or use default
724
+ let filename = fileName.textContent || 'edited_rom.nes';
725
+
726
+ // Ensure .nes extension
727
+ if (!filename.toLowerCase().endsWith('.nes')) {
728
+ filename += '.nes';
729
+ }
730
+
731
+ downloadData(romData, filename);
732
+ showLoading(false);
733
+ updateStatus(`Downloaded edited ROM as ${filename}`);
734
+ }, 100);
735
+ });
736
+
737
+ searchBtn.addEventListener('click', searchTextInRom);
738
+ replaceBtn.addEventListener('click', replaceTextInRom);
739
+
740
+ findTextBtn.addEventListener('click', function() {
741
+ searchText.focus();
742
+ });
743
+
744
+ exportPRGBtn.addEventListener('click', exportPRG);
745
+ exportCHRBtn.addEventListener('click', exportCHR);
746
+
747
+ gotoBtn.addEventListener('click', function() {
748
+ const offsetStr = gotoInput.value.trim();
749
+ if (!offsetStr || !romData) return;
750
+
751
+ let offset;
752
+
753
+ if (offsetStr.startsWith('0x')) {
754
+ offset = parseHex(offsetStr.substring(2));
755
+ } else {
756
+ offset = parseInt(offsetStr, 10);
757
+ }
758
+
759
+ if (isNaN(offset) || offset < 0 || offset >= romData.byteLength) {
760
+ alert('Invalid offset');
761
+ return;
762
+ }
763
+
764
+ selectCell(offset);
765
+ });
766
+ </script>
767
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://deepsite.hf.co/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://deepsite.hf.co" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://deepsite.hf.co?remix=C50BARZ/nes-rom-editor" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
768
+ </html>
prompts.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ please build a .nes rom editor tool for nintendo roms editing and then saving
2
+ please make a dark gray theme with pixel texture and please add more .nes rom editing features