|
- import Utils from "/app/js/common/Utils.js";
- import ISurface from "/app/js/ifaces/ISurface.js";
- import NESBank from "/app/js/models/NESBank.js";
- import NESPalette from "/app/js/models/NESPalette.js";
-
-
- function NumToHex(n){
- var h = n.toString(16);
- if (h.length %2)
- h = '0' + h;
- return '$' + h;
- }
-
- function CompileAttribs(attrib){
- return (attrib[3] << 6) | (attrib[2] << 4) | (attrib[1] << 2) | (attrib[0] << 0)
- }
-
- function DecompileAttribs(v){
- return [
- v & 0b00000011,
- (v & 0b00001100) >> 2,
- (v & 0b00110000) >> 4,
- (v & 0b11000000) >> 6
- ];
- }
-
- export default class NESNameTable extends ISurface{
- constructor(){
- super();
- this.__bank = null;
- this.__bankrp = null;
- this.__palette = null;
- this.__tiles = [];
- this.__attribs = [];
-
- this.__undos = [];
- this.__redos = [];
-
- for (let i=0; i < 960; i++)
- this.__tiles[i] = 0;
- for (let i=0; i < 64; i++)
- this.__attribs[i] = [0,0,0,0];
- }
-
- get base64(){
- var b = "";
- for (let i = 0; i < this.__tiles.length; i++)
- b += String.fromCharCode(this.__tiles[i]);
- for (let i = 0; i < this.__attribs.length; i++)
- b += String.fromCharCode(CompileAttribs(this.__attribs[i]));
- return window.btoa(b);
- }
-
- set base64(s){
- var b = window.atob(s);
- var len = b.length;
- if (b.length !== 1024){
- throw new Error("Base64 string contains invalid byte count.");
- }
- b = new Uint8Array(b.split("").map(function(c){
- return c.charCodeAt(0);
- }));
- for (let i=0; i < b.length; i++){
- if (i < 960){
- this.__tiles[i] = b[i];
- } else {
- this.__attribs[i-960] = DecompileAttribs(b[i]);
- }
- }
- this.emit("data_changed");
- }
-
- get bank(){return this.__bank;}
- set bank(b){
- if (b !== null && !(b instanceof NESBank))
- throw new TypeError("Expected a NESBank object.");
- this.__bank = b;
- this.__bankrp = (b !== null) ? this.__bank.rp : null;
- this.emit("data_changed");
- }
-
- get palette(){return this.__palette;}
- set palette(p){
- if (p !== null && !(p instanceof NESPalette))
- throw new TypeError("Expected a NESPalette object.");
- this.__palette = p;
- this.emit("data_changed");
- }
-
- get width(){return 256;}
- get height(){return 240;}
- get length(){return this.width * this.height;}
- get undos(){return this.__undos.length;}
- get redos(){return this.__redos.length;}
-
-
- copy(b){
- this.bank = b.bank;
- this.palette = b.palette;
- for (let i=0; i < 960; i++)
- this.__tiles[i] = b.__tiles[i];
- for (let i=0; i < 64; i++){
- this.__attribs[i] = [
- b.__attribs[i][0],
- b.__attribs[i][1],
- b.__attribs[i][2],
- b.__attribs[i][3]
- ];
- }
- this.emit("data_changed");
- return this;
- }
-
- clone(){
- return (new NESNameTable()).clone(this);
- }
-
- snapshot(){
- if (this.__redos.length > 0) // Remove the redo history. We're adding a new snapshot.
- this.__redos = [];
-
- var snap = this.base64;
- if (this.__undos.length === this.__historyLength){
- this.__undos.pop();
- }
- this.__undos.splice(0,0,snap);
- return this;
- }
-
- undo(){
- if (this.__undos.length > 0){
- var usnap = this.__undos.splice(0, 1)[0];
- var rsnap = this.base64;
- this.base64 = usnap;
- if (this.__redos.length === this.__historyLength){
- this.__redos.pop();
- }
- this.__redos.splice(0,0,rsnap);
- }
- return this;
- }
-
- redo(){
- if (this.__redos.length > 0){
- var rsnap = this.__redos.splice(0,1)[0];
- var usnap = this.base64;
- this.base64 = rsnap;
- if (this.__undos.length === this.__historyLength){
- this.__undos.pop();
- }
- this.__undos.splice(0,0,usnap);
- }
- return this;
- }
-
- clearUndos(){
- this.__undos = [];
- return this;
- }
-
- clearRedos(){
- this.__redos = [];
- return this;
- }
-
- getColor(x, y){
- var pal = {pi:-1, ci:-1};
- try {
- pal = this.getColorIndex(x, y);
- } catch (e) {throw e;}
-
- if (this.__palette !== null && pal.pi >= 0 && pal.ci >= 0) {
- return this.__palette.get_palette_color(pal.pi, pal.ci);
- } else if (pal.ci >= 0){
- return NESPalette.Default(pal.ci);
- }
- return NESPalette.Default(4);
- }
-
-
- getColorIndex(x, y){
- if (x < 0 || x >= this.width || y < 0 || y >= this.height)
- throw new RangeError("Coordinates are out of bounds.");
-
- var pi = -1;
- var ci = -1;
- if (this.__bank !== null){
- var _x = Math.floor(x % 8);
- var _y = Math.floor(y % 8);
-
- var tileX = Math.floor(x / 8);
- var tileY = Math.floor(y / 8);
-
- ci = this.__bankrp[this.__tiles[(tileY * 32) + tileX]].getPixelIndex(_x, _y);
- pi = this._PaletteFromCoords(x, y);
- }
- return {pi:pi, ci:ci};
- }
-
-
- setColorIndex(x, y, ci, pi){
- if (x < 0 || x >= this.width || y < 0 || y >= this.height)
- throw new RangeError("Coordinates are out of bounds.");
- if (pi < 0 || pi >= 4)
- throw new RangeError("Palette index is out of bounds.");
-
- // NOTE: This method (setColorIndex) is called by CTRLPainter, which doesn't know about painting tile
- // indicies... therefore, CTRLPainter will still call this method for NESNameTable surfaces, but all it
- // will do is change palette indicies.
- // To paint the actual tile index, however, we'll use this emit that will be watched by the CTRLNameTable class
- // and call this class's setTileIndex() method for tile painting.
- // YAY to cheating!!
- this.emit("paint_nametable", x, y);
-
- // Then business as usual!
- var bp = this._GetAttribBlockPalette(x,y);
- if (this.__attribs[bp.bindex][bp.pindex] !== pi){
- this.__attribs[bp.bindex][bp.pindex] = pi;
- this.emit("data_changed");
- }
- return this;
- }
-
- setTileIndex(x, y, ti){
- if (ti < 0 || ti >= 256)
- throw new RangeError("Tile index is out of bounds.");
- if (x < 0 || x >= this.width || y < 0 || y >= this.height)
- throw new RangeError("Coordinates are out of bounds.");
-
- var _x = Math.floor(x / 8);
- var _y = Math.floor(y / 8);
- var tindex = (_y * 32) + _x;
- if (this.__tiles[tindex] !== ti){
- this.__tiles[tindex] = ti;
- this.emit("data_changed");
- }
- }
-
-
- eq(nt){
- return (nt instanceof NESNameTable) ? (this.base64 === nt.base64) : false;
- }
-
-
- /**
- * Generates a small 6502 assembly block string containing the current nametable data.
- * @param {string} [ntname="NameTableData"] The label name under which to store the data.
- * @returns {string}
- */
- nametable_asm(ntname="NameTableData"){
- var s = ntname + ":";
-
- for (let i=0; i < this.__tiles.length; i++){
- if (i % 32 === 0)
- s += "\n\t.db";
- s += " " + NumToHex(this.__tiles[i]);
- }
-
- return s;
- }
-
-
- /**
- * Generates a small 6502 assembly block string containing the current attribute table data.
- * @param {string} [atname="AttribTableData"] The label name under which to store the data.
- * @returns {string}
- */
- attribtable_asm(atname="AttribTableData"){
- var s = atname + ":";
-
- for (let i=0; i < this.__attribs.length; i++){
- if (i % 32 === 0)
- s += "\n\t.db";
- s += " " + NumToHex(CompileAttribs(this.__attribs[i]));
- }
-
- return s;
- }
-
-
- /**
- * Generates a small 6502 assembly block string containing the current name and attribute table data.
- * @param {string} [ntname="NameTableData"] The label name under which to store the Nametable data.
- * @param {string} [atname="AttribTableData"] the label name under which to store the Attribute table data.
- * @returns {string}
- */
- to_asm(ntname="NameTableData", atname="AttribTableData"){
- return this.nametable_asm(ntname) + "\n\n" + this.attribtable_asm(atname);
- }
-
- _GetAttribBlockPalette(x,y){
- var bp = {bindex:0, pindex:0};
-
- var blockX = Math.floor(x / 32);
- var blockY = Math.floor(y / 32);
- bp.bindex = (blockY * 8) + blockX;
-
- var palX = Math.floor(x % 32);
- var palY = Math.floor(y % 32);
- bp.pindex = ((palX < 16) ? 0 : 1) + ((palY >= 16) ? 2 : 0);
-
- return bp;
- }
-
- _PaletteFromCoords(x,y){
- var bp = this._GetAttribBlockPalette(x,y);
- return this.__attribs[bp.bindex][bp.pindex];
- }
- }
-
-
-
-
|