ZNDS智能电视网 推荐当贝市场

TV应用下载 / 资源分享区

软件下载 | 游戏 | 讨论 | 电视计算器

综合交流 / 评测 / 活动区

交流区 | 测硬件 | 网站活动 | Z币中心

新手入门 / 进阶 / 社区互助

新手 | 你问我答 | 免费刷机救砖 | ROM固件

查看: 1233|回复: 2
上一主题 下一主题
[分享]

gk6323分区表生成工具hitool烧录备份分区表

[复制链接]
跳转到指定楼层
楼主
发表于 2026-10-14 08:35 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式 | 来自北京
闲来无事,写了个小工具
gk6323分区表生成工具hitool烧录备份分区表 gk6323分区表生成工具hitool烧录备份分区表

直接复制代码保存为.html文件即可

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <title>烧录分区表生成器 (文本 → XML)</title>
  7. <style>
  8.   :root {
  9.     --bg: #0f1420;
  10.     --panel: #171d2c;
  11.     --panel-2: #1f2738;
  12.     --border: #2a3346;
  13.     --text: #e6ecf5;
  14.     --muted: #8a98b0;
  15.     --accent: #4f9dff;
  16.     --accent-2: #36d399;
  17.     --danger: #ff6b6b;
  18.     --code: #0b0f17;
  19.   }
  20.   * { box-sizing: border-box; }
  21.   body {
  22.     margin: 0;
  23.     font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
  24.     background: var(--bg);
  25.     color: var(--text);
  26.     line-height: 1.5;
  27.   }
  28.   header { padding: 20px 24px; border-bOTTom: 1px solid var(--border); background: linear-gradient(180deg, #141b2b, #0f1420); }
  29.   header h1 { margin: 0 0 6px; font-size: 20px; }
  30.   header p { margin: 0; color: var(--muted); font-size: 13px; }
  31.   .wrap { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; padding: 16px; max-width: 1400px; margin: 0 auto; }
  32.   [url=home.php?mod=space&uid=3180104]@media[/url] (max-width: 920px) { .wrap { grid-template-columns: 1fr; } }
  33.   .panel { background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 14px; display: flex; flex-direction: column; min-height: 200px; }
  34.   .panel h2 { margin: 0 0 10px; font-size: 14px; color: var(--accent); letter-spacing: .3px; }
  35.   textarea { width: 100%; flex: 1; min-height: 220px; resize: vertical; background: var(--code); color: var(--text); border: 1px solid var(--border); border-radius: 8px; padding: 12px; font-family: "SFMono-Regular", Consolas, Menlo, monospace; font-size: 13px; line-height: 1.5; }
  36.   .options { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 12px; font-size: 13px; color: var(--muted); align-items: center; }
  37.   .options label { display: flex; align-items: center; gap: 6px; }
  38.   .options input[type="number"], .options select {
  39.     background: var(--code); color: var(--text); border: 1px solid var(--border); border-radius: 6px; padding: 4px 6px; font-size: 13px;
  40.   }
  41.   .options input[type="number"] { width: 96px; }
  42.   .btns { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
  43.   button { background: var(--panel-2); color: var(--text); border: 1px solid var(--border); border-radius: 8px; padding: 8px 14px; font-size: 13px; cursor: pointer; transition: .15s; }
  44.   button:hover { border-color: var(--accent); color: #fff; }
  45.   button.primary { background: var(--accent); border-color: var(--accent); color: #04101f; font-weight: 600; }
  46.   button.primary:hover { filter: brightness(1.08); }
  47.   button.ok { background: var(--accent-2); border-color: var(--accent-2); color: #052016; font-weight: 600; }
  48.   pre { background: var(--code); border: 1px solid var(--border); border-radius: 8px; padding: 12px; overflow: auto; flex: 1; min-height: 340px; margin: 0; font-family: "SFMono-Regular", Consolas, Menlo, monospace; font-size: 13px; white-space: pre; }
  49.   .summary { width: 100%; border-collapse: collapse; font-size: 12.5px; }
  50.   .summary th, .summary td { border: 1px solid var(--border); padding: 5px 8px; text-align: left; }
  51.   .summary th { background: var(--panel-2); color: var(--muted); position: sticky; top: 0; }
  52.   .summary td.num { text-align: right; font-variant-numeric: tabular-nums; }
  53.   .summary .ro { color: var(--danger); font-weight: 600; }
  54.   .table-scroll { max-height: 360px; overflow: auto; }
  55.   .msg { font-size: 12.5px; margin-top: 10px; min-height: 16px; }
  56.   .msg.err { color: var(--danger); }
  57.   .msg.ok { color: var(--accent-2); }
  58.   .msg.warn { color: #ffcf6b; }
  59.   .full { grid-column: 1 / -1; }
  60.   .note { color: var(--muted); font-size: 12px; margin: 6px 0 0; }
  61.   code.inline { background: var(--code); padding: 1px 5px; border-radius: 4px; color: #cfe0ff; }
  62.   .fmtbox { display:flex; gap:14px; flex-wrap:wrap; align-items:center; margin-bottom:4px; }
  63.   .fmtbox strong { color: var(--text); }
  64. </style>
  65. </head>
  66. <body>
  67. <header>
  68.   <h1>烧录分区表生成器 · 文本 → XML</h1>
  69.   <p>输入 <code class="inline">设备:大小(名称)标志,大小(名称)标志,…</code> 形式的自定义文本,自动解析并生成 HiTool XML 分区表,支持预览、复制与下载。</p>
  70. </header>

  71. <div class="wrap">
  72.   <!-- 输入区 -->
  73.   <section class="panel">
  74.     <h2>① 输入分区描述文本</h2>
  75.     <textarea id="input" spellcheck="false" placeholder="例如:mmcblk0:1M(fastboot),512K(bootargs),-(userdata)"></textarea>

  76.     <div class="options" id="hitoolOpts">
  77.       <label>FlashType:
  78.         <select id="flashType">
  79.           <option value="emmc" selected>emmc</option>
  80.           <option value="nand">nand</option>
  81.           <option value="spi">spi</option>
  82.         </select>
  83.       </label>
  84.       <label>FileSystem:
  85.         <select id="fileSystem">
  86.           <option value="none" selected>none</option>
  87.           <option value="fat">fat</option>
  88.           <option value="ext4">ext4</option>
  89.         </select>
  90.       </label>
  91.       <label><input type="checkbox" id="selAll" checked /> 全部勾选(Sel=1)</label>
  92.     </div>

  93.     <div class="options">
  94.       <label>总磁盘容量(MiB,可选):
  95.         <input type="number" id="total" min="0" step="1" placeholder="留空自动" />
  96.       </label>
  97.       <label>扇区大小(字节):
  98.         <input type="number" id="sector" min="1" step="1" value="512" />
  99.       </label>
  100.     </div>

  101.     <div class="btns">
  102.       <button class="primary" id="gen">生成 XML</button>
  103.       <button id="example">载入示例</button>
  104.       <button id="clear">清空</button>
  105.     </div>
  106.     <div class="msg" id="msg"></div>
  107.   </section>

  108.   <!-- 输出区 -->
  109.   <section class="panel">
  110.     <h2>② 生成的 XML 分区表</h2>
  111.     <pre id="output">(点击“生成 XML”查看结果)</pre>
  112.     <div class="btns">
  113.       <button class="ok" id="copy">复制 XML</button>
  114.       <button id="download">下载 partition_table.xml</button>
  115.     </div>
  116.   </section>

  117.   <!-- 解析摘要 -->
  118.   <section class="panel full">
  119.     <h2>③ 解析结果摘要(用于核对)</h2>
  120.     <div class="table-scroll">
  121.       <table class="summary" id="summary">
  122.         <thead>
  123.           <tr>
  124.             <th>#</th><th>名称</th><th>大小(可读)</th><th>大小(字节)</th>
  125.             <th>起始(可读)</th><th>起始(字节)</th><th>起始(扇区)</th><th>标志</th>
  126.           </tr>
  127.         </thead>
  128.         <tbody id="summaryBody"></tbody>
  129.       </table>
  130.     </div>
  131.     <p class="note">说明:<code class="inline">1K = 1024 字节</code>、<code class="inline">1M = 1048576 字节</code>(二进制换算,符合嵌入式分区惯例);起始地址从 0 开始按序累加;<code class="inline">-</code> 表示占用剩余全部空间,<b>HiTool 格式下必须填写“总磁盘容量”才能算出其 Length</b>。</p>
  132.     <p class="note">HiTool 注意:分区需按顺序逐条排列(首分区必须为 <code class="inline">fastboot</code> 且不可改名/删除),起始地址与长度需为 flash 块大小的整数倍,否则可能烧写出错。</p>
  133.   </section>
  134. </div>

  135. <script>
  136. "use strict";

  137. const EXAMPLE = "mmcblk0:1M(fastboot),512K(bootargs),512K(bootargsbak),10M(stbid),1M(loaderdb),20M(recovery),2M(deviceinfo),8M(securestore),2M(atf),8M(baseparam),8M(pqparam),2M(dtbo),10M(logo),10M(logobak),20M(fastplay),20M(fastplaybak),20M(recoverybak),60M(boot),20M(misc),20M(trustedcore),1500M(system)ro,896M(cache),300M(vendor)ro,700M(backup),80M(private),256M(ctc),50M(keydata),-(userdata)";

  138. const UNIT_MULT = { "": 1, "K": 1024, "M": 1024 ** 2, "G": 1024 ** 3, "T": 1024 ** 4, "P": 1024 ** 5 };

  139. function parseSize(raw) {
  140.   raw = raw.trim();
  141.   if (raw === "-" || raw === "") return { remAIning: true, bytes: -1, human: "-" };
  142.   const m = raw.match(/^(\d+(?:\.\d+)?)\s*([KMGTP]?)$/i);
  143.   if (!m) return null;
  144.   const num = parseFloat(m[1]);
  145.   const unit = m[2].toUpperCase();
  146.   return { remaining: false, bytes: Math.round(num * (UNIT_MULT[unit] || 1)), human: raw };
  147. }

  148. function escapeXml(s) {
  149.   return String(s).replace(/[<>&'"]/g, c => ({ "<": "<", ">": ">", "&": "&", "'": "'", '"': """ }[c]));
  150. }

  151. function parseInput(text) {
  152.   text = text.trim();
  153.   if (!text) throw new Error("输入为空。");

  154.   let device = "unknown", body = text;
  155.   const colon = text.indexOf(":");
  156.   if (colon !== -1) { device = text.slice(0, colon).trim(); body = text.slice(colon + 1).trim(); }

  157.   const tokens = body.split(",").map(t => t.trim()).filter(Boolean);
  158.   if (tokens.length === 0) throw new Error("未找到任何分区条目(请用逗号分隔)。");

  159.   const partitions = [];
  160.   tokens.forEach((tok, i) => {
  161.     const pIdx = tok.indexOf("(");
  162.     if (pIdx === -1) throw new Error(`第 ${i + 1} 个分区格式错误:“${tok}” 缺少 (名称)。`);
  163.     const sizeRaw = tok.slice(0, pIdx);
  164.     const rest = tok.slice(pIdx + 1);
  165.     const m = rest.match(/^([^)]+)\)(.*)$/);
  166.     if (!m) throw new Error(`第 ${i + 1} 个分区格式错误:“${tok}” 括号不匹配。`);
  167.     const name = m[1].trim();
  168.     const flags = m[2].trim();
  169.     const size = parseSize(sizeRaw);
  170.     if (!size) throw new Error(`第 ${i + 1} 个分区大小无法识别:“${sizeRaw}”。`);
  171.     if (!name) throw new Error(`第 ${i + 1} 个分区名称为空。`);
  172.     partitions.push({ name, sizeBytes: size.bytes, sizeHuman: size.human, remaining: size.remaining, flags });
  173.   });
  174.   return { device, partitions };
  175. }

  176. // 计算偏移(字节 / 扇区 / hex),并在提供总容量时算出剩余分区大小
  177. function computeOffsets(partitions, totalMiB, sectorSize) {
  178.   let offset = 0, remainingHandled = false;
  179.   const totalBytes = (totalMiB && totalMiB > 0) ? Math.round(totalMiB * 1024 * 1024) : 0;
  180.   partitions.forEach(p => {
  181.     p.start = offset;
  182.     p.startSector = Math.floor(offset / sectorSize);
  183.     p.startHex = "0x" + offset.toString(16).toUpperCase();
  184.     if (p.remaining) {
  185.       remainingHandled = true;
  186.       if (totalBytes > 0 && totalBytes >= offset) { p.sizeBytes = totalBytes - offset; p.sizeHuman = toHisiSize(p.sizeBytes); }
  187.       else p.sizeBytes = -1;
  188.     }
  189.     if (p.sizeBytes > 0) offset += p.sizeBytes;
  190.   });
  191.   return { usedBytes: offset, totalBytes, overflow: totalBytes > 0 && offset > totalBytes, remainingHandled };
  192. }

  193. // 转换为 HiTool 友好的人类可读单位(保证精确:整除 MiB→M,整除 KiB→K,否则原始字节)
  194. function toHisiSize(bytes) {
  195.   if (bytes === 0) return "0";
  196.   if (bytes < 0) return "";
  197.   if (bytes % (1024 * 1024) === 0) return (bytes / 1048576) + "M";
  198.   if (bytes % 1024 === 0) return (bytes / 1024) + "K";
  199.   return String(bytes);
  200. }

  201. function formatBytes(b) {
  202.   if (b < 1024) return b + "B";
  203.   const units = ["K", "M", "G", "T"];
  204.   let v = b / 1024, u = 0;
  205.   while (v >= 1024 && u < units.length - 1) { v /= 1024; u++; }
  206.   return (v >= 100 || v % 1 === 0 ? v.toFixed(0) : v.toFixed(1)) + units[u];
  207. }

  208. function generateHiTool(device, partitions, opts) {
  209.   const L = [];
  210.   L.push('<?xml version="1.0" encoding="GB2312"?>');
  211.   L.push('<Partition_Info>');
  212.   partitions.forEach(p => {
  213.     const sel = opts.selAll ? "1" : "0";
  214.     const start = toHisiSize(p.start);
  215.     const length = p.remaining ? (p.sizeBytes > 0 ? toHisiSize(p.sizeBytes) : "") : toHisiSize(p.sizeBytes);
  216.     // HiTool 标准字段:Sel / PartitionName / FlashType / FileSystem / Start / Length / SelectFile
  217.     // SelectFile 自动生成为 <分区名>.img(如 fastboot.img)
  218.     // (ro 等只读标志不属于该 schema,仅在摘要表展示,不写入 XML)
  219.     L.push(`  <Part Sel="${sel}" PartitionName="${escapeXml(p.name)}" FlashType="${opts.flashType}" FileSystem="${opts.fileSystem}" Start="${start}" Length="${length}" SelectFile="${escapeXml(p.name)}.img"/>`);
  220.   });
  221.   L.push('</Partition_Info>');
  222.   return L.join("\n");
  223. }

  224. function renderSummary(partitions, info) {
  225.   const tb = document.getElementById("summaryBody");
  226.   tb.innerHTML = "";
  227.   partitions.forEach((p, i) => {
  228.     const tr = document.createElement("tr");
  229.     const sizeB = p.sizeBytes < 0 ? "(剩余)" : p.sizeBytes.toLocaleString();
  230.     const sizeH = p.sizeBytes < 0 ? "(剩余)" : toHisiSize(p.sizeBytes);
  231.     const roCell = p.flags.toLowerCase() === "ro" ? `<span class="ro">ro</span>` : (escapeXml(p.flags) || "—");
  232.     tr.innerHTML = `
  233.       <td>${i + 1}</td>
  234.       <td>${escapeXml(p.name)}</td>
  235.       <td>${sizeH}</td>
  236.       <td class="num">${sizeB}</td>
  237.       <td>${toHisiSize(p.start)}</td>
  238.       <td class="num">${p.start.toLocaleString()}</td>
  239.       <td class="num">${p.startSector.toLocaleString()}</td>
  240.       <td>${roCell}</td>`;
  241.     tb.appendChild(tr);
  242.   });
  243.   const tr = document.createElement("tr");
  244.   tr.style.fontWeight = "600";
  245.   tr.innerHTML = `
  246.     <td colspan="3">合计已定空间</td>
  247.     <td class="num">${info.usedBytes.toLocaleString()}</td>
  248.     <td colspan="3">${info.totalBytes ? "总容量 " + info.totalBytes.toLocaleString() + " 字节" : "(未设总容量)"}</td>
  249.     <td></td>`;
  250.   tb.appendChild(tr);
  251. }

  252. function showMsg(text, type) {
  253.   const el = document.getElementById("msg");
  254.   el.textContent = text;
  255.   el.className = "msg" + (type ? " " + type : "");
  256. }

  257. function doGenerate() {
  258.   const text = document.getElementById("input").value;
  259.   const totalMiB = parseFloat(document.getElementById("total").value) || 0;
  260.   const sectorSize = Math.max(1, parseInt(document.getElementById("sector").value, 10) || 512);

  261.   try {
  262.     const { device, partitions } = parseInput(text);
  263.     const info = computeOffsets(partitions, totalMiB, sectorSize);

  264.     const opts = {
  265.       flashType: document.getElementById("flashType").value,
  266.       fileSystem: document.getElementById("fileSystem").value,
  267.       selAll: document.getElementById("selAll").checked
  268.     };
  269.     const xml = generateHiTool(device, partitions, opts);
  270.     document.getElementById("output").textContent = xml;
  271.     renderSummary(partitions, info);

  272.     let note = `解析成功:${partitions.length} 个分区,已定空间 ${formatBytes(info.usedBytes)}。`;
  273.     let type = "ok";
  274.     if (info.remainingHandled && !info.totalBytes) {
  275.       note += " ⚠ 末尾含 -(剩余) 分区且未填“总磁盘容量”,其 Length 留空,请补填容量后重新生成。";
  276.       type = "warn";
  277.     }
  278.     if (info.totalBytes && info.overflow) {
  279.       note += " ⚠ 已定空间超过总容量!请检查总容量或分区大小。";
  280.       type = "err";
  281.     }
  282.     showMsg(note, type);
  283.   } catch (e) {
  284.     document.getElementById("output").textContent = "(生成失败)";
  285.     document.getElementById("summaryBody").innerHTML = "";
  286.     showMsg("解析错误:" + e.message, "err");
  287.   }
  288. }

  289. document.getElementById("gen").addEventListener("click", doGenerate);
  290. document.getElementById("example").addEventListener("click", () => { document.getElementById("input").value = EXAMPLE; doGenerate(); });
  291. document.getElementById("clear").addEventListener("click", () => {
  292.   document.getElementById("input").value = "";
  293.   document.getElementById("output").textContent = "(点击“生成 XML”查看结果)";
  294.   document.getElementById("summaryBody").innerHTML = "";
  295.   showMsg("");
  296. });
  297. document.getElementById("copy").addEventListener("click", async () => {
  298.   const txt = document.getElementById("output").textContent;
  299.   try { await navigator.clipboard.writeText(txt); showMsg("XML 已复制到剪贴板。", "ok"); }
  300.   catch {
  301.     const ta = document.createElement("textarea"); ta.value = txt; document.body.appendChild(ta); ta.select();
  302.     document.execCommand("copy"); document.body.removeChild(ta); showMsg("XML 已复制(兼容模式)。", "ok");
  303.   }
  304. });
  305. document.getElementById("download").addEventListener("click", () => {
  306.   const txt = document.getElementById("output").textContent;
  307.   const blob = new Blob([txt], { type: "application/xml;charset=utf-8" });
  308.   const url = URL.createObjectURL(blob);
  309.   const a = document.createElement("a");
  310.   a.href = url; a.download = "partition_table.xml";
  311.   document.body.appendChild(a); a.click(); document.body.removeChild(a);
  312.   URL.revokeObjectURL(url);
  313. });

  314. // 初始载入示例并生成(默认 HiTool 格式)
  315. document.getElementById("input").value = EXAMPLE;
  316. doGenerate();
  317. </script>
  318. </body>
  319. </html>
复制代码
partition_xml_generator.txt (15.76 KB, 下载次数: 10)




上一篇:【求助】诺威达 K2001N(全志 T3)想要带 NCM 内核补丁固件
下一篇:需要泰捷WE40刷机固件
沙发
发表于 2026-10-14 10:46 | 只看该作者 | 来自北京
感谢分享
回复 支持 反对

使用道具 举报

板凳
发表于 2026-10-14 16:56 | 只看该作者 | 来自浙江
有效验功能么
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐 上一条 /2 下一条

Archiver|新帖|标签|软件|Sitemap|ZNDS智能电视网 ( 苏ICP备2023012627号 )

网络信息服务信用承诺书 | 增值电信业务经营许可证:苏B2-20221768 丨 苏公网安备 32011402011373号

GMT+8, 2026-10-14 23:54 , Processed in 0.075661 second(s), 14 queries , Redis On.

Powered by Discuz!

监督举报:report#znds.com (请将#替换为@)

© 2007-2026 ZNDS.Com

快速回复 返回顶部 返回列表