diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e83b23c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +notes.txt +.gb +.gbc +.map diff --git a/README.md b/README.md index 29d4cc2..8a694a7 100644 --- a/README.md +++ b/README.md @@ -1 +1,12 @@ -# gbnp \ No newline at end of file +# GBNP: Game Boy Nintendo Power ROM Builder + +This tool can build the data necessary to flash new games to a [Nintendo Power Game Boy cart](https://en.wikipedia.org/wiki/Nintendo_Power_(cartridge)). The cartridge can store up to seven roms, or a total of 896KB of game data. For flashing, I recommend the [GBxCart](https://www.gbxcart.com/). + +## Credits +- GB Memory Maker: https://github.com/Infinest/GB-Memory-Binary-Maker + - Without this source, this project would have been much more difficult! +- NesDev Forum Thread: http://forums.nesdev.com/viewtopic.php?f=12&t=11453 +- Pixeltype Font: https://www.dafont.com/pixeltype.font +- Early GameBoy Font: https://www.dafont.com/early-gameboy.font +- Nokia Cellphone FC Font: https://www.dafont.com/nokia-cellphone.font +- Gamer Font: https://www.dafont.com/gamer-2.font diff --git a/Early-GameBoy.woff b/font/Early-GameBoy.woff similarity index 100% rename from Early-GameBoy.woff rename to font/Early-GameBoy.woff diff --git a/font/Gamer.woff b/font/Gamer.woff new file mode 100644 index 0000000..3666f02 Binary files /dev/null and b/font/Gamer.woff differ diff --git a/GillSansHeavyItalic.woff b/font/GillSansHeavyItalic.woff similarity index 100% rename from GillSansHeavyItalic.woff rename to font/GillSansHeavyItalic.woff diff --git a/font/Nokia.woff b/font/Nokia.woff new file mode 100644 index 0000000..b99617c Binary files /dev/null and b/font/Nokia.woff differ diff --git a/font/Pixeltype.woff b/font/Pixeltype.woff new file mode 100644 index 0000000..224c1a4 Binary files /dev/null and b/font/Pixeltype.woff differ diff --git a/font/Type-Writer.woff b/font/Type-Writer.woff new file mode 100644 index 0000000..68a3d63 Binary files /dev/null and b/font/Type-Writer.woff differ diff --git a/img/font.png b/img/font.png deleted file mode 100644 index 65fd19b..0000000 Binary files a/img/font.png and /dev/null differ diff --git a/img/gbmm_font.bmp b/img/gbmm_font.bmp deleted file mode 100644 index b09d7c5..0000000 Binary files a/img/gbmm_font.bmp and /dev/null differ diff --git a/index.html b/index.html index 75738d3..18e9866 100644 --- a/index.html +++ b/index.html @@ -93,12 +93,41 @@ -

3. Download Files

-
-
- +

3. Tweak Settings

+
+
+
+ Menu Font: +
+ +
+ +
-
+ +
+
+ + + + + + + + + + + +
+ +
+ +
+
+
+ +

4. Download Files

+
Download MAP File / Download GB File @@ -106,7 +135,11 @@
diff --git a/nesdev_notes.txt b/nesdev_notes.txt deleted file mode 100644 index fff017f..0000000 --- a/nesdev_notes.txt +++ /dev/null @@ -1,101 +0,0 @@ -from http://forums.nesdev.com/viewtopic.php?f=12&t=11453&start=135#p161062 - - - -GB Mapping Info -Entries start at 0x0 divided into 3 Byte segments - -For example, let's look at the start of the mapping for a multi-game cart: -A8 00 00 71 04 00 48 94 04 separates into -A8 00 00 MENU -71 04 00 GAME 1 -48 94 04 GAME 2 - -Byte 00 of each entry contains the MBC Type, ROM Size, and SRAM Size (start). -SRAM Size is 3 bits across Byte 00 and Byte 01. - MBC SZE SRAM -A8 = 101 010 00 0 MBC5, size 2 = 128KB -71 = 011 100 01 0 MBC3, size 4 = 512KB, SRAM 8KB -48 = 010 010 00 1 MBC2, size 2 = 128KB, SRAM MBC2 - -Byte 00: 1st 3 Bits = MBC Type - 000 = MBC0 - 001 = MBC1 - 010 = MBC2 - 011 = MBC3 - 101 = MBC5 - -Byte 00: 2nd 3 Bits = ROM Size (minimum size is 128KB due to block size) - 010 = Size 2 - 128KB - 011 = Size 3 - 256KB - 100 = Size 4 - 512KB - 101 = Size 5 - 1MB - -Byte 00: Last 2 Bits (bit1..bit0) + Byte 01: 1st Bit (bit7) = SRAM Size - 00 0 = NONE - 00 1 = SRAM MBC2 - 01 0 = SRAM 8KB - 01 1 = SRAM 32KB - -Byte 01 contains the SRAM Size (end) and ROM Block in the Flash. -ROM Blocks are 128KB (8 total) -Byte 01: Last 7 Bits (bit6..bit0) = ROM Block - 00 = ROM Block 0 start offset 0KB - 04 = ROM Block 1 start offset 128KB - 08 = ROM Block 2 start offset 256KB - 0C = ROM Block 3 start offset 384KB - 10 = ROM Block 4 start offset 512KB - 14 = ROM Block 5 start offset 640KB - 18 = ROM Block 6 start offset 768KB - 1C = ROM Block 7 start offset 896KB - -Byte 02 contains the RAM Block in the SRAM. -RAM Blocks are 8KB (16 total) -Byte 02: RAM Block: - 00 = RAM Block 0 start offset 0KB - 04 = RAM Block 1 start offset 8KB - 08 = RAM Block 2 start offset 16KB - 0C = RAM Block 3 start offset 24KB - and so on until RAM Block 15. - -Going back to our example cart: -A8 00 00 MENU: MBC5, size 2 = 128KB, ROM Block 0, RAM Block 0 (Ignored since SRAM is 000) -71 04 00 GAME 1: MBC3, size 4 = 512KB, SRAM 8KB, ROM Block 1, RAM Block 0 -48 94 04 GAME 2: MBC2, size 2 = 128KB, SRAM MBC2, ROM Block 5, RAM Block 1 - - - -Nintendo Power Directory for Gameboy version -Located at ROM offset 1C000h (aka 7:4000h in gameboy memory). -Entries are 200h bytes in size, the GUI supports max 8 entries at 7:4000h..7:4FFFh. -The first entry is a dummy entry for the menu, the other entries are for game(s), unused entries are FFh-filled. -The format is almost exactly same as in SNES version (but using only 200h bytes per entry, using a smaller bitmap, and without the SNES's weird overlapping bitmap tiles, and with different granularity for the ROM/SRAM values). - - 000h 1 Index (00h..07h) (or FFh=Unused) (or initially 07h for menu) - 001h 1 ROM base in 128K units - 002h 1 maybe SRAM base? in ???-units - 003h 2 ROM size in 128Kbyte units (0001h..0007h = 128K..896K) - 005h 2 SRAM size in 32-byte units (0000h,00xxh,01xxh,xxxxh=0,MBC2,8K,32K) - 007h 12 Title ASCII "DMP -xxxx- " - 013h 44 Title SHIFT-JIS - 03Fh 100h Title Bitmap (128x8 pixels, 16 tiles at 2bpp) - 13Fh 80h Zerofilled - 1BFh 10 Date ASCII "MM/DD/YYYY" - 1C9h 8 Time ASCII "HH:MM:SS" - 1D1h 8 LAW ASCII "LAWnnnnn" - 1D9h .. Unused (FFh-filled) - 1F0h 16 Unused (FFh-filled)(game entries) or "MULTICARTRIDGE 8"(menu entry) -bitmap palette: - DMG/SGB: (0=white, 1=light gray, 2=dark gray, 3=black) - CGB/GBA: (0=white, 1=dark red, 2=dark magenta, 3=black) - -For the SRAM sizes, the "xx" means that the GUI ignores those 8bit fragments. And no matter of what values that bits have, it does support only four different SRAM sizes: 0, "MBC2", 8K, 32K. Anyways, the "xx" should be probably set to some specific values - skaman, did you check which games use which SRAM size settings in the directory? - -News Ticker -Aside from the directory, the programming stations are also updating data at bank 6:4000h (ROM offset 18000h). Which contains the scrolling text that is shown at the bottom of the menu. There are at least two variants, one found in blank carts (with only the menu installed), and one other variant found in my cart with Puyo Puyo 2 installed. -Don't know how often that text has been updated. Skaman, if you compare the ROM offsets at 18000h..1BFFFFh from your cartridge collection, are there lots of different variants? - -Selftest Function -The Menu contains some selftest function which seems to be activated when pressing all four buttons plus all four DPAD directions (which normally isn't mechanically possible). The test is barely testing the MENU's memory mapping, not testing the mapping or checksums of the other installed games. And, the test is working ONLY on blank carts, for two reasons: -The checksum in cart header isn't adjusted for changed data at 6:4000h (news ticker) or 7:4000h (directory). -The bank test expects 7:4000h to contain 07h (whilst non-empty carts are storing the menu index (00h) in that location). diff --git a/script/app.js b/script/app.js index 4298c9e..592d69e 100644 --- a/script/app.js +++ b/script/app.js @@ -13,7 +13,8 @@ let app = new Vue({ processor: new Processor([]), filename: 'GBNP', mapData: '', - romData: '' + romData: '', + fontIndex: 0 }, created: function() { this.processor.menu = this.menu; @@ -24,6 +25,11 @@ let app = new Vue({ }, romOverflow: function() { return this.processor.romOverflow(); } }, + watch: { + fontIndex: function() { + for (let i = 0; i < this.roms.length; i++) { this.roms[i].updateBitmap(this.fontIndex); } + } + }, methods: { addMenu: function(e) { let fileReader = new FileReader() @@ -34,7 +40,7 @@ let app = new Vue({ }, addROM: function(e) { let fileReader = new FileReader() - fileReader.onload = () => this.roms.push(new ROM(fileReader.result)); + fileReader.onload = () => this.roms.push(new ROM(fileReader.result, this.fontIndex)); fileReader.readAsArrayBuffer(e.target.files[0]); this.processor.roms = this.roms; @@ -67,8 +73,8 @@ let app = new Vue({ }, updateMenuText: function(rom, val) { if (rom.bitmapTimeoutHandle) { clearTimeout(rom.bitmapTimeoutHandle); } - rom.bitmapTimeoutHandle = setTimeout(function() { - rom.updateMenuText(val); + rom.bitmapTimeoutHandle = setTimeout(() => { + rom.updateMenuText(val, this.fontIndex); }, 500); }, stopPropagation: function(e) { e.stopImmediatePropagation(); }, diff --git a/script/gbnp.js b/script/gbnp.js index 39024c1..5d33ec6 100644 --- a/script/gbnp.js +++ b/script/gbnp.js @@ -13,6 +13,12 @@ const BITMAP_PREVIEW_BYTES = [ [0x66, 0x66, 0x66, 0xFF], // dark grey [0x00, 0x00, 0x00, 0xFF] // black ] +const FONTS = [ + { style: 'normal 16px Pixeltype', y: 7 }, + { style: 'normal 8px Early-Gameboy', y: 7 }, + { style: 'normal 8px Nokia', y: 7 }, + { style: 'normal 16px Gamer', y: 7 } +] class Menu { constructor() { @@ -45,7 +51,7 @@ class Menu { } class ROM { - constructor(arrayBuffer) { + constructor(arrayBuffer, fontIndex) { let file = new FileSeeker(arrayBuffer); file.seek(0x134); @@ -67,7 +73,7 @@ class ROM { let paddedFile = new FileSeeker(this.arrayBuffer); paddedFile.writeBytes(file.read(file.size())); - this.updateBitmap(); + this.updateBitmap(fontIndex); // add error for "invalid" rom (IE not a gb rom file) if (!this.type) { alert('Cartridge type could not be determined!') } @@ -90,12 +96,12 @@ class ROM { return Math.trunc(Math.pow(4, this.ramByte - 1)) * 2; } - updateMenuText(text) { + updateMenuText(text, fontIndex) { this.menuText = text; - this.updateBitmap(); + this.updateBitmap(fontIndex); } - updateBitmap() { + updateBitmap(fontIndex) { let buffer = []; const canvas = document.createElement("canvas"); @@ -105,9 +111,10 @@ class ROM { ctx.imageSmoothingEnabled = false; ctx.fillStyle = 'white'; ctx.fillRect(0, 0, 96, 8); - ctx.font = '8px Early-Gameboy' - ctx.fillStyle = '#777'; - ctx.fillText(this.menuText,1,7); + const font = FONTS[fontIndex || 0]; + ctx.font = font.style; + ctx.fillStyle = 'black'; + ctx.fillText(this.menuText,1,font.y); const imageData = ctx.getImageData(0, 0, 96, 8).data; for (let i = 0; i < imageData.length; i+=16){ diff --git a/style.css b/style.css index 8eea98c..9a0759f 100644 --- a/style.css +++ b/style.css @@ -1,22 +1,43 @@ -html { - font-family: sans-serif; - line-height: 1.4; -} - @font-face { font-family: "GillSans-HeavyItalic"; - src: url("./GillSansHeavyItalic.woff") format("woff"); + src: url("./font/GillSansHeavyItalic.woff") format("woff"); font-weight: normal; font-style: normal; } @font-face { font-family: "Early-Gameboy"; - src: url("./Early-Gameboy.woff") format("woff"); + src: url("./font/Early-Gameboy.woff") format("woff"); font-weight: normal; font-style: normal; } +@font-face { + font-family: "Gamer"; + src: url("./font/Gamer.woff") format("woff"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "Pixeltype"; + src: url("./font/Pixeltype.woff") format("woff"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "Nokia"; + src: url("./font/Nokia.woff") format("woff"); + font-weight: normal; + font-style: normal; +} + +html { + font-family: sans-serif; + line-height: 1.4; +} + h1 { color: #000079; } @@ -104,11 +125,6 @@ td.overflow { background-color: red; } -#filename-label { - font-weight: bold; - margin-right: 20px; -} - thead tr { border-bottom: 1px solid #ddd; } @@ -120,3 +136,45 @@ tfoot tr { tbody tr:nth-child(odd) { background-color: #f2f2f2; } + +.settings-label { + font-weight: bold; + margin-right: 20px; +} + +label.radio-label { + vertical-align: middle; + margin-right: 10px; +} + +label#pixeltype { + font-family: 'Pixeltype'; + font-size: 32px; + line-height: 0; + position: relative; + top: 3px; +} + +label#gameboy { + font-family: 'Early-Gameboy'; +} + +label#nokia { + font-family: 'Nokia'; +} + +label#gamer { + font-family: Gamer; + font-size: 32px; + line-height: 0; + position: relative; + top: -2px; +} + +.flex-container { + display: flex; +} + +.flex-column * { + margin-bottom: 8px; +}