mirror of
https://github.com/orangeglo/gbnp.git
synced 2026-04-18 14:37:34 -05:00
Add multiple font options
This commit is contained in:
parent
b81a456a95
commit
cb44d8a83b
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.DS_Store
|
||||
notes.txt
|
||||
.gb
|
||||
.gbc
|
||||
.map
|
||||
13
README.md
13
README.md
|
|
@ -1 +1,12 @@
|
|||
# gbnp
|
||||
# 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
|
||||
|
|
|
|||
BIN
font/Gamer.woff
Normal file
BIN
font/Gamer.woff
Normal file
Binary file not shown.
BIN
font/Nokia.woff
Normal file
BIN
font/Nokia.woff
Normal file
Binary file not shown.
BIN
font/Pixeltype.woff
Normal file
BIN
font/Pixeltype.woff
Normal file
Binary file not shown.
BIN
font/Type-Writer.woff
Normal file
BIN
font/Type-Writer.woff
Normal file
Binary file not shown.
BIN
img/font.png
BIN
img/font.png
Binary file not shown.
|
Before Width: | Height: | Size: 458 B |
Binary file not shown.
|
Before Width: | Height: | Size: 6.1 KiB |
45
index.html
45
index.html
|
|
@ -93,12 +93,41 @@
|
|||
</table>
|
||||
</section>
|
||||
|
||||
<h2>3. Download Files</h2>
|
||||
<section>
|
||||
<div>
|
||||
<label id="filename-label" for="filename-input">Filename: </label><input id="filename-input" type="text" v-model:value="filename"/>
|
||||
<h2>3. Tweak Settings </h2>
|
||||
<section class="flex-container">
|
||||
<div class="flex-column">
|
||||
<div>
|
||||
<span class="settings-label">Menu Font: </span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="settings-label" id="filename-label" for="filename-input">Filename: </label>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<div class="flex-column">
|
||||
<div>
|
||||
<input type="radio" id="font0" name="font" value="0" v-model="fontIndex">
|
||||
<label for="font0" id="pixeltype" class="radio-label">Pixeltype</label>
|
||||
|
||||
<input type="radio" id="font1" name="font" value="1" v-model="fontIndex">
|
||||
<label for="font1" id="gameboy" class="radio-label">Game Boy</label>
|
||||
|
||||
<input type="radio" id="font2" name="font" value="2" v-model="fontIndex">
|
||||
<label for="font2" id="nokia" class="radio-label">Nokia</label>
|
||||
|
||||
<input type="radio" id="font3" name="font" value="3" v-model="fontIndex">
|
||||
<label for="font3" id="gamer" class="radio-label">Gamer</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input id="filename-input" type="text" v-model:value="filename"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<h2>4. Download Files</h2>
|
||||
<section>
|
||||
<a class='download-link' :download="filename + '.map'" :href="downloadEnabled ? mapData : null" type="application/octet-stream" v-on:click="downloadMapFile" :class="{ disabled: !downloadEnabled }">Download MAP File</a>
|
||||
/
|
||||
<a class='download-link' :download="filename + '.gb'" :href="downloadEnabled ? romData : null" type="application/octet-stream" v-on:click="downloadRomFile" :class="{ disabled: !downloadEnabled }">Download GB File</a>
|
||||
|
|
@ -106,7 +135,11 @@
|
|||
</div>
|
||||
|
||||
<footer>
|
||||
v0.1 - built by orangeglo - <a href="https://github.com/orangeglo/gbnp">view this project on github</a>
|
||||
v0.1
|
||||
-
|
||||
built by orangeglo
|
||||
-
|
||||
<a href="https://github.com/orangeglo/gbnp">view this project on github</a>
|
||||
</footer>
|
||||
|
||||
<script src="script/app.js"></script>
|
||||
|
|
|
|||
101
nesdev_notes.txt
101
nesdev_notes.txt
|
|
@ -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).
|
||||
|
|
@ -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(); },
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
82
style.css
82
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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user