Home Reference Source

scripts/simple/file.js

import nstructjs from '../path-controller/util/struct.js';

import {UIBase} from '../core/ui_base.js';

export class FileHeader {
  constructor(version, magic, flags) {
    this.magic = magic;
    this.flags = flags;
    this.version_major = version ? version[0] : 0;
    this.version_minor = version ? version[1] : 0;
    this.version_micro = version ? version[2] : 0;
    this.schema = nstructjs.write_scripts();
  }
}

FileHeader.STRUCT = `
simple.FileHeader {
  magic         : static_string[4];
  version_major : short;
  version_minor : short;
  version_micro : short;
  flags         : short;
  schema        : string; 
}
`;
nstructjs.register(FileHeader);

export class FileFull extends FileHeader {
  constructor(version, magic, flags) {
    super(version, magic, flags);
    this.objects = [];
  }
}

FileFull.STRUCT = nstructjs.inherit(FileFull, FileHeader) + `
  objects : array(abstract(Object));
  screen  : abstract(Object);
}
`;
nstructjs.register(FileFull);


export class FileArgs {
  constructor(args = {}) {
    this.ext = args.ext || ".data";
    this.magic = args.magic || "STRT";
    this.doScreen = args.doScreen !== undefined ? args.doScreen : true;
    this.resetOnLoad = args.resetOnLoad !== undefined ? args.resetOnLoad : true;
    this.useJSON = args.useJSON !== undefined ? args.useJSON : false;
    this.version = args.version !== undefined ? args.version : 0;
    this.fileFlags = args.fileFlags !== undefined ? args.fileFlags : 0;
    this.fromFileOp = false; /* SimpleSaveOp and SimpleOpenOp set this to true */
  }
}

export class EmptyStruct {

}
EmptyStruct.STRUCT = `
EmptyStruct {
}
`;
nstructjs.register(EmptyStruct);

export function saveFile(appstate, args, objects) {
  if (args.useJSON === undefined) {
    args.useJSON = appstate.saveFilesInJSON;
  }

  args = new FileArgs(args);

  let version = args.version;
  if (typeof version === "number") {
    if (version === Math.floor(version)) {
      version = [version, 0, 0];
    } else {
      let major = ~~version;
      let minor = ~~(Math.fract(version)*10.0);
      let micro = (Math.fract(version) - minor) * 100.0;

      version = [major, minor, micro];
    }
  }

  let file = new FileFull(version, args.magic, args.fileFlags);

  if (args.doScreen) {
    file.screen = appstate.screen;
  } else {
    file.screen = new EmptyStruct();
  }

  for (let ob of objects) {
    file.objects.push(ob);
  }

  if (args.useJSON) {
    return nstructjs.writeJSON(file);
  } else {
    let data = [];
    nstructjs.writeObject(data, file);
    return (new Uint8Array(data)).buffer;
  }
}

export function loadFile(appstate, args, data) {
  let header;

  if (args.useJSON === undefined) {
    args.useJSON = appstate.saveFilesInJSON;
  }

  args = new FileArgs(args);

  if (!args.useJSON) {
    if (data instanceof Array) {
      data = (new Uint8Array(data)).buffer;
    }

    if (data instanceof Uint8Array) {
      data = data.buffer;
    }

    if (data instanceof ArrayBuffer) {
      data = new DataView(data);
    }

    header = nstructjs.readObject(data, FileHeader);
  } else {
    if (typeof data === "string") {
      data = JSON.parse(data);
    }

    header = nstructjs.readJSON(data, FileHeader);
  }

  if (header.magic !== args.magic) {
    throw new Error("invalid file");
  }

  let istruct = new nstructjs.STRUCT();
  istruct.parse_structs(header.schema);

  let ret;

  if (!args.useJSON) {
    ret = istruct.readObject(data, FileFull);
  } else {
    ret = istruct.readJSON(data, FileFull);
  }

  if (args.resetOnLoad) {
    appstate.reset();
  }

  if (args.doScreen) {
    if (appstate.screen) {
      appstate.screen.unlisten();
      appstate.screen.remove();
    }

    ret.screen.ctx = appstate.ctx;

    if (!(ret.screen instanceof appstate.screenClass)) {
      let screen = UIBase.createElement(appstate.screenClass.define().tagname);
      screen.ctx = appstate.ctx;

      for (let sarea of ret.screen.sareas) {
        screen.appendChild(sarea);

        sarea.area.afterSTRUCT();
        sarea.area.on_fileload();
      }

      ret.screen = screen;
    }

    appstate.screen = ret.screen;

    document.body.appendChild(appstate.screen);
    appstate.screen.listen();
  }

  return ret;
}