"use strict"; var CloudSDK; (function (CloudSDK) { // Local backend endpoints const API_PREFIX = ""; // Relative to current origin async function resolveToken(token) { if (token && token.length === 5) { try { const response = await fetch(API_PREFIX + "/token/get?id=" + token); const data = await response.json(); if (data.token === token) { return data; } } catch (e) { console.error("Token check failed", e); } } return null; } CloudSDK.resolveToken = resolveToken; async function pushToStorage(token, fileName, payload) { const profile = await resolveToken(token); if (!profile || !profile.premium) { return false; } // Compression logic from original SDK const boundSize = compressBound(payload.length); const compressed = new Uint8Array(boundSize + 4); writeUint32(compressed, payload.length, 0); const compressedSize = compress(payload, compressed, 4, compressed.length); const upload = compressed.slice(0, compressedSize); const formData = new FormData(); formData.append("token", token); formData.append("fileName", fileName); formData.append("file", new Blob([upload])); try { const response = await fetch(API_PREFIX + "/saves/upload", { method: "POST", body: formData }); if (response.ok) { return true; } } catch (e) { console.error("Upload failed", e); } return false; } CloudSDK.pushToStorage = pushToStorage; async function pullFromStorage(token, fileName) { const profile = await resolveToken(token); if (profile) { try { const response = await fetch(API_PREFIX + "/saves/download/" + token + "/" + encodeURIComponent(fileName), { cache: "no-cache" }); if (response.ok) { const compressed = new Uint8Array(await response.arrayBuffer()); if (compressed.length < 4) return null; const uncompressedSize = readUint32(compressed, 0); const uncompressed = new Uint8Array(uncompressedSize); if (uncompress(compressed, uncompressed, 4) === uncompressedSize) { return uncompressed; } } } catch (e) { console.error("Download failed", e); } } return null; } CloudSDK.pullFromStorage = pullFromStorage; // LZ4 Implementation const lz4 = {}; lz4.uncompress = function (input, output, sIdx, eIdx) { sIdx = sIdx || 0; eIdx = eIdx || (input.length - sIdx); for (var i = sIdx, n = eIdx, j = 0; i < n;) { var token = input[i++]; var literals_length = (token >> 4); if (literals_length > 0) { var l = literals_length + 240; while (l === 255) { l = input[i++]; literals_length += l; } var end = i + literals_length; while (i < end) output[j++] = input[i++]; if (i === n) return j; } var offset = input[i++] | (input[i++] << 8); if (offset === 0) return j; if (offset > j) return -(i - 2); var match_length = (token & 0xf); var l = match_length + 240; while (l === 255) { l = input[i++]; match_length += l; } var pos = j - offset; var end = j + match_length + 4; while (j < end) output[j++] = output[pos++]; } return j; }; var maxInputSize = 0x7E000000, minMatch = 4, hashLog = 16, hashShift = (minMatch * 8) - hashLog, hashSize = 1 << hashLog, copyLength = 8, lastLiterals = 5, mfLimit = copyLength + minMatch, skipStrength = 6, mlBits = 4, mlMask = (1 << mlBits) - 1, runBits = 8 - mlBits, runMask = (1 << runBits) - 1, hasher = 2654435761; // assert(hashShift === 16); var hashTable = new Int16Array(1 << 16); var empty = new Int16Array(hashTable.length); lz4.compressBound = function (isize) { return isize > maxInputSize ? 0 : (isize + (isize / 255) + 16) | 0; }; lz4.compress = function (src, dst, sIdx, eIdx) { hashTable.set(empty); return compressBlock(src, dst, 0, sIdx || 0, eIdx || dst.length); }; function compressBlock(src, dst, pos, sIdx, eIdx) { var dpos = sIdx; var dlen = eIdx - sIdx; var anchor = 0; if (src.length >= maxInputSize) throw new Error("input too large"); if (src.length > mfLimit) { var n = lz4.compressBound(src.length); if (dlen < n) throw Error("output too small: " + dlen + " < " + n); var step = 1, findMatchAttempts = (1 << skipStrength) + 3, srcLength = src.length - mfLimit; while (pos + minMatch < srcLength) { var sequenceLowBits = src[pos + 1] << 8 | src[pos]; var sequenceHighBits = src[pos + 3] << 8 | src[pos + 2]; var hash = Math.imul(sequenceLowBits | (sequenceHighBits << 16), hasher) >>> hashShift; var ref = hashTable[hash] - 1; hashTable[hash] = pos + 1; if (ref < 0 || ((pos - ref) >>> 16) > 0 || (((src[ref + 3] << 8 | src[ref + 2]) != sequenceHighBits) || ((src[ref + 1] << 8 | src[ref]) != sequenceLowBits))) { step = findMatchAttempts++ >> skipStrength; pos += step; continue; } findMatchAttempts = (1 << skipStrength) + 3; var literals_length = pos - anchor; var offset = pos - ref; pos += minMatch; ref += minMatch; var match_length = pos; while (pos < srcLength && src[pos] == src[ref]) { pos++; ref++; } match_length = pos - match_length; var token = match_length < mlMask ? match_length : mlMask; if (literals_length >= runMask) { dst[dpos++] = (runMask << mlBits) + token; for (var len = literals_length - runMask; len > 254; len -= 255) { dst[dpos++] = 255; } dst[dpos++] = len; } else { dst[dpos++] = (literals_length << mlBits) + token; } for (var i = 0; i < literals_length; i++) { dst[dpos++] = src[anchor + i]; } dst[dpos++] = offset; dst[dpos++] = (offset >> 8); if (match_length >= mlMask) { match_length -= mlMask; while (match_length >= 255) { match_length -= 255; dst[dpos++] = 255; } dst[dpos++] = match_length; } anchor = pos; } } if (anchor == 0) return 0; literals_length = src.length - anchor; if (literals_length >= runMask) { dst[dpos++] = (runMask << mlBits); for (var ln = literals_length - runMask; ln > 254; ln -= 255) { dst[dpos++] = 255; } dst[dpos++] = ln; } else { dst[dpos++] = (literals_length << mlBits); } pos = anchor; while (pos < src.length) { dst[dpos++] = src[pos++]; } return dpos; } lz4.CHUNK_SIZE = 2048; const compressBound = lz4.compressBound; const compress = lz4.compress; const uncompress = lz4.uncompress; function writeUint32(container, value, offset) { container[offset] = value & 0xFF; container[offset + 1] = (value & 0x0000FF00) >> 8; container[offset + 2] = (value & 0x00FF0000) >> 16; container[offset + 3] = (value & 0xFF000000) >> 24; return offset + 4; } function readUint32(container, offset) { return (container[offset] & 0x000000FF) | ((container[offset + 1] << 8) & 0x0000FF00) | ((container[offset + 2] << 16) & 0x00FF0000) | ((container[offset + 3] << 24) & 0xFF000000); } })(CloudSDK || (CloudSDK = {})); var CloudSDKUI; (function (CloudSDKUI) { let v8Key = localStorage.getItem("js.cloud.sdk.v8.key"); let premium = false; let renderedState = null; let state = "init"; const lang = navigator.language.substring(0, 2); const t = { en: { enter: "Enter", key: "key", toenable: "to enable cloud saves", cloudsaves: "Cloud saves", enabled: "enabled", disabled: "disabled, no", subscription: "subscription", savedincloud: "Saved in cloud", savedlocally: "Saved locally", saveerror: "Save error", }, ru: { enter: "Введите", key: "ключ", toenable: "чтобы включить облачные сохранения", cloudsaves: "Облачные сохранения", enabled: "включены", disabled: "выключены, отсутствует", subscription: "подписка", savedincloud: "Сохранено в облаке", savedlocally: "Сохранено локально", saveerror: "Ошибка сохранения", }, }[lang === "ru" ? "ru" : "en"]; function Loading() { return { html: `
`, bind: () => { }, unbind: () => { }, }; } function Init() { return { html: `
`, bind: () => { if (v8Key === null) { state = "nokey"; } else { CloudSDK.resolveToken(v8Key).then((token) => { var _a; premium = (_a = token === null || token === void 0 ? void 0 : token.premium) !== null && _a !== void 0 ? _a : false; state = "key"; }).catch(() => { state = "nokey"; }); } }, unbind: () => { }, }; } function NoKey() { function onKeyChange(event) { const input = event.target; const value = input.value; if (value.length === 5) { state = "loading"; CloudSDK.resolveToken(value).then((token) => { var _a; if (token) { v8Key = value; localStorage.setItem("js.cloud.sdk.v8.key", value); if (premium) { location.reload(); } else { state = "key"; } } else { v8Key = null; localStorage.removeItem("js.cloud.sdk.v8.key"); input.value = ""; state = "nokey"; } premium = (_a = token === null || token === void 0 ? void 0 : token.premium) !== null && _a !== void 0 ? _a : false; }).catch(() => { state = "nokey"; }); } } function onPaste(event) { setTimeout(() => { onKeyChange(event); }, 100); } return { html: `
${t.enter} ${t.key} ${t.toenable}
`, bind: (root) => { const input = root.querySelector(".key-input"); input.value = ""; input.addEventListener("input", onKeyChange); input.addEventListener("paste", onPaste); }, unbind: (root) => { const input = root.querySelector(".key-input"); input.removeEventListener("input", onKeyChange); input.removeEventListener("paste", onPaste); }, }; } function Key() { function reset() { v8Key = null; localStorage.removeItem("js.cloud.sdk.v8.key"); state = "nokey"; } ; const subscriptionLink = ""; return { html: `
${t.cloudsaves}: ${premium ? t.enabled : t.disabled} ${subscriptionLink}
`, bind: (root) => { var _a; (_a = root.querySelector(".cloud-saves-clear")) === null || _a === void 0 ? void 0 : _a.addEventListener("click", reset); }, unbind: (root) => { var _a; (_a = root.querySelector(".cloud-saves-clear")) === null || _a === void 0 ? void 0 : _a.removeEventListener("click", reset); }, }; } function Hidden() { return { html: "", bind: (root) => { root.style.display = "none"; }, unbind: (root) => { root.style.display = "flex"; }, }; } function SavedInCloud() { return { html: `
${t.savedincloud}
`, bind: () => { setTimeout(() => { state = "hidden"; }, 3000); }, unbind: () => { }, }; } function SavedLocally() { return { html: `
${t.savedlocally}
`, bind: () => { setTimeout(() => { state = "hidden"; }, 3000); }, unbind: () => { }, }; } function SaveError() { return { html: `
${t.saveerror}
`, bind: () => { setTimeout(() => { state = "hidden"; }, 3000); }, unbind: () => { }, }; } function render() { if (renderedState === state) { return null; } renderedState = state; switch (state) { case "loading": return Loading(); case "init": return Init(); case "nokey": return NoKey(); case "key": return Key(); case "hidden": return Hidden(); case "saved-in-cloud": return SavedInCloud(); case "saved-locally": return SavedLocally(); case "save-error": return SaveError(); } } async function pushToStorage(fileName, payload) { state = "loading"; try { const saved = await (async () => { if (!v8Key || !premium) { return false; } return CloudSDK.pushToStorage(v8Key, fileName, payload); })(); state = saved ? "saved-in-cloud" : "saved-locally"; return saved; } catch (e) { console.error(e); state = "save-error"; return false; } } CloudSDKUI.pushToStorage = pushToStorage; async function pullFromStorage(fileName) { if (!v8Key || !premium) { throw new Error("Not logged in or not premium"); } return CloudSDK.pullFromStorage(v8Key, fileName); } CloudSDKUI.pullFromStorage = pullFromStorage; function mount() { return new Promise((resolve) => { const style = document.createElement('style'); style.textContent = cssStyles; document.head.appendChild(style); const root = document.createElement('div'); root.id = 'cloud-saves'; document.body.appendChild(root); let { html, bind, unbind } = render(); root.innerHTML = html; bind(root); let resolved = false; setInterval(() => { const tuple = render(); if (tuple) { unbind(root); html = tuple.html; bind = tuple.bind; unbind = tuple.unbind; root.innerHTML = html; bind(root); if (!resolved) { resolved = true; resolve(() => { state = "hidden"; }); } } }, 150); }); } CloudSDKUI.mount = mount; const cssStyles = ` #cloud-saves { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); z-index: 9999; background-color: rgba(69, 69, 78, 0.5); padding: 8px 16px; border-radius: 5px; color: white; font-family: sans-serif; font-size: 14px; backdrop-filter: blur(4px); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); border: 1px solid rgba(255, 255, 255, 0.1); display: flex; align-items: center; justify-content: center; gap: 8px; text-align: center; } #cloud-saves a { text-decoration: underline; color: #ffeb3b; } .cloud-saves-enabled { color: #39ff14; font-weight: bold; } .cloud-saves-disabled { color: #ff3939; } a.cloud-saves-disabled { margin-left: -4px; color: #ff3939 !important; } .cloud-saves-spinner { width: 20px; height: 20px; border: 2px solid white; border-top: 2px solid transparent; border-radius: 50%; } .cloud-saves-spinner-inner { width: 100%; height: 100%; border: 2px solid transparent; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .cloud-saves-spinner { animation: spin 1s linear infinite; } .cloud-saves-no-key { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; } .key-input { width: 50px; height: 20px; background-color: #1a1a1a; color: #ffffff; border: none; border-radius: 4px; padding: 4px; text-align: center; } .cloud-saves-key { display: flex; flex-direction: row; align-items: center; justify-content: center; gap: 8px; } .cloud-saves-clear { background-color: transparent; border: none; padding: 0; color: white; cursor: pointer; width: 20px; height: 20px; } .cloud-saves-saved-in-cloud { color: #39ff14; font-weight: bold; } .cloud-saves-saved-locally { color: #ffeb3b; font-weight: bold; } .cloud-saves-save-error { color: #ff3939; font-weight: bold; } `; })(CloudSDKUI || (CloudSDKUI = {})); (function (global) { global.CloudSDK = CloudSDK; global.CloudSDKUI = CloudSDKUI; })(typeof window !== "undefined" ? window : this);