mirror of
https://github.com/birth1mark/orbis8.git
synced 2026-10-29 00:00:34 +01:00
695 lines
21 KiB
C++
695 lines
21 KiB
C++
#include <string>
|
|
#include <sstream>
|
|
#include <cstring>
|
|
#include <vector>
|
|
#include <stack>
|
|
#include <array>
|
|
#include <algorithm>
|
|
#include <regex>
|
|
|
|
#include "lodepng.h"
|
|
|
|
#include "cart.h"
|
|
#include "filehelpers.h"
|
|
|
|
#include "stringToDataHelpers.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "logger.h"
|
|
|
|
#include "emojiconversion.h"
|
|
|
|
#include "DefaultCart.h"
|
|
|
|
#include "FakoSettings.h"
|
|
|
|
|
|
static char const *legacyCompressionLut = "\n 0123456789abcdefghijklmnopqrstuvwxyz!#%(){}[]<>+=/*:;.,~_";
|
|
static std::regex _includeRegex = std::regex("\\s*#include\\s+([\\\\/A-Za-z0-9_\\-\\.]+)");
|
|
|
|
//https://github.com/samhocevar/zepto8/blob/b1a13516945c49e47495c739e6a43a241ad99291/src/pico8/code.cpp
|
|
// Move to front structure
|
|
struct move_to_front
|
|
{
|
|
move_to_front()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
void reset()
|
|
{
|
|
for (int n = 0; n < 256; ++n)
|
|
state[n] = uint8_t(n);
|
|
}
|
|
|
|
// Get the nth byte and move it to front
|
|
uint8_t get(int n)
|
|
{
|
|
std::rotate(state.begin(), state.begin() + n, state.begin() + n + 1);
|
|
return state.front();
|
|
}
|
|
|
|
// Find index of a given byte in the structure
|
|
int find(uint8_t ch)
|
|
{
|
|
auto val = std::find(state.begin(), state.end(), ch);
|
|
return int(std::distance(state.begin(), val));
|
|
}
|
|
|
|
// Push a character and return its previous index, allowing the caller to compute the cost
|
|
// of the operation. This operation can be undone by pop_op().
|
|
int push_op(uint8_t ch)
|
|
{
|
|
int n = find(ch);
|
|
get(n);
|
|
ops.push(uint8_t(n));
|
|
return n;
|
|
}
|
|
|
|
// Undo an push_op() operation
|
|
void pop_op()
|
|
{
|
|
std::rotate(state.begin(), state.begin() + 1, state.begin() + ops.top() + 1);
|
|
ops.pop();
|
|
}
|
|
|
|
private:
|
|
std::array<uint8_t, 256> state;
|
|
std::stack<uint8_t> ops;
|
|
};
|
|
|
|
//implementation from zepto8 code.cpp, pxa_decompress
|
|
//https://github.com/samhocevar/zepto8/blob/b1a13516945c49e47495c739e6a43a241ad99291/src/pico8/code.cpp
|
|
static std::string pxa_decompress(uint8_t const *input)
|
|
{
|
|
size_t length = input[4] * 256 + input[5];
|
|
size_t compressed = input[6] * 256 + input[7];
|
|
|
|
size_t pos = size_t(8) * 8; // stream position in bits
|
|
auto get_bits = [&](size_t count) -> uint32_t
|
|
{
|
|
uint32_t n = 0;
|
|
for (size_t i = 0; i < count && pos < compressed * 8; ++i, ++pos)
|
|
n |= ((input[pos >> 3] >> (pos & 0x7)) & 0x1) << i;
|
|
return n;
|
|
};
|
|
|
|
move_to_front mtf;
|
|
std::string ret;
|
|
|
|
//TRACE("# Size: %d (%04x)\n", int(compressed), int(compressed));
|
|
|
|
while (ret.size() < length && pos < compressed * 8)
|
|
{
|
|
auto oldpos = pos; (void)oldpos;
|
|
|
|
if (get_bits(1))
|
|
{
|
|
int nbits = 4;
|
|
while (get_bits(1))
|
|
++nbits;
|
|
int n = get_bits(nbits) + (1 << nbits) - 16;
|
|
uint8_t ch = mtf.get(n);
|
|
if (!ch)
|
|
break;
|
|
//TRACE("%04x [%d] %s\n", int(ret.size()), int(pos-oldpos), printable(ch).c_str());
|
|
ret.push_back(char(ch));
|
|
}
|
|
else
|
|
{
|
|
int nbits = get_bits(1) ? get_bits(1) ? 5 : 10 : 15;
|
|
int offset = get_bits(nbits) + 1;
|
|
|
|
if (nbits == 10 && offset == 1)
|
|
{
|
|
uint8_t ch = get_bits(8);
|
|
while (ch)
|
|
{
|
|
ret.push_back(char(ch));
|
|
ch = get_bits(8);
|
|
}
|
|
//TRACE("%04x [%d] #%d\n", int(ret.size()), int(pos-oldpos), int(pos-oldpos-21) / 8);
|
|
}
|
|
else
|
|
{
|
|
int n, len = 3;
|
|
do
|
|
len += (n = get_bits(3));
|
|
while (n == 7);
|
|
|
|
//TRACE("%04x [%d] %d@-%d\n", int(ret.size()), int(pos-oldpos), len, offset);
|
|
for (int i = 0; i < len; ++i)
|
|
ret.push_back(ret[ret.size() - offset]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define HEADERLEN 8
|
|
|
|
bool Cart::loadCartFromPng(std::vector<unsigned char> image) {
|
|
//160x205 == 32800 == 0x8020
|
|
//0x8000 is actual used data size
|
|
size_t imageBytes = image.size();
|
|
|
|
uint8_t version = 0;
|
|
|
|
for(size_t i = 0; i < imageBytes; i += 4) {
|
|
if (i + 3 >= imageBytes) {
|
|
break;
|
|
}
|
|
//get argb values
|
|
uint8_t r = image[i];
|
|
uint8_t g = image[i + 1];
|
|
uint8_t b = image[i + 2];
|
|
uint8_t a = image[i + 3];
|
|
|
|
//get lower bits where pico data is encoded
|
|
a = a & 0x0003;
|
|
r = r & 0x0003;
|
|
g = g & 0x0003;
|
|
b = b & 0x0003;
|
|
|
|
uint8_t extractedByte = (a << 6) + (r << 4) + (g << 2) + b;
|
|
|
|
//TODO: write this all to one array, then memcpy to picoram?
|
|
//store extracted byte in correct place
|
|
size_t picoDataIdx = i / 4;
|
|
if (picoDataIdx < 0x2000) {
|
|
CartRom.SpriteSheetData[picoDataIdx] = extractedByte;
|
|
}
|
|
else if (picoDataIdx < 0x3000) {
|
|
CartRom.MapData[picoDataIdx - 0x2000] = extractedByte;
|
|
}
|
|
else if (picoDataIdx < 0x3100) {
|
|
CartRom.SpriteFlagsData[picoDataIdx - 0x3000] = extractedByte;
|
|
}
|
|
else if (picoDataIdx < 0x3200) {
|
|
size_t offset = picoDataIdx - 0x3100;
|
|
size_t songIdx = offset / sizeof(song);
|
|
size_t channelIdx = offset % sizeof(song);
|
|
CartRom.SongData[songIdx].data[channelIdx] = extractedByte;
|
|
}
|
|
else if (picoDataIdx < 0x4300) {
|
|
size_t offset = picoDataIdx - 0x3200;
|
|
size_t sfxIdx = offset / sizeof(sfx);
|
|
size_t byteIdx = offset % sizeof(sfx);
|
|
CartRom.SfxData[sfxIdx].data[byteIdx] = extractedByte;
|
|
}
|
|
else if (picoDataIdx < 0x8000) {
|
|
CartLuaData[picoDataIdx - 0x4300] = extractedByte;
|
|
}
|
|
else if (picoDataIdx == 0x8000) {
|
|
version = extractedByte;
|
|
}
|
|
}
|
|
|
|
uint8_t compression = 0;
|
|
|
|
if (CartLuaData[0] == '\0' && CartLuaData[1] == 'p' && CartLuaData[2] == 'x' && CartLuaData[3] == 'a'){
|
|
compression = 2;
|
|
}
|
|
|
|
if (CartLuaData[0] == ':' && CartLuaData[1] == 'c' && CartLuaData[2] == ':' && CartLuaData[3] == '\0'){
|
|
compression = 1;
|
|
}
|
|
|
|
Logger_Write("Version: %d Compression %d", version, compression);
|
|
|
|
if (compression == 0) {
|
|
auto codeBlockLength = 0x8000 - 0x4300;
|
|
auto length = codeBlockLength;
|
|
|
|
auto endOfCodePtr = (uint8_t const *)std::memchr(CartLuaData, '\0', codeBlockLength);
|
|
if (endOfCodePtr) {
|
|
length = endOfCodePtr - CartLuaData;
|
|
}
|
|
|
|
LuaString = std::string((char const *)CartLuaData, length);
|
|
}
|
|
else if (compression == 1){
|
|
//from pico 8 wiki: https://pico-8.fandom.com/wiki/P8PNGFileFormat
|
|
//The first four bytes (0x4300-0x4303) are :c:\x00.
|
|
//The next two bytes (0x4304-0x4305) are the length of the decompressed code, stored MSB first.
|
|
//The next two bytes (0x4306-0x4307) are always zero.
|
|
size_t length = CartLuaData[4] * 256 + CartLuaData[5];
|
|
|
|
LuaString.resize(0);
|
|
|
|
for (size_t i = 8; i < sizeof(CartLuaData) && LuaString.length() < length; ++i){
|
|
//0x00: Copy the next byte directly to the output stream.
|
|
if(CartLuaData[i] == 0x00){
|
|
LuaString += CartLuaData[++i];
|
|
}
|
|
//0x01-0x3b: Emit a character from a lookup table
|
|
else if (CartLuaData[i] < 0x3c){
|
|
LuaString += legacyCompressionLut[CartLuaData[i] - 1];
|
|
}
|
|
//0x3c-0xff: Calculate an offset and length from this byte and the next byte,
|
|
//then copy those bytes from what has already been emitted. In other words,
|
|
//go back "offset" characters in the output stream, copy "length" characters,
|
|
//then paste them to the end of the output stream. Offset and length are
|
|
//calculated as:
|
|
// offset = (current_byte - 0x3c) * 16 + (next_byte & 0xf)
|
|
// length = (next_byte >> 4) + 2
|
|
else {
|
|
size_t offset = (CartLuaData[i] - 0x3c) * 16 + (CartLuaData[i + 1] & 0xf);
|
|
size_t length = (CartLuaData[i + 1] >> 4) + 2;
|
|
|
|
int startIndex = LuaString.length() - offset;
|
|
if (startIndex > -1) {
|
|
for(size_t j = 0; j < length; ++j) {
|
|
LuaString += LuaString[startIndex + j];
|
|
}
|
|
}
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
}
|
|
else if (compression == 2){
|
|
LuaString = pxa_decompress(CartLuaData);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Cart::loadCartFromPng(const unsigned char* cartData, size_t size){
|
|
std::vector<unsigned char> image; //the raw pixels
|
|
unsigned width, height;
|
|
|
|
//decode
|
|
unsigned error = lodepng::decode(image, width, height, cartData, size);
|
|
|
|
//if there's an error, display it
|
|
if(error) {
|
|
LoadError = "png decoder error " + std::string(lodepng_error_text(error));
|
|
Logger_Write("%s%s", LoadError.c_str(), "\n");
|
|
return false;
|
|
}
|
|
|
|
if (width != 160 || height != 205) {
|
|
LoadError = "Invalid png dimensions";
|
|
Logger_Write("invalid dimensions\n");
|
|
return false;
|
|
}
|
|
|
|
return loadCartFromPng(image);
|
|
}
|
|
|
|
|
|
bool Cart::loadCartFromPng(std::string filename){
|
|
std::vector<unsigned char> image; //the raw pixels
|
|
unsigned width, height;
|
|
|
|
//decode
|
|
unsigned error = lodepng::decode(image, width, height, filename);
|
|
//the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it,
|
|
|
|
//if there's an error, display it
|
|
if(error) {
|
|
LoadError = "png decoder error " + std::string(lodepng_error_text(error));
|
|
Logger_Write("%s%s", LoadError.c_str(), "\n");
|
|
return false;
|
|
}
|
|
|
|
if (width != 160 || height != 205) {
|
|
LoadError = "Invalid png dimensions";
|
|
Logger_Write("invalid dimensions\n");
|
|
return false;
|
|
}
|
|
|
|
return loadCartFromPng(image);
|
|
}
|
|
|
|
bool Cart::loadCartFromString(std::string cartStr) {
|
|
fullCartText = cartStr;
|
|
|
|
std::istringstream s(cartStr);
|
|
std::string line;
|
|
std::string currSec = "";
|
|
std::smatch sm;
|
|
|
|
while (std::getline(s, line)) {
|
|
line = utils::trimright(line, " \n\r");
|
|
line = charset::utf8_to_pico8(line);
|
|
//line = convert_emojis(line);
|
|
|
|
if (line.length() > 2 && line[0] == '_' && line[1] == '_') {
|
|
currSec = line;
|
|
}
|
|
else if (currSec == "__lua__"){
|
|
if (std::regex_match(line, sm, _includeRegex)) {
|
|
auto dir = getDirectory(FullCartPath);
|
|
auto fullPath = dir + "/" + sm[1].str();
|
|
|
|
auto includeContents = get_file_contents(fullPath);
|
|
if (includeContents.length() > 0){
|
|
includeContents = charset::utf8_to_pico8(includeContents);
|
|
LuaString += includeContents + "\n";
|
|
}
|
|
else{
|
|
//todo: report error
|
|
//error: can't find included file
|
|
LoadError = "Can't find included file";
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
LuaString += line + "\n";
|
|
}
|
|
}
|
|
else if (currSec == "__gfx__"){
|
|
SpriteSheetString += line + "\n";
|
|
}
|
|
else if (currSec == "__gff__"){
|
|
SpriteFlagsString += line + "\n";
|
|
}
|
|
else if (currSec == "__map__"){
|
|
MapString += line + "\n";
|
|
}
|
|
else if (currSec == "__sfx__"){
|
|
SfxString += line + "\n";
|
|
}
|
|
else if (currSec == "__music__"){
|
|
MusicString += line + "\n";
|
|
}
|
|
else if (currSec == "__label__"){
|
|
LabelString += line + "\n";
|
|
}
|
|
}
|
|
|
|
Logger_Write("Setting cart graphics rom data from strings\n");
|
|
setSpriteSheet(SpriteSheetString);
|
|
setSpriteFlags(SpriteFlagsString);
|
|
setMapData(MapString);
|
|
|
|
Logger_Write("Setting cart audio rom data from strings\n");
|
|
setSfx(SfxString);
|
|
setMusic(MusicString);
|
|
|
|
return true;
|
|
}
|
|
|
|
Cart::Cart (const unsigned char* cartData, size_t size){
|
|
if (size < 5) {
|
|
LoadError = "Invalid cart. Less than 5 bytes";
|
|
return;
|
|
}
|
|
|
|
//TODO: check if it is a text cart, decode string, then parse
|
|
if ((char)cartData[0] == '\x89' &&
|
|
(char)cartData[1] == 'P' &&
|
|
(char)cartData[2] == 'N' &&
|
|
(char)cartData[3] == 'G') {
|
|
bool success = loadCartFromPng(cartData, size);
|
|
|
|
if (!success){
|
|
return;
|
|
}
|
|
LoadError = "";
|
|
Logger_Write("got valid png cart\n");
|
|
}
|
|
else if((char)cartData[0] == 'p' &&
|
|
(char)cartData[1] == 'i' &&
|
|
(char)cartData[2] == 'c' &&
|
|
(char)cartData[3] == 'o') {
|
|
std::string strContents(reinterpret_cast<const char*>(cartData), size);
|
|
|
|
bool success = loadCartFromString(strContents);
|
|
|
|
if (!success){
|
|
return;
|
|
}
|
|
LoadError = "";
|
|
Logger_Write("got valid p8 cart\n");
|
|
}
|
|
else {
|
|
LoadError = "unknown cart file format";
|
|
}
|
|
|
|
}
|
|
|
|
//tac08 based cart parsing and stripping of emoji
|
|
Cart::Cart(std::string filename, std::string cartDirectory){
|
|
//the leading # indicates it is the BBS key. In the future, it would be nice to fetch them,
|
|
//but for now expect the user to supply the carts
|
|
if (filename.length() > 0 && filename[0] == '#') {
|
|
filename = filename.substr(1);
|
|
}
|
|
|
|
std::string fileExtension = getFileExtension(filename);
|
|
|
|
if (fileExtension == "") {
|
|
filename = filename + ".p8";
|
|
}
|
|
|
|
// Check for built-in carts before prepending cart directory
|
|
bool isDefaultCart = (filename == "__FAKE08-DEFAULT.p8");
|
|
bool isSettingsCart = (filename == "__FAKE08-SETTINGS.p8");
|
|
|
|
if (isDefaultCart || isSettingsCart) {
|
|
// Built-in carts don't need a directory path
|
|
FullCartPath = filename;
|
|
}
|
|
else if (cartDirectory.length() > 0 && ! isAbsolutePath(filename)) {
|
|
FullCartPath = cartDirectory + "/" + filename;
|
|
}
|
|
else {
|
|
FullCartPath = filename;
|
|
}
|
|
//zero out cart rom so no garbage is left over
|
|
initCartRom();
|
|
|
|
Logger_Write("getting file contents for %s\n", FullCartPath.c_str());
|
|
|
|
std::string firstFourChars = get_first_four_chars(FullCartPath);
|
|
|
|
if ((fileExtension == "" || fileExtension == ".p8") && firstFourChars.length() == 0
|
|
&& !isDefaultCart && !isSettingsCart) {
|
|
//fallback for cart loading by key - support .p8 and .p8.png
|
|
filename = filename + ".png";
|
|
// Rebuild full path with cart directory (same logic as initial path build)
|
|
if (cartDirectory.length() > 0 && !isAbsolutePath(filename)) {
|
|
FullCartPath = cartDirectory + "/" + filename;
|
|
} else {
|
|
FullCartPath = filename;
|
|
}
|
|
firstFourChars = get_first_four_chars(FullCartPath);
|
|
}
|
|
|
|
// For non-built-in carts, check if file exists first
|
|
if (!isDefaultCart && !isSettingsCart) {
|
|
if (firstFourChars.length() == 0) {
|
|
LoadError = "cart file not found: " + FullCartPath;
|
|
Logger_Write("%s\n", LoadError.c_str());
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (isDefaultCart || isSettingsCart || firstFourChars == "pico"){
|
|
std::string cartStr;
|
|
|
|
if (isDefaultCart) {
|
|
Logger_Write("Loading built-in default cart\n");
|
|
cartStr = fake08DefaultCart;
|
|
}
|
|
else if (isSettingsCart) {
|
|
Logger_Write("Loading built-in settings cart\n");
|
|
cartStr = fake08SettingsP8;
|
|
}
|
|
else {
|
|
cartStr = get_file_contents(FullCartPath.c_str());
|
|
// File existence already checked above, but double-check just in case
|
|
if (cartStr.length() == 0) {
|
|
LoadError = "cart file not found: " + FullCartPath;
|
|
Logger_Write("%s\n", LoadError.c_str());
|
|
return;
|
|
}
|
|
}
|
|
Logger_Write("Got file contents... parsing cart\n");
|
|
|
|
bool success = loadCartFromString(cartStr);
|
|
|
|
if (!success){
|
|
return;
|
|
}
|
|
}
|
|
else if (firstFourChars == "\x89PNG") {
|
|
bool success = loadCartFromPng(FullCartPath);
|
|
|
|
if (!success){
|
|
return;
|
|
}
|
|
|
|
LoadError = "";
|
|
Logger_Write("got valid png cart\n");
|
|
}
|
|
else {
|
|
// File exists but format is unknown
|
|
LoadError = "unknown cart file format: " + FullCartPath;
|
|
Logger_Write("%s\n", LoadError.c_str());
|
|
return;
|
|
}
|
|
}
|
|
|
|
Cart::~Cart(){
|
|
|
|
}
|
|
|
|
void Cart::initCartRom(){
|
|
//zero out cart rom so no garbage is left over
|
|
for(size_t i = 0; i < sizeof(CartRom.SpriteSheetData); i++) {
|
|
CartRom.SpriteSheetData[i] = 0;
|
|
}
|
|
for(size_t i = 0; i < sizeof(CartRom.SpriteFlagsData); i++) {
|
|
CartRom.SpriteFlagsData[i] = 0;
|
|
}
|
|
for(size_t i = 0; i < sizeof(CartRom.MapData); i++) {
|
|
CartRom.MapData[i] = 0;
|
|
}
|
|
for(size_t i = 0; i < 64; i++) {
|
|
CartRom.SfxData[i] = {0};
|
|
}
|
|
for(size_t i = 0; i < 64; i++) {
|
|
CartRom.SongData[i] = {0};
|
|
}
|
|
}
|
|
|
|
void Cart::setSpriteSheet(std::string spritesheetstring){
|
|
Logger_Write("Copying data to spritesheet\n");
|
|
copy_string_to_sprite_memory(CartRom.SpriteSheetData, spritesheetstring);
|
|
}
|
|
|
|
void Cart::setSpriteFlags(std::string spriteFlagsstring){
|
|
Logger_Write("Copying data to sprite flags\n");
|
|
copy_string_to_memory(CartRom.SpriteFlagsData, spriteFlagsstring);
|
|
}
|
|
|
|
void Cart::setMapData(std::string mapDataString){
|
|
Logger_Write("Copying data to map data\n");
|
|
copy_string_to_memory(CartRom.MapData, mapDataString);
|
|
}
|
|
|
|
void Cart::setMusic(std::string musicString){
|
|
std::istringstream s(musicString);
|
|
std::string line;
|
|
char buf[3] = {0};
|
|
int musicIdx = 0;
|
|
|
|
while (std::getline(s, line)) {
|
|
if (line.length() < 11){
|
|
continue;
|
|
}
|
|
|
|
buf[0] = line[0];
|
|
buf[1] = line[1];
|
|
uint8_t flagByte = (uint8_t)strtol(buf, NULL, 16);
|
|
|
|
uint8_t mode = (flagByte & 8) >> 3;
|
|
uint8_t fstop = (flagByte & 4) >> 2;
|
|
uint8_t frepeat = (flagByte & 2) >> 1;
|
|
uint8_t fnext = flagByte & 1;
|
|
|
|
buf[0] = line[3];
|
|
buf[1] = line[4];
|
|
uint8_t channel1byte = (uint8_t)strtol(buf, NULL, 16);
|
|
|
|
buf[0] = line[5];
|
|
buf[1] = line[6];
|
|
uint8_t channel2byte = (uint8_t)strtol(buf, NULL, 16);
|
|
|
|
buf[0] = line[7];
|
|
buf[1] = line[8];
|
|
uint8_t channel3byte = (uint8_t)strtol(buf, NULL, 16);
|
|
|
|
buf[0] = line[9];
|
|
buf[1] = line[10];
|
|
uint8_t channel4byte = (uint8_t)strtol(buf, NULL, 16);
|
|
|
|
CartRom.SongData[musicIdx].data[0] = channel1byte | fnext << 7;
|
|
CartRom.SongData[musicIdx].data[1] = channel2byte | frepeat << 7;
|
|
CartRom.SongData[musicIdx].data[2] = channel3byte | fstop << 7;
|
|
CartRom.SongData[musicIdx].data[3] = channel4byte | mode << 7;
|
|
|
|
musicIdx++;
|
|
if (musicIdx >= 64) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void Cart::setSfx(std::string sfxString) {
|
|
std::istringstream s(sfxString);
|
|
std::string line;
|
|
char buf[3] = {0};
|
|
int sfxIdx = 0;
|
|
|
|
//SFX speed defaults to 16. Everything else should be zeroed out
|
|
for (int i = 0; i < 64; i++) {
|
|
CartRom.SfxData[i].speed = 16;
|
|
}
|
|
|
|
while (std::getline(s, line)) {
|
|
buf[0] = line[0];
|
|
buf[1] = line[1];
|
|
uint8_t filters = (uint8_t)strtol(buf, NULL, 16);
|
|
|
|
buf[0] = line[2];
|
|
buf[1] = line[3];
|
|
uint8_t noteDuration = (uint8_t)strtol(buf, NULL, 16);
|
|
|
|
buf[0] = line[4];
|
|
buf[1] = line[5];
|
|
uint8_t loopRangeStart = (uint8_t)strtol(buf, NULL, 16);
|
|
|
|
buf[0] = line[6];
|
|
buf[1] = line[7];
|
|
uint8_t loopRangeEnd = (uint8_t)strtol(buf, NULL, 16);
|
|
|
|
CartRom.SfxData[sfxIdx].filters = filters;
|
|
CartRom.SfxData[sfxIdx].speed = noteDuration;
|
|
CartRom.SfxData[sfxIdx].loopRangeStart = loopRangeStart;
|
|
CartRom.SfxData[sfxIdx].loopRangeEnd = loopRangeEnd;
|
|
|
|
//32 notes, 5 chars each
|
|
int noteIdx = 0;
|
|
for (int i = 8; i < 168; i+=5) {
|
|
buf[0] = line[i];
|
|
buf[1] = line[i + 1];
|
|
uint8_t key = (uint8_t)strtol(buf, NULL, 16);
|
|
|
|
|
|
buf[0] = '0';
|
|
buf[1] = line[i + 2];
|
|
uint8_t waveform = (uint8_t)strtol(buf, NULL, 16);
|
|
uint8_t custom = waveform > 7 ? 1 : 0;
|
|
|
|
buf[0] = '0';
|
|
buf[1] = line[i + 3];
|
|
uint8_t volume = (uint8_t)strtol(buf, NULL, 16);
|
|
|
|
buf[0] = '0';
|
|
buf[1] = line[i + 4];
|
|
uint8_t effect = (uint8_t)strtol(buf, NULL, 16);
|
|
|
|
CartRom.SfxData[sfxIdx].notes[noteIdx].setKey(key);
|
|
CartRom.SfxData[sfxIdx].notes[noteIdx].setWaveform(waveform);
|
|
CartRom.SfxData[sfxIdx].notes[noteIdx].setVolume(volume);
|
|
CartRom.SfxData[sfxIdx].notes[noteIdx].setEffect(effect);
|
|
CartRom.SfxData[sfxIdx].notes[noteIdx].setCustom(custom);
|
|
|
|
noteIdx++;
|
|
}
|
|
|
|
sfxIdx++;
|
|
}
|
|
}
|
|
|
|
|