Back to Outbreak: Soycoon City
SOURCE / PINNED RELEASE

Made of little things.

Outbreak: Soycoon City

Release
db7750b1d009…
Author-recorded commit
3ee3f5fd27a6…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-b87c04571a9

Archive hash verified: dbebdc772b142a1c…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

src/files.ts
import { fs } from '@napplet/sdk';
import { runtimeHasDomain } from './domain-availability.js';
export function base64(bytes: Uint8Array): string {
  let out = ''; for (let i = 0; i < bytes.length; i += 8192) out += String.fromCharCode(...bytes.subarray(i, i + 8192)); return btoa(out);
}
export async function exportFile(name: string, content: string, encoded = false): Promise<void> {
  if (!runtimeHasDomain('fs')) throw new Error('This host has no file exports. Your map and prompt remain available here.');
  const extension = '.' + name.split('.').pop();
  const picked = await fs.pickSaveFile({ suggestedName: name, accept: [{ extension }], description: 'Export a Soyindent Evil editor file' });
  const path = picked.entries[0]?.path;
  if (!path) throw new Error('Export cancelled.');
  const data = encoded ? content : base64(new TextEncoder().encode(content));
  let first = true;
  for (let i = 0; i < data.length; i += 262144) {
    await fs.write(path, data.slice(i, i + 262144), { mode: first ? 'replace' : 'append' }); first = false;
  }
}
export async function importFile(extensions: string[], maxBytes = 8_000_000): Promise<{ name: string; data: Uint8Array }> {
  if (!runtimeHasDomain('fs')) throw new Error('This host has no file imports. Paste map JSON in the map text field instead.');
  const picked = await fs.pickFile({ accept: extensions.map(extension => ({ extension })), description: 'Choose a map or a 4:3 background image' });
  const entry = picked.entries[0];
  if (!entry) throw new Error('Import cancelled.');
  const info = await fs.stat(entry.path);
  if (info.size !== undefined && info.size > maxBytes) throw new Error(`File is too large (limit ${Math.round(maxBytes / 1e6)} MB).`);
  const chunks: Uint8Array[] = []; let offset = 0;
  for (;;) {
    const result = await fs.read(entry.path, { offset, length: 262144 });
    const chunk = Uint8Array.from(atob(result.data), c => c.charCodeAt(0)); chunks.push(chunk); offset += chunk.length;
    if (offset > maxBytes) throw new Error('File exceeds the import limit.');
    if (result.eof || !chunk.length) break;
  }
  const data = new Uint8Array(offset); let pos = 0; for (const c of chunks) { data.set(c, pos); pos += c.length; }
  return { name: entry.path.split('/').pop() ?? 'import', data };
}