bin/
+tools/png2bin/bin
+tools/png2bin/lib
+tools/png2bin/obj
+tools/png2bin/src/include/*.gch
+tools/png2bin/src/*.txt
+tools/png2bin/*.sprite
P0STARTY = $32
P1STARTX = 0
P1STARTY = 0
-M0HEIGHT = 8
+M0HEIGHT = 4
M0RESPAWNT = 255
+MAPCOUNT = 0
+OFFSETPERMAP = 6
;===============================================================================
; Define Start of Cartridge
sta WSYNC
sta VSYNC
- lda #$30 ;
- sta NUSIZ0 ; set missile0 to be 8x
+ lda #%00100000 ;
+ sta NUSIZ0 ; set missile0 to be 2x
sta NUSIZ1
Sleep12 ; jsr here to sleep for 12 cycles
rts
jmp Reset
NoReset
jsr ResetPPositions
+ jsr SetM0Pos
NoP0P1Collision ; p0 and p1 did not collide!
; now we dio p0 m0 collision. m0 must be collected by turtle to advance
adc Level
; score is 3 + level
sta Score
+
+ ; now we roll for next map
+ jsr Random
+ lda Rand8
+ and #MAPCOUNT ; only allow MAPCOUNT for roll
+ tay
+ cpy #0 ; 0 does not require an offset
+ beq NextMapDone
+
+ lda #OFFSETPERMAP
+ sta Temp
+ lda #0
+ ; now add 6 for each number rolled
+NextMapLoop
+ adc Temp
+ dey
+ bne NextMapLoop
+ sta CurrentMap
+NextMapDone
rts
SetObjectColours
.byte $C6 ; green - goes into COLUP0, color for player1 and missile0
.byte $86 ; blue - goes into COLUP1, color for player0 and missile1
.byte $46 ; red - goes into COLUPF, color for playfield and ball
- .byte $00 ; black - goes into COLUBK, color for background
+ .byte $EE ; yellowish - goes into COLUBK, color for background
.byte $0E ; white - goes into COLUP0, B&W for player0 and missile0
.byte $06 ; dark grey - goes into COLUP1, B&W for player1 and missile1
.byte $0A ; light grey - goes into COLUPF, B&W for playfield and ball
--- /dev/null
+CC = g++
+CFLAGS = -g `libpng-config --cflags`
+LIBS = `libpng-config --ldflags`
+
+LIBDIR = ./lib
+IDIR = ./src/include
+ODIR = ./obj
+BINDIR = ./bin
+SRCDIR = ./src
+
+_DEPS = imageconverter.h util.h colour.h
+DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
+
+_OBJ = main.o imageconverter.o util.o colour.o
+OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
+
+$(ODIR)/%.o: $(SRCDIR)/%.cpp $(DEPS)
+ $(CC) -c -o $@ $< $(CFLAGS)
+
+png2bin: $(OBJ)
+ $(CC) -o $(BINDIR)/$@ $^ $(LIBS)
+
+.PHONY: clean
+clean:
+ rm $(ODIR)/*.o
+ rm $(BINDIR)/*
--- /dev/null
+This is a simple tool used to convert N by N sized images to byte arrays.
+I used it to simplify making sprites and maps.
+All images must be multiples of 8 in size!
--- /dev/null
+#include <ostream>
+#include"include/colour.h"
+namespace Colour {
+ // global colours
+ Modifier red(Colour::FG_RED);
+ Modifier def(Colour::FG_DEFAULT);
+
+ // class definition
+
+ Modifier::Modifier(Code pCode): code(pCode) {
+
+ }
+}
--- /dev/null
+#include"include/imageconverter.h"
+#include"include/colour.h"
+#include <exception>
+#include<string>
+#include <sstream>
+#include <iomanip>
+#include <iostream>
+#include <fstream>
+#include <bitset>
+
+using namespace Colour;
+
+namespace Image {
+ ImageConverter::ImageConverter(Settings *settings) {
+ this->settings = settings;
+ try {
+ this->image = new png::image<png::rgb_pixel>(settings->inputfile);
+ } catch(std::exception &e) {
+ std::cerr << red << "[FATAL] " << def << e.what() << std::endl;
+ exit(-1);
+ }
+
+ if(settings->verbose) {
+ std::cout << "Creating array of size: (" << image->get_width() << "/" << image->get_height() << ")" << std::endl;
+ }
+
+ /*
+ init converted array
+ this array contains every pixel -> access like this:
+ x * width + y
+ This simulates a 2d array with less of a memory mess
+ */
+ this->converted = new unsigned int[image->get_width() * image->get_height()];
+ if(!this->converted) {
+ std::cerr << red << "[FATAL] " << def
+ << "Unable to allocate enough memory for converted image!" << std::endl;
+ exit(-1);
+ }
+ }
+
+ ImageConverter::~ImageConverter() {
+ // TODO error when freeing
+ if(this->converted) {
+ delete[] this->converted;
+ }
+
+ if(this->image) {
+ delete this->image;
+ }
+ }
+
+ void ImageConverter::convertPixels() {
+ if(settings->verbose) {
+ std::cout << "Converting pixels" << std::endl;
+ }
+
+ for(int w = 0; w < this->image->get_width(); w++) {
+ for(int h = 0; h < this->image->get_height(); h++) {
+ png::rgb_pixel pixel = image->get_pixel(w, h);
+
+ // converted pixel
+ int pixelConverted = 0;
+
+ // write pixel into file if it is in pallette
+
+ // convert colour to hex string just like the user input would specify
+ std::stringstream stream;
+ stream << std::hex << std::setw(2) << std::setfill('0') << (short)pixel.red;
+ std::string hexColour = "#" + stream.str();
+
+ // reset stream for next int
+ stream.str("");
+ stream << std::hex << std::setw(2) << std::setfill('0') << (short)pixel.green;
+ hexColour = hexColour + stream.str();
+
+ stream.str("");
+ stream << std::hex << std::setw(2) << std::setfill('0') << (short)pixel.blue;
+ hexColour = hexColour + stream.str();
+
+ bool colourFound = false;
+ for(int i = 0; i < settings->coloursLen; i++) {
+ std::string colourCheck = "#";
+ stream.str("");
+ stream << std::hex << std::setw(6) << settings->colours[i];
+ colourCheck = colourCheck + stream.str();
+
+ // if colour is not in there default to 0 and output a warning
+ if(colourCheck == hexColour) {
+ if(settings->verbose) {
+ std::cout << "Pixel (" << w << "/" << h << ") "
+ << "Colour found for " << hexColour << ". Converted int: " << i << std::endl;
+ }
+ pixelConverted = i;
+ colourFound = true;
+ break;
+ }
+ }
+
+ if(!colourFound) {
+ std::cerr << "Pixel (" << w << "/" << h << ") "
+ << red << "[WARNING] " << def << "Colour " << hexColour << " not found in list. Defaulting to 0 "
+ << std::endl;
+ }
+
+ if(settings->verbose) {
+ std::cout << "Pixel (" << w << "/" << h << ") "
+ << std::setw(2) << std::setfill('0')
+ << (short)pixel.red << " " << std::setw(2) << std::setfill('0') <<
+ (short)pixel.green << " " << std::setw(2) << std::setfill('0')
+ << (short)pixel.blue
+ << " " << hexColour << std::endl;
+ }
+ this->converted[w * image->get_width() + h] = pixelConverted;
+ if(settings->verbose) {
+ std::cout << "Set " << this->converted[w * image->get_width() + h] << "(" <<
+ w * image->get_width() + h << "th element)\n\n";
+ }
+ }
+ }
+ }
+
+ void ImageConverter::writePixels() {
+ std::ofstream outfile;
+
+ outfile.open(settings->outfile);
+ if(settings->outputBase == "detailed") {
+ if(settings->verbose) std::cout << "Outputting sprite in detailed mode!\n";
+
+ // outputting to file
+ unsigned short stepCounter = image->get_width() / 8;
+ for(int sc = 0; sc < stepCounter; sc++) {
+ // each of these loops will write 8 by 8 pixels at most so we loop N times to get all arrays
+ outfile << "; Sprite of " << settings->inputfile << " Part " << sc << "/" << stepCounter << std::endl;
+ for(int x = 0; x < image->get_width(); x++) {
+ outfile << ".byte ";
+
+ for(int y = stepCounter * 8 - 8; y < 8 * stepCounter; y++) {
+ if(y < image->get_width()) {
+ outfile << converted[x * image->get_width() + y];
+ } else {
+ outfile << 0;
+ }
+ }
+ outfile << std::endl;
+ }
+ outfile << std::endl;
+ }
+
+ outfile << std::endl << std::endl;
+ }
+ outfile.close();
+ }
+}
--- /dev/null
+#ifndef COLOUR_H
+#define COLOUR_H
+
+namespace Colour {
+ enum Code {
+ FG_RED = 31,
+ FG_GREEN = 32,
+ FG_BLUE = 34,
+ FG_DEFAULT = 39,
+ BG_RED = 41,
+ BG_GREEN = 42,
+ BG_BLUE = 44,
+ BG_DEFAULT = 49
+ };
+
+ class Modifier {
+ Code code;
+ public:
+ Modifier(Code pCode);
+ friend std::ostream& operator<<(std::ostream& os, const Modifier& mod) {
+ return os << "\033[" << mod.code << "m";
+ }
+ };
+
+ extern Modifier red;
+ extern Modifier def;
+}
+
+#endif
--- /dev/null
+#ifndef PNG_CONVERTER_H
+#define PNG_CONVERTER_H
+
+#include<png++/png.hpp>
+#include<cstdint>
+#include"util.h"
+
+namespace Image {
+ class ImageConverter {
+ private:
+ png::image<png::rgb_pixel> *image;
+
+ unsigned int *converted; // converted image for each pixel
+
+ Settings *settings;
+ public:
+ ImageConverter(Settings *settings);
+
+ ~ImageConverter();
+
+ void convertPixels();
+
+ void writePixels();
+ };
+}
+
+#endif
--- /dev/null
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <vector>
+#include<iostream>
+#include<cstdint>
+
+// this struct holds program settings
+struct Settings {
+ unsigned int minorVersion;
+ unsigned int majorVersion;
+ unsigned int patch;
+ std::string appname;
+ std::string dev;
+ std::string github;
+ std::string email;
+
+ std::string appmode; // mode the app is in (sprite is the only one right now!)
+ std::string outputBase;
+ std::string inputfile;
+ std::string outfile;
+ uint32_t *colours; // pointer to as many colours are the user input
+ unsigned short coloursLen;
+ bool verbose;
+};
+
+extern Settings globalsettings;
+
+/*
+This function inits the program and settings struct
+*/
+void init();
+
+/*
+This function parses CLI arguments based on criteria
+*/
+void parseArgs(int argc, char **argv);
+
+/*
+This is just a helper function to make my life easier
+*/
+unsigned int hexStringToInt(const std::string &hexstr);
+
+/*
+This function determines if the arguments needed
+are still covered by an array with size n
+*/
+bool extraArgumentsNeeded(int needed, int argc, int index);
+
+/*
+This function splits a string at a specific char and
+returns an std::vector of all split elements
+*/
+std::vector<std::string> splitStr(const std::string &text, char sep);
+
+int setBit(int num, unsigned int bit);
+
+int unsetBit(int num, unsigned int bit);
+
+int readBit(int num, unsigned int bit);
+
+int toggleBit(int num, unsigned int bit);
+
+#endif
--- /dev/null
+#include<iostream>
+#include<stdlib.h>
+#include<stdio.h>
+#include<png++/png.hpp>
+#include"include/imageconverter.h"
+#include"include/util.h"
+
+int main(int argc, char **argv) {
+ init();
+ parseArgs(argc, argv);
+
+ // start conversion
+ Image::ImageConverter converter(&globalsettings);
+ converter.convertPixels();
+ converter.writePixels();
+}
--- /dev/null
+#include"include/util.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include"include/colour.h"
+#include<string>
+
+Settings globalsettings;
+
+using namespace Colour;
+
+void init() {
+ // init settings struct with default values
+ globalsettings.appname = "png2bin";
+ globalsettings.dev = "Lukas Krickl";
+ globalsettings.email = "lukaskrickl@gmail.com";
+ globalsettings.minorVersion = 0;
+ globalsettings.majorVersion = 0;
+ globalsettings.patch = 1;
+ globalsettings.github = "https://github.com/unlink2/png2bin";
+
+ // set default options
+ globalsettings.appmode = "sprite";
+ globalsettings.outputBase = "detailed";
+ globalsettings.colours = (uint32_t*)malloc(5 * sizeof(uint32_t));
+
+ // set default colour pallette
+ globalsettings.colours[0] = 0x000000;
+ globalsettings.colours[1] = 0xFFFFFF;
+
+ globalsettings.coloursLen = 2;
+
+ globalsettings.outfile = "./out.sprite";
+
+ globalsettings.verbose = false;
+}
+
+void parseArgs(int argc, char **argv) {
+ if(argc <= 1) {
+ std::cout << globalsettings.appname << ": No arguments given! Use -h for help!\n";
+ }
+ for(int index = 0; index < argc; index++) {
+ std::string arg = argv[index]; // convert to std::string for ease of use
+
+ // simple version info
+ if(arg == "version" || arg == "-v") {
+ std::cout << globalsettings.appname << " Version: " <<
+ globalsettings.majorVersion << "." << globalsettings.minorVersion <<
+ "." << globalsettings.patch << ". Developed by: " << globalsettings.dev << " (" <<
+ globalsettings.email << ") Github: " << globalsettings.github << std::endl;
+ } else if(arg == "-h") {
+ std::cout << globalsettings.appname << " help page. Use man " << globalsettings.appname <<
+ " for more information!\n\n" <<
+ "Arguments:\n" <<
+ "-h\t(optional) Outputs this menu\n" <<
+ "-v\t(optional) Outputs version information\n" <<
+ "-f ./example.png\t(required) Set the file that is to be converted\n" <<
+ "-o ./output\t(optional) Set the output file and directory\n" <<
+ "-b detailed/compressed\t(optional) Set the base to output the data in (defaults to detailed)\n" <<
+ "verbose\t(optional) Verbose mode.";
+ } else if(arg == "-f") {
+ if(extraArgumentsNeeded(1, argc, index)) {
+ index++;
+ globalsettings.inputfile = argv[index];
+ } else {
+ std::cerr << red << "[FATAL]" << def << " Insufficent arguments: Use -h for syntax!" << std::endl;
+ exit(-1);
+ }
+ } else if(arg == "-o") {
+ if(extraArgumentsNeeded(1, argc, index)) {
+ index++;
+ globalsettings.outfile = argv[index];
+ } else {
+ std::cerr << red << "[FATAL]" << def << " Insufficent arguments: Use -h for syntax!" << std::endl;
+ exit(-1);
+ }
+ } else if(arg == "-b") {
+ if(extraArgumentsNeeded(1, argc, index)) {
+ index++;
+ globalsettings.outputBase = argv[index];
+ } else {
+ std::cerr << red << "[FATAL]" << def << " Insufficent arguments: Use -h for syntax!" << std::endl;
+ exit(-1);
+ }
+ } else if(arg == "verbose") {
+ globalsettings.verbose = true;
+ }
+ }
+}
+
+unsigned int hexStringToInt(const std::string &hexstr) {
+ return std::stoul(hexstr, nullptr, 16);
+}
+
+bool extraArgumentsNeeded(int needed, int argc, int index) {
+ if(index + needed >= argc) {
+ return false;
+ }
+
+ return true;
+}
+
+std::vector<std::string> splitStr(const std::string &text, char sep) {
+ std::vector<std::string> tokens;
+ std::size_t start = 0, end = 0;
+ while ((end = text.find(sep, start)) != std::string::npos) {
+ tokens.push_back(text.substr(start, end - start));
+ start = end + 1;
+ }
+ tokens.push_back(text.substr(start));
+ return tokens;
+}
+
+int setBit(int num, unsigned int bit) {
+ return num | 1u << bit;
+}
+
+int unsetBit(int num, unsigned int bit) {
+ return num & ~(1u << bit);
+}
+
+int readBit(int num, unsigned int bit) {
+ int mask = 1 << bit;
+ int masked_n = num & mask;
+ int thebit = masked_n >> bit;
+ return thebit;
+}
+
+int toggleBit(int num, unsigned int bit) {
+ return num ^ (1u << bit);
+}