Last active
June 20, 2025 10:20
-
-
Save PyYoshi/e9c31855d4c1db8c89108c05ba28a2f7 to your computer and use it in GitHub Desktop.
Macropad EEPROM キーコード変換ツール https://github.com/MrGeorgeK55/Macropad-3-keys-1-knob
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="ja"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Macropad EEPROM キーコード変換ツール</title> | |
| <style> | |
| body { | |
| font-family: Arial, sans-serif; | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| background-color: #f5f5f5; | |
| } | |
| h1 { | |
| color: #333; | |
| text-align: center; | |
| } | |
| .container { | |
| background: white; | |
| padding: 20px; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
| margin-bottom: 20px; | |
| } | |
| .key-config { | |
| border: 1px solid #ddd; | |
| padding: 15px; | |
| margin: 10px 0; | |
| border-radius: 5px; | |
| background: #fafafa; | |
| } | |
| .key-config h3 { | |
| margin-top: 0; | |
| color: #555; | |
| } | |
| label { | |
| display: inline-block; | |
| width: 120px; | |
| font-weight: bold; | |
| color: #666; | |
| } | |
| select, input[type="number"], input[type="text"] { | |
| margin: 5px; | |
| padding: 5px; | |
| border: 1px solid #ccc; | |
| border-radius: 3px; | |
| } | |
| select { | |
| width: 200px; | |
| } | |
| input[type="number"] { | |
| width: 60px; | |
| } | |
| .macro-keys { | |
| margin-top: 10px; | |
| padding: 10px; | |
| background: #f0f0f0; | |
| border-radius: 3px; | |
| } | |
| .output { | |
| font-family: monospace; | |
| background: #222; | |
| color: #0f0; | |
| padding: 15px; | |
| border-radius: 5px; | |
| white-space: pre; | |
| overflow-x: auto; | |
| } | |
| button { | |
| background: #4CAF50; | |
| color: white; | |
| padding: 10px 20px; | |
| border: none; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| font-size: 16px; | |
| margin: 10px 5px; | |
| } | |
| button:hover { | |
| background: #45a049; | |
| } | |
| .info { | |
| background: #e3f2fd; | |
| padding: 10px; | |
| border-radius: 5px; | |
| margin: 10px 0; | |
| font-size: 14px; | |
| } | |
| .modifier-info { | |
| font-size: 12px; | |
| color: #666; | |
| margin-top: 5px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div style="text-align: center; margin-bottom: 20px;"> | |
| <button onclick="toggleLanguage()" style="background: #2196F3; font-size: 14px; padding: 5px 15px;">🌐 English/日本語</button> | |
| </div> | |
| <h1 id="main-title">Macropad EEPROM キーコード変換ツール</h1> | |
| <div class="container"> | |
| <h2 id="config-title">キー設定</h2> | |
| <div class="info" id="config-info"> | |
| 各キー(3個のキー + エンコーダースイッチ + エンコーダー回転CW/CCW)の動作を設定します。 | |
| トグル機能を使用すると、1つのキーで2つの異なる動作を切り替えることができます。 | |
| </div> | |
| <!-- Key 1 --> | |
| <div class="key-config" id="key1-container"> | |
| <h3>キー 1</h3> | |
| <label>キータイプ:</label> | |
| <select id="key1-type" onchange="updateKeyType(1)"> | |
| <option value="0">キーボードキー</option> | |
| <option value="1">コンシューマーキー</option> | |
| <option value="2">マクロキー</option> | |
| </select> | |
| <div id="key1-config"></div> | |
| </div> | |
| <!-- Key 2 --> | |
| <div class="key-config" id="key2-container"> | |
| <h3>キー 2</h3> | |
| <label>キータイプ:</label> | |
| <select id="key2-type" onchange="updateKeyType(2)"> | |
| <option value="0">キーボードキー</option> | |
| <option value="1">コンシューマーキー</option> | |
| <option value="2">マクロキー</option> | |
| </select> | |
| <div id="key2-config"></div> | |
| </div> | |
| <!-- Key 3 --> | |
| <div class="key-config" id="key3-container"> | |
| <h3>キー 3</h3> | |
| <label>キータイプ:</label> | |
| <select id="key3-type" onchange="updateKeyType(3)"> | |
| <option value="0">キーボードキー</option> | |
| <option value="1">コンシューマーキー</option> | |
| <option value="2">マクロキー</option> | |
| </select> | |
| <div id="key3-config"></div> | |
| </div> | |
| <!-- Encoder Switch --> | |
| <div class="key-config" id="key4-container"> | |
| <h3>エンコーダースイッチ</h3> | |
| <label>キータイプ:</label> | |
| <select id="key4-type" onchange="updateKeyType(4)"> | |
| <option value="0">キーボードキー</option> | |
| <option value="1">コンシューマーキー</option> | |
| <option value="2">マクロキー</option> | |
| </select> | |
| <div id="key4-config"></div> | |
| </div> | |
| <!-- Encoder CW --> | |
| <div class="key-config" id="key5-container"> | |
| <h3>エンコーダー時計回り</h3> | |
| <label>キータイプ:</label> | |
| <select id="key5-type" onchange="updateKeyType(5)"> | |
| <option value="0">キーボードキー</option> | |
| <option value="1">コンシューマーキー</option> | |
| <option value="2">マクロキー</option> | |
| </select> | |
| <div id="key5-config"></div> | |
| </div> | |
| <!-- Encoder CCW --> | |
| <div class="key-config" id="key6-container"> | |
| <h3>エンコーダー反時計回り</h3> | |
| <label>キータイプ:</label> | |
| <select id="key6-type" onchange="updateKeyType(6)"> | |
| <option value="0">キーボードキー</option> | |
| <option value="1">コンシューマーキー</option> | |
| <option value="2">マクロキー</option> | |
| </select> | |
| <div id="key6-config"></div> | |
| </div> | |
| <button onclick="generateEEPROM()">EEPROM データ生成</button> | |
| <button onclick="copyToClipboard()">クリップボードにコピー</button> | |
| <button onclick="downloadBinary()">バイナリファイルをダウンロード</button> | |
| <div style="margin-top: 15px;"> | |
| <label for="fileInput" style="background: #2196F3; color: white; padding: 10px 20px; border-radius: 5px; cursor: pointer;"> | |
| バイナリファイルを読み込む | |
| </label> | |
| <input type="file" id="fileInput" accept=".bin,.eeprom" style="display: none;" onchange="loadBinary(event)"> | |
| </div> | |
| </div> | |
| <div class="container"> | |
| <h2 id="output-title">EEPROM データ出力</h2> | |
| <div id="output" class="output">ここに生成されたEEPROMデータが表示されます</div> | |
| </div> | |
| <script> | |
| // Language support | |
| let currentLang = 'ja'; | |
| const translations = { | |
| ja: { | |
| title: "Macropad EEPROM キーコード変換ツール", | |
| configTitle: "キー設定", | |
| configInfo: "各キー(3個のキー + エンコーダースイッチ + エンコーダー回転CW/CCW)の動作を設定します。\nトグル機能を使用すると、1つのキーで2つの異なる動作を切り替えることができます。", | |
| outputTitle: "EEPROM データ出力", | |
| outputDefault: "ここに生成されたEEPROMデータが表示されます", | |
| key1: "キー 1", | |
| key2: "キー 2", | |
| key3: "キー 3", | |
| encoderSwitch: "エンコーダースイッチ", | |
| encoderCW: "エンコーダー時計回り", | |
| encoderCCW: "エンコーダー反時計回り", | |
| keyType: "キータイプ:", | |
| keyboardKey: "キーボードキー", | |
| consumerKey: "コンシューマーキー", | |
| macroKey: "マクロキー", | |
| toggleState1: "トグル状態1", | |
| toggleState2: "トグル状態2", | |
| modifier: "修飾キー:", | |
| keycode: "キーコード:", | |
| function: "機能:", | |
| macroCount: "マクロキー数:", | |
| key: "キー", | |
| toggleInfo: "トグル機能: キーを押すたびに状態1と状態2が切り替わります", | |
| generateBtn: "EEPROM データ生成", | |
| copyBtn: "クリップボードにコピー", | |
| downloadBtn: "バイナリファイルをダウンロード", | |
| loadBtn: "バイナリファイルを読み込む", | |
| alertCopy: "クリップボードにコピーしました!", | |
| alertGenerate: "先にEEPROMデータを生成してください", | |
| alertSize: "ファイルサイズが小さすぎます。最低72バイト必要です。", | |
| alertLoaded: "バイナリファイルを読み込みました", | |
| none: "なし", | |
| unknown: "不明" | |
| }, | |
| en: { | |
| title: "Macropad EEPROM Keycode Converter", | |
| configTitle: "Key Configuration", | |
| configInfo: "Configure the behavior of each key (3 keys + encoder switch + encoder rotation CW/CCW).\nToggle function allows you to switch between two different actions with one key.", | |
| outputTitle: "EEPROM Data Output", | |
| outputDefault: "Generated EEPROM data will be displayed here", | |
| key1: "Key 1", | |
| key2: "Key 2", | |
| key3: "Key 3", | |
| encoderSwitch: "Encoder Switch", | |
| encoderCW: "Encoder Clockwise", | |
| encoderCCW: "Encoder Counter-Clockwise", | |
| keyType: "Key Type:", | |
| keyboardKey: "Keyboard Key", | |
| consumerKey: "Consumer Key", | |
| macroKey: "Macro Key", | |
| toggleState1: "Toggle State 1", | |
| toggleState2: "Toggle State 2", | |
| modifier: "Modifier:", | |
| keycode: "Keycode:", | |
| function: "Function:", | |
| macroCount: "Macro Key Count:", | |
| key: "Key", | |
| toggleInfo: "Toggle function: Switches between state 1 and state 2 each time the key is pressed", | |
| generateBtn: "Generate EEPROM Data", | |
| copyBtn: "Copy to Clipboard", | |
| downloadBtn: "Download Binary File", | |
| loadBtn: "Load Binary File", | |
| alertCopy: "Copied to clipboard!", | |
| alertGenerate: "Please generate EEPROM data first", | |
| alertSize: "File size too small. Minimum 72 bytes required.", | |
| alertLoaded: "Binary file loaded", | |
| none: "None", | |
| unknown: "Unknown" | |
| } | |
| }; | |
| function t(key) { | |
| return translations[currentLang][key] || key; | |
| } | |
| function toggleLanguage() { | |
| currentLang = currentLang === 'ja' ? 'en' : 'ja'; | |
| updateUILanguage(); | |
| } | |
| function updateUILanguage() { | |
| document.getElementById('main-title').textContent = t('title'); | |
| document.getElementById('config-title').textContent = t('configTitle'); | |
| document.getElementById('config-info').textContent = t('configInfo'); | |
| document.getElementById('output-title').textContent = t('outputTitle'); | |
| // Update key labels | |
| const keyNames = [t('key1'), t('key2'), t('key3'), t('encoderSwitch'), t('encoderCW'), t('encoderCCW')]; | |
| for (let i = 1; i <= 6; i++) { | |
| const container = document.getElementById(`key${i}-container`); | |
| if (container) { | |
| const h3 = container.querySelector('h3'); | |
| if (h3) h3.textContent = keyNames[i-1]; | |
| const label = container.querySelector('label'); | |
| if (label) label.textContent = t('keyType'); | |
| const select = document.getElementById(`key${i}-type`); | |
| if (select) { | |
| select.options[0].textContent = t('keyboardKey'); | |
| select.options[1].textContent = t('consumerKey'); | |
| select.options[2].textContent = t('macroKey'); | |
| } | |
| } | |
| // Update the key type configuration if it's already displayed | |
| updateKeyType(i); | |
| } | |
| // Update buttons | |
| const buttons = document.querySelectorAll('button'); | |
| buttons[1].textContent = t('generateBtn'); | |
| buttons[2].textContent = t('copyBtn'); | |
| buttons[3].textContent = t('downloadBtn'); | |
| // Update file input label | |
| document.querySelector('label[for="fileInput"]').textContent = t('loadBtn'); | |
| // Update output if it's default text | |
| const outputText = document.getElementById('output').textContent; | |
| if (outputText === translations['ja'].outputDefault || outputText === translations['en'].outputDefault) { | |
| document.getElementById('output').textContent = t('outputDefault'); | |
| } | |
| } | |
| // USB HID キーコード定義 | |
| const keyboardCodes = { | |
| "なし": 0x00, | |
| "A": 0x04, "B": 0x05, "C": 0x06, "D": 0x07, "E": 0x08, "F": 0x09, | |
| "G": 0x0A, "H": 0x0B, "I": 0x0C, "J": 0x0D, "K": 0x0E, "L": 0x0F, | |
| "M": 0x10, "N": 0x11, "O": 0x12, "P": 0x13, "Q": 0x14, "R": 0x15, | |
| "S": 0x16, "T": 0x17, "U": 0x18, "V": 0x19, "W": 0x1A, "X": 0x1B, | |
| "Y": 0x1C, "Z": 0x1D, | |
| "1": 0x1E, "2": 0x1F, "3": 0x20, "4": 0x21, "5": 0x22, | |
| "6": 0x23, "7": 0x24, "8": 0x25, "9": 0x26, "0": 0x27, | |
| "Enter": 0x28, "Escape": 0x29, "Backspace": 0x2A, "Tab": 0x2B, | |
| "Space": 0x2C, "-": 0x2D, "=": 0x2E, "[": 0x2F, "]": 0x30, | |
| "\\": 0x31, ";": 0x33, "'": 0x34, "`": 0x35, ",": 0x36, | |
| ".": 0x37, "/": 0x38, | |
| "F1": 0x3A, "F2": 0x3B, "F3": 0x3C, "F4": 0x3D, "F5": 0x3E, | |
| "F6": 0x3F, "F7": 0x40, "F8": 0x41, "F9": 0x42, "F10": 0x43, | |
| "F11": 0x44, "F12": 0x45, | |
| "PrintScreen": 0x46, "ScrollLock": 0x47, "Pause": 0x48, | |
| "Insert": 0x49, "Home": 0x4A, "PageUp": 0x4B, | |
| "Delete": 0x4C, "End": 0x4D, "PageDown": 0x4E, | |
| "Right": 0x4F, "Left": 0x50, "Down": 0x51, "Up": 0x52, | |
| "NumLock": 0x53, | |
| "KP /": 0x54, "KP *": 0x55, "KP -": 0x56, "KP +": 0x57, | |
| "KP Enter": 0x58, "KP 1": 0x59, "KP 2": 0x5A, "KP 3": 0x5B, | |
| "KP 4": 0x5C, "KP 5": 0x5D, "KP 6": 0x5E, "KP 7": 0x5F, | |
| "KP 8": 0x60, "KP 9": 0x61, "KP 0": 0x62, "KP .": 0x63 | |
| }; | |
| const modifierCodesJa = { | |
| "なし": 0x00, | |
| "Ctrl": 0x01, | |
| "Shift": 0x02, | |
| "Ctrl+Shift": 0x03, | |
| "Alt": 0x04, | |
| "Ctrl+Alt": 0x05, | |
| "Shift+Alt": 0x06, | |
| "Ctrl+Shift+Alt": 0x07 | |
| }; | |
| const modifierCodesEn = { | |
| "None": 0x00, | |
| "Ctrl": 0x01, | |
| "Shift": 0x02, | |
| "Ctrl+Shift": 0x03, | |
| "Alt": 0x04, | |
| "Ctrl+Alt": 0x05, | |
| "Shift+Alt": 0x06, | |
| "Ctrl+Shift+Alt": 0x07 | |
| }; | |
| const consumerCodesJa = { | |
| "なし": 0x00, | |
| "再生": 0xB0, | |
| "一時停止": 0xB1, | |
| "録音": 0xB2, | |
| "早送り": 0xB3, | |
| "巻き戻し": 0xB4, | |
| "次のトラック": 0xB5, | |
| "前のトラック": 0xB6, | |
| "停止": 0xB7, | |
| "イジェクト": 0xB8, | |
| "ランダム再生": 0xB9, | |
| "不明 (0xE1)": 0xE1, | |
| "音量ミュート": 0xE2, | |
| "音量アップ": 0xE9, | |
| "音量ダウン": 0xEA, | |
| "システム電源": 0x30, | |
| "システムリセット": 0x31, | |
| "システムスリープ": 0x32, | |
| "メニュー": 0x40, | |
| "メニュー選択": 0x41, | |
| "メニュー上": 0x42, | |
| "メニュー下": 0x43, | |
| "メニュー左": 0x44, | |
| "メニュー右": 0x45, | |
| "メニューエスケープ": 0x46, | |
| "メニュー増加": 0x47, | |
| "メニュー減少": 0x48 | |
| }; | |
| const consumerCodesEn = { | |
| "None": 0x00, | |
| "Play": 0xB0, | |
| "Pause": 0xB1, | |
| "Record": 0xB2, | |
| "Fast Forward": 0xB3, | |
| "Rewind": 0xB4, | |
| "Next Track": 0xB5, | |
| "Previous Track": 0xB6, | |
| "Stop": 0xB7, | |
| "Eject": 0xB8, | |
| "Random Play": 0xB9, | |
| "Unknown (0xE1)": 0xE1, | |
| "Volume Mute": 0xE2, | |
| "Volume Up": 0xE9, | |
| "Volume Down": 0xEA, | |
| "System Power": 0x30, | |
| "System Reset": 0x31, | |
| "System Sleep": 0x32, | |
| "Menu": 0x40, | |
| "Menu Pick": 0x41, | |
| "Menu Up": 0x42, | |
| "Menu Down": 0x43, | |
| "Menu Left": 0x44, | |
| "Menu Right": 0x45, | |
| "Menu Escape": 0x46, | |
| "Menu Increment": 0x47, | |
| "Menu Decrement": 0x48 | |
| }; | |
| function getModifierCodes() { | |
| return currentLang === 'ja' ? modifierCodesJa : modifierCodesEn; | |
| } | |
| function getConsumerCodes() { | |
| return currentLang === 'ja' ? consumerCodesJa : consumerCodesEn; | |
| } | |
| function updateKeyType(keyNum) { | |
| const type = document.getElementById(`key${keyNum}-type`).value; | |
| const configDiv = document.getElementById(`key${keyNum}-config`); | |
| if (type === "0") { // Keyboard key | |
| configDiv.innerHTML = ` | |
| <div> | |
| <h4>${t('toggleState1')}</h4> | |
| <label>${t('modifier')}</label> | |
| <select id="key${keyNum}-mod1"> | |
| ${Object.entries(getModifierCodes()).map(([name, code]) => | |
| `<option value="${code}">${name}</option>` | |
| ).join('')} | |
| </select><br> | |
| <label>${t('keycode')}</label> | |
| <select id="key${keyNum}-code1"> | |
| ${Object.entries(keyboardCodes).sort((a, b) => { | |
| if (a[0] === "なし" || a[0] === "None") return -1; | |
| if (b[0] === "なし" || b[0] === "None") return 1; | |
| return 0; | |
| }).map(([name, code]) => | |
| `<option value="${code}" ${(name === "なし" || name === "None") ? "selected" : ""}>${name}</option>` | |
| ).join('')} | |
| </select> | |
| </div> | |
| <div style="margin-top: 10px;"> | |
| <h4>${t('toggleState2')}</h4> | |
| <label>${t('modifier')}</label> | |
| <select id="key${keyNum}-mod2"> | |
| ${Object.entries(getModifierCodes()).map(([name, code]) => | |
| `<option value="${code}">${name}</option>` | |
| ).join('')} | |
| </select><br> | |
| <label>${t('keycode')}</label> | |
| <select id="key${keyNum}-code2"> | |
| ${Object.entries(keyboardCodes).sort((a, b) => { | |
| if (a[0] === "なし" || a[0] === "None") return -1; | |
| if (b[0] === "なし" || b[0] === "None") return 1; | |
| return 0; | |
| }).map(([name, code]) => | |
| `<option value="${code}" ${(name === "なし" || name === "None") ? "selected" : ""}>${name}</option>` | |
| ).join('')} | |
| </select> | |
| </div> | |
| <div class="modifier-info"> | |
| ${t('toggleInfo')} | |
| </div> | |
| `; | |
| } else if (type === "1") { // Consumer key | |
| configDiv.innerHTML = ` | |
| <div> | |
| <h4>${t('toggleState1')}</h4> | |
| <label>${t('function')}:</label> | |
| <select id="key${keyNum}-consumer1"> | |
| ${Object.entries(getConsumerCodes()).sort((a, b) => { | |
| if (a[0] === "なし" || a[0] === "None") return -1; | |
| if (b[0] === "なし" || b[0] === "None") return 1; | |
| return 0; | |
| }).map(([name, code]) => | |
| `<option value="${code}" ${(name === "なし" || name === "None") ? "selected" : ""}>${name}</option>` | |
| ).join('')} | |
| </select> | |
| </div> | |
| <div style="margin-top: 10px;"> | |
| <h4>${t('toggleState2')}</h4> | |
| <label>${t('function')}:</label> | |
| <select id="key${keyNum}-consumer2"> | |
| ${Object.entries(getConsumerCodes()).sort((a, b) => { | |
| if (a[0] === "なし" || a[0] === "None") return -1; | |
| if (b[0] === "なし" || b[0] === "None") return 1; | |
| return 0; | |
| }).map(([name, code]) => | |
| `<option value="${code}" ${(name === "なし" || name === "None") ? "selected" : ""}>${name}</option>` | |
| ).join('')} | |
| </select> | |
| </div> | |
| `; | |
| } else if (type === "2") { // Macro key | |
| configDiv.innerHTML = ` | |
| <div class="macro-keys"> | |
| <label>${t('macroCount')}:</label> | |
| <input type="number" id="key${keyNum}-macro-count" min="1" max="10" value="3" onchange="updateMacroKeys(${keyNum})"> | |
| <div id="key${keyNum}-macro-keys" style="margin-top: 10px;"></div> | |
| </div> | |
| `; | |
| updateMacroKeys(keyNum); | |
| } | |
| } | |
| function updateMacroKeys(keyNum) { | |
| const count = parseInt(document.getElementById(`key${keyNum}-macro-count`).value); | |
| const macroDiv = document.getElementById(`key${keyNum}-macro-keys`); | |
| let html = ''; | |
| for (let i = 0; i < count; i++) { | |
| html += ` | |
| <div> | |
| <label>${t('key')} ${i + 1}:</label> | |
| <select id="key${keyNum}-macro${i}"> | |
| ${Object.entries(keyboardCodes).sort((a, b) => { | |
| if (a[0] === "なし" || a[0] === "None") return -1; | |
| if (b[0] === "なし" || b[0] === "None") return 1; | |
| return 0; | |
| }).map(([name, code]) => | |
| `<option value="${code}" ${(name === "なし" || name === "None") ? "selected" : ""}>${name}</option>` | |
| ).join('')} | |
| </select> | |
| </div> | |
| `; | |
| } | |
| macroDiv.innerHTML = html; | |
| } | |
| function generateEEPROM() { | |
| let eepromData = new Array(128).fill(0xFF); // Initialize 128 bytes with 0xFF | |
| for (let keyNum = 1; keyNum <= 6; keyNum++) { | |
| const type = parseInt(document.getElementById(`key${keyNum}-type`).value); | |
| const offset = (keyNum - 1) * 12; // Each key uses 12 bytes | |
| eepromData[offset] = type; | |
| if (type === 0) { // Keyboard key | |
| eepromData[offset + 1] = parseInt(document.getElementById(`key${keyNum}-mod1`).value); | |
| eepromData[offset + 2] = parseInt(document.getElementById(`key${keyNum}-code1`).value); | |
| eepromData[offset + 3] = parseInt(document.getElementById(`key${keyNum}-mod2`).value); | |
| eepromData[offset + 4] = parseInt(document.getElementById(`key${keyNum}-code2`).value); | |
| } else if (type === 1) { // Consumer key | |
| eepromData[offset + 1] = parseInt(document.getElementById(`key${keyNum}-consumer1`).value); | |
| eepromData[offset + 2] = parseInt(document.getElementById(`key${keyNum}-consumer2`).value); | |
| } else if (type === 2) { // Macro key | |
| const count = parseInt(document.getElementById(`key${keyNum}-macro-count`).value); | |
| eepromData[offset + 1] = count; | |
| for (let i = 0; i < count && i < 10; i++) { | |
| eepromData[offset + 2 + i] = parseInt(document.getElementById(`key${keyNum}-macro${i}`).value); | |
| } | |
| } | |
| } | |
| // Format output | |
| let output = "EEPROM Data (128 bytes):\n\n"; | |
| output += "Address: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n"; | |
| output += "--------------------------------------------------------\n"; | |
| for (let i = 0; i < 128; i += 16) { | |
| output += `0x${i.toString(16).padStart(2, '0').toUpperCase()}: `; | |
| for (let j = 0; j < 16; j++) { | |
| if (i + j < 128) { | |
| output += eepromData[i + j].toString(16).padStart(2, '0').toUpperCase() + " "; | |
| } | |
| } | |
| if (i < 72) { | |
| output += ` // ${getKeyDescription(Math.floor(i / 12) + 1)}`; | |
| } | |
| output += "\n"; | |
| } | |
| output += "\n\nC配列形式 (128 bytes):\n"; | |
| output += "const uint8_t eeprom_data[128] = {\n"; | |
| for (let i = 0; i < 128; i += 16) { | |
| output += " "; | |
| for (let j = 0; j < 16 && i + j < 128; j++) { | |
| output += "0x" + eepromData[i + j].toString(16).padStart(2, '0').toUpperCase(); | |
| if (i + j < 127) output += ", "; | |
| } | |
| if (i < 112) output += ","; | |
| if (i < 72) { | |
| output += ` // ${getKeyDescription(Math.floor(i / 12) + 1)}`; | |
| } | |
| output += "\n"; | |
| } | |
| output += "};\n"; | |
| document.getElementById('output').textContent = output; | |
| globalEepromData = eepromData; // Store for download | |
| } | |
| function getKeyDescription(keyNum) { | |
| const names = [t('key1'), t('key2'), t('key3'), t('encoderSwitch'), t('encoderCW'), t('encoderCCW')]; | |
| return names[keyNum - 1]; | |
| } | |
| function copyToClipboard() { | |
| const output = document.getElementById('output').textContent; | |
| navigator.clipboard.writeText(output).then(() => { | |
| alert(t('alertCopy')); | |
| }); | |
| } | |
| let globalEepromData = []; // Store EEPROM data globally for download | |
| function downloadBinary() { | |
| if (globalEepromData.length === 0) { | |
| alert(t('alertGenerate')); | |
| return; | |
| } | |
| // Create binary data | |
| const buffer = new Uint8Array(globalEepromData); | |
| const blob = new Blob([buffer], { type: 'application/octet-stream' }); | |
| // Create download link | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = 'macropad_eeprom.bin'; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| } | |
| function loadBinary(event) { | |
| const file = event.target.files[0]; | |
| if (!file) return; | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| const arrayBuffer = e.target.result; | |
| const bytes = new Uint8Array(arrayBuffer); | |
| if (bytes.length < 72) { | |
| alert(t('alertSize')); | |
| return; | |
| } | |
| // Parse the binary data and update UI | |
| for (let keyNum = 1; keyNum <= 6; keyNum++) { | |
| const offset = (keyNum - 1) * 12; | |
| const type = bytes[offset]; | |
| // Set key type | |
| document.getElementById(`key${keyNum}-type`).value = type; | |
| updateKeyType(keyNum); | |
| // Wait for DOM update | |
| setTimeout(() => { | |
| if (type === 0) { // Keyboard key | |
| document.getElementById(`key${keyNum}-mod1`).value = bytes[offset + 1]; | |
| document.getElementById(`key${keyNum}-code1`).value = bytes[offset + 2]; | |
| document.getElementById(`key${keyNum}-mod2`).value = bytes[offset + 3]; | |
| document.getElementById(`key${keyNum}-code2`).value = bytes[offset + 4]; | |
| } else if (type === 1) { // Consumer key | |
| document.getElementById(`key${keyNum}-consumer1`).value = bytes[offset + 1]; | |
| document.getElementById(`key${keyNum}-consumer2`).value = bytes[offset + 2]; | |
| } else if (type === 2) { // Macro key | |
| const count = bytes[offset + 1]; | |
| document.getElementById(`key${keyNum}-macro-count`).value = count; | |
| updateMacroKeys(keyNum); | |
| // Wait for macro keys to be created | |
| setTimeout(() => { | |
| for (let i = 0; i < count && i < 10; i++) { | |
| const macroSelect = document.getElementById(`key${keyNum}-macro${i}`); | |
| if (macroSelect) { | |
| macroSelect.value = bytes[offset + 2 + i]; | |
| } | |
| } | |
| }, 50); | |
| } | |
| }, 50); | |
| } | |
| alert(t('alertLoaded')); | |
| }; | |
| reader.readAsArrayBuffer(file); | |
| } | |
| // Initialize all keys | |
| for (let i = 1; i <= 6; i++) { | |
| updateKeyType(i); | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment