OCH Channel List Decoder 2

by Himbeer

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/pako/1.0.10/pako.min.js"></script>
<!DOCTYPE HTML>
<html>
  <head>
    <title>OCH Channel List Decoder</title>
  </head>
  <body>
    <h1>OCH Channel List Decoder</h1>
    <h2>For Triax-Hirschmann S-930 and OHS1720IR (possibly other models)</h2>

    <div class="formElement">
      <label for="upload">Upload .OCH File:</label>
      <input id="upload" type="file">
    </div>

    <div class="formElement">
      <label for="showRaw">Display raw data:</label>
      <input id="showRaw" type="checkbox">
    </div>

    <button id="confirm">Decode</button>

    <p id="status"></p>

    <h3>TV Channels</h3>
    <table>
      <thead>
        <tr>
          <th>Number</th><th>Channel Name</th><th>Raw Data</th>
        </tr>
      </thead>
      <tbody id="tvTable"></tbody>
    </table>
    <h3>Radio Channels</h3>
    <table>
      <thead>
        <tr>
          <th>Number</th><th>Channel Name</th><th>Raw Data</th>
        </tr>
      </thead>
      <tbody id="radioTable"></tbody>
    </table>
  </body>
</html>

CSS

html, body {
  font-family: sans-serif;
}

button {
  display: block;
}

table {
  border: solid 0.1rem black;
}

.formElement {
  margin-bottom: 1rem;
}

.tdRaw {
  font-family: monospace;
}

JavaScript

const upload = document.getElementById("upload");
const showRaw = document.getElementById("showRaw");
const confirm = document.getElementById("confirm");
const status = document.getElementById("status");
const tvTable = document.getElementById("tvTable");
const radioTable = document.getElementById("radioTable");

confirm.addEventListener("click", async () => {
	if (upload.files.length > 0) {
  	status.innerText = "Processing...";
  	const file = upload.files[0];
    const arrayBuffer = await file.arrayBuffer();
    const view = new Uint8Array(arrayBuffer);
    const decompressed = pako.inflate(view.slice(96));
    const res = convert(decompressed);
    if (res === true) {
    	status.innerText = "Done decoding file.";
    	console.log("success");
    } else {
    	status.innerText = "Failed decoding file. Reason: " + res;
    	console.log("failure");
    }
  } else {
  	alert("No file selected!");
  }
});

function readCName(arr) {
	let i = 0, str = "";
	while (arr[i] !== 0 && i < arr.length) {
  	if (!(arr[i] >= 0x85)) str += String.fromCharCode(arr[i]);
    //str += String.fromCharCode(arr[i]);
    i++;
  }
  return str;
}

function readmultiByte(array, offset, count) {
	let value = 0;
  for (let i = 0; i < count; i++) {
  	value += array[offset + i] * (256 ** (i));
  }
  return value;
}

function arrToHexStr(arr) {
	let str = "";
  arr.forEach((e, i) => {
  	str += e.toString(16).padStart(2, "0");
    if (i % 2 === 0) str += " ";
  });
  return str;
}

const b_start = 100400;
const b_end = 333644;
const b_cLen = 76;
const b_s_start = 556400;
const b_s_end = 560456;

function convert(arr) {
	const channels = [];
	const usedChannels = [];

  tvTable.innerHTML = radioTable.innerHTML = "";
	console.log("=======");
  console.log("Length: " + arr.length);
	let i = b_start;
  while (i < b_end) {
  	const part = arr.slice(i, i + b_cLen);
    const cName = readCName(part);
    const cRadio = part[23] === 1;

    //console.log(`${i} ${cName} (${cName.length})`);
    
  ...