Skip to content

Commit 10c4107

Browse files
committed
Add opcode diff applier web page
1 parent 8cf826a commit 10c4107

1 file changed

Lines changed: 263 additions & 0 deletions

File tree

‎gh-pages/index.html‎

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
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>Opcode Diff Applier</title>
7+
<style>
8+
body {
9+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
10+
line-height: 1.6;
11+
max-width: 1200px;
12+
margin: 0 auto;
13+
padding: 20px;
14+
background-color: #f5f5f5;
15+
}
16+
.container {
17+
background-color: #fff;
18+
padding: 20px;
19+
border-radius: 8px;
20+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
21+
}
22+
.controls {
23+
margin-bottom: 20px;
24+
display: flex;
25+
gap: 10px;
26+
align-items: center;
27+
flex-wrap: wrap;
28+
}
29+
.textareas {
30+
display: flex;
31+
gap: 20px;
32+
}
33+
.textarea-group {
34+
flex: 1;
35+
display: flex;
36+
flex-direction: column;
37+
}
38+
textarea {
39+
height: 500px;
40+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
41+
font-size: 13px;
42+
padding: 10px;
43+
border: 1px solid #ccc;
44+
border-radius: 4px;
45+
resize: vertical;
46+
}
47+
label {
48+
font-weight: bold;
49+
margin-bottom: 5px;
50+
}
51+
button {
52+
padding: 10px 20px;
53+
background-color: #007bff;
54+
color: white;
55+
border: none;
56+
border-radius: 4px;
57+
cursor: pointer;
58+
}
59+
button:hover {
60+
background-color: #0056b3;
61+
}
62+
button:disabled {
63+
background-color: #ccc;
64+
}
65+
select {
66+
padding: 8px;
67+
border-radius: 4px;
68+
border: 1px solid #ccc;
69+
}
70+
</style>
71+
</head>
72+
<body>
73+
<div class="container">
74+
<h1>Opcode Diff Applier</h1>
75+
<div class="controls">
76+
<div>
77+
<label for="diff-select">Select Diff:</label>
78+
<select id="diff-select">
79+
<option value="">Loading diffs...</option>
80+
</select>
81+
</div>
82+
<button id="apply-btn" disabled>Apply Diff</button>
83+
</div>
84+
<div class="textareas">
85+
<div class="textarea-group">
86+
<label for="old-content" id="old-label">Old File Content:</label>
87+
<textarea id="old-content" placeholder="Paste content from C-style IPCs file or ACT format here..."></textarea>
88+
</div>
89+
<div class="textarea-group">
90+
<label for="new-content" id="new-label">New File Content:</label>
91+
<textarea id="new-content" readonly placeholder="Result will appear here..."></textarea>
92+
</div>
93+
</div>
94+
</div>
95+
96+
<script>
97+
const REPO = 'xivdev/opcodediff';
98+
const DIFFS_DIR = 'diffs';
99+
const diffSelect = document.getElementById('diff-select');
100+
const applyBtn = document.getElementById('apply-btn');
101+
const oldContent = document.getElementById('old-content');
102+
const newContent = document.getElementById('new-content');
103+
const oldLabel = document.getElementById('old-label');
104+
const newLabel = document.getElementById('new-label');
105+
106+
let loadedDiffs = {};
107+
let diffFilesList = [];
108+
109+
async function fetchDiffList() {
110+
try {
111+
const response = await fetch(`https://api.github.com/repos/${REPO}/contents/${DIFFS_DIR}`);
112+
const files = await response.json();
113+
114+
diffFilesList = files
115+
.filter(f => f.name.endsWith('.diff.json'))
116+
.sort((a, b) => {
117+
return b.name.localeCompare(a.name, undefined, { numeric: true, sensitivity: 'base' });
118+
});
119+
120+
diffSelect.innerHTML = '<option value="">-- Choose a version --</option>';
121+
diffFilesList.forEach((file, index) => {
122+
const option = document.createElement('option');
123+
option.value = file.download_url;
124+
option.dataset.index = index;
125+
option.textContent = file.name;
126+
diffSelect.appendChild(option);
127+
});
128+
} catch (error) {
129+
console.error('Error fetching diff list:', error);
130+
diffSelect.innerHTML = '<option value="">Error loading diffs</option>';
131+
}
132+
}
133+
134+
diffSelect.addEventListener('change', async () => {
135+
if (!diffSelect.value) {
136+
applyBtn.disabled = true;
137+
oldLabel.textContent = 'Old File Content:';
138+
newLabel.textContent = 'New File Content:';
139+
return;
140+
}
141+
applyBtn.disabled = false;
142+
143+
const selectedOption = diffSelect.options[diffSelect.selectedIndex];
144+
const currentIndex = parseInt(selectedOption.dataset.index);
145+
const newVersion = selectedOption.text.replace('.diff.json', '');
146+
147+
// The list is sorted descending (newest first).
148+
// So the 'previous' version in chronological order is the one AFTER it in the list.
149+
let oldVersion = 'Old';
150+
if (currentIndex < diffFilesList.length - 1) {
151+
oldVersion = diffFilesList[currentIndex + 1].name.replace('.diff.json', '');
152+
}
153+
154+
oldLabel.textContent = `Old File Content (${oldVersion}):`;
155+
newLabel.textContent = `New File Content (${newVersion}):`;
156+
157+
// Store oldVersion for use in applyDiff
158+
applyBtn.dataset.oldVersion = oldVersion;
159+
});
160+
161+
applyBtn.addEventListener('click', async () => {
162+
const diffUrl = diffSelect.value;
163+
const diffName = diffSelect.options[diffSelect.selectedIndex].text;
164+
const newVersion = diffName.replace('.diff.json', '');
165+
const oldVersion = applyBtn.dataset.oldVersion;
166+
167+
try {
168+
applyBtn.disabled = true;
169+
applyBtn.textContent = 'Applying...';
170+
171+
let diffData;
172+
if (loadedDiffs[diffUrl]) {
173+
diffData = loadedDiffs[diffUrl];
174+
} else {
175+
const response = await fetch(diffUrl);
176+
diffData = await response.json();
177+
loadedDiffs[diffUrl] = diffData;
178+
}
179+
180+
const result = applyDiff(oldContent.value, diffData, newVersion, oldVersion);
181+
newContent.value = result;
182+
} catch (error) {
183+
console.error('Error applying diff:', error);
184+
alert('Error applying diff. See console for details.');
185+
} finally {
186+
applyBtn.disabled = false;
187+
applyBtn.textContent = 'Apply Diff';
188+
}
189+
});
190+
191+
function getNormalizedHex(val) {
192+
if (!val) return '';
193+
return val.toLowerCase().startsWith('0x') ? val.substring(2).toLowerCase() : val.toLowerCase();
194+
}
195+
196+
function createDiffMap(diff) {
197+
const diffMap = new Map();
198+
diff.forEach(entry => {
199+
const oldVal = entry.old[0];
200+
const newVal = entry.new[0];
201+
if (oldVal && newVal) {
202+
diffMap.set(getNormalizedHex(oldVal), getNormalizedHex(newVal));
203+
}
204+
});
205+
return diffMap;
206+
}
207+
208+
function processEnumLine(line, diffMap, oldVersion, newVersion) {
209+
const enumRegex = /^(\s*\w+\s*=\s*)(0x[0-9a-fA-F]+|[0-9a-fA-F]+)([\s,;]*)/;
210+
const match = line.match(enumRegex);
211+
if (!match) return null;
212+
213+
const [fullMatch, prefix, valStr, suffix] = match;
214+
215+
// Only update if it has the specific "updated OLD_VERSION" comment
216+
const versionPattern = new RegExp(`// updated\\s+${oldVersion.replace('.', '\\.')}`);
217+
if (!versionPattern.test(line)) return null;
218+
219+
const newVal = diffMap.get(getNormalizedHex(valStr));
220+
if (!newVal) return null;
221+
222+
const formattedNewVal = valStr.toLowerCase().startsWith('0x') ? '0x' + newVal : newVal;
223+
let updatedLine = line.replace(prefix + valStr + suffix, prefix + formattedNewVal + suffix);
224+
225+
// Update the version comment
226+
return updatedLine.replace(/\/\/ updated\s+(\d+\.\d+\w*)/, `// updated ${newVersion}`);
227+
}
228+
229+
function processPipeLine(line, diffMap) {
230+
const pipeRegex = /^([^|]+\|)([0-9a-fA-F]+)$/;
231+
const match = line.match(pipeRegex);
232+
if (!match) return null;
233+
234+
const [fullMatch, prefix, val] = match;
235+
const newVal = diffMap.get(getNormalizedHex(val));
236+
if (!newVal) return null;
237+
238+
return prefix + newVal;
239+
}
240+
241+
function applyDiff(text, diff, newVersion, oldVersion) {
242+
const diffMap = createDiffMap(diff);
243+
const lines = text.split('\n');
244+
245+
const resultLines = lines.map(line => {
246+
// Try Enum format first
247+
const updatedEnum = processEnumLine(line, diffMap, oldVersion, newVersion);
248+
if (updatedEnum !== null) return updatedEnum;
249+
250+
// Try Pipe format
251+
const updatedPipe = processPipeLine(line, diffMap);
252+
if (updatedPipe !== null) return updatedPipe;
253+
254+
return line;
255+
});
256+
257+
return resultLines.join('\n');
258+
}
259+
260+
fetchDiffList();
261+
</script>
262+
</body>
263+
</html>

0 commit comments

Comments
 (0)