Skip to content

Instantly share code, notes, and snippets.

@benthetechguy
Last active October 3, 2024 20:25
Show Gist options
  • Save benthetechguy/9cc8e5b83fdae4fa74be9ab77332c985 to your computer and use it in GitHub Desktop.
Save benthetechguy/9cc8e5b83fdae4fa74be9ab77332c985 to your computer and use it in GitHub Desktop.
Generate a CHRP script with custom icon to dual boot with Linux on PowerPC Macs
/*
chrp.cpp - Generate a CHRP script to dual boot with Linux on PowerPC Macs
Copyright (C) 2024 Ben Westover <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <string>
#include <bitset>
#include <png++/png.hpp>
using namespace std;
string generateLogo(string logoPath, string hoverPath) {
string logoText = "<OS-BADGE-ICONS>\n";
png::image< png::rgba_pixel > logoImage(logoPath);
png::image< png::rgba_pixel > hoverImage(hoverPath);
png::image< png::rgba_pixel > image[2] = {logoImage, hoverImage};
png::uint_32 width[2] = {image[0].get_width(), image[1].get_width()};
png::uint_32 height[2] = {image[0].get_height(), image[1].get_height()};
if (width[0] == 16 && height[0] == 16) {
logoText += "1010\n";
} else if (width[0] == 52 && height[0] == 52) {
logoText += "3434\n";
} else {
return "logoSizeError";
}
if ((width[1] != 16 || height[1] != 16) && (width[1] != 52 || height[1] != 52)) {
return "hoverSizeError";
}
for (int i = 0; i < 2; i++) {
cout << i;
for (int y = 0; y < height[i]; y++) {
for (int x = 0; x < width[i]; x++) {
png::rgba_pixel pixel = image[i].get_pixel(x, y);
int red = round(pixel.red * (7.0 / 255.0));
int green = round(pixel.green * (7.0 / 255.0));
int blue = round(pixel.blue * (3.0 / 255.0));
string redBits = bitset< 3 >(red).to_string();
string greenBits = bitset< 3 >(green).to_string();
string blueBits = bitset< 2 >(blue).to_string();
string pixelBits = redBits + greenBits + blueBits;
for(int j = 0; j < 8; j += 4) {
if (pixelBits.substr(j, 4) == "0000") logoText += "0";
else if (pixelBits.substr(j, 4) == "0001") logoText += "1";
else if (pixelBits.substr(j, 4) == "0010") logoText += "2";
else if (pixelBits.substr(j, 4) == "0011") logoText += "3";
else if (pixelBits.substr(j, 4) == "0100") logoText += "4";
else if (pixelBits.substr(j, 4) == "0101") logoText += "5";
else if (pixelBits.substr(j, 4) == "0110") logoText += "6";
else if (pixelBits.substr(j, 4) == "0111") logoText += "7";
else if (pixelBits.substr(j, 4) == "1000") logoText += "8";
else if (pixelBits.substr(j, 4) == "1001") logoText += "9";
else if (pixelBits.substr(j, 4) == "1010") logoText += "A";
else if (pixelBits.substr(j, 4) == "1011") logoText += "B";
else if (pixelBits.substr(j, 4) == "1100") logoText += "C";
else if (pixelBits.substr(j, 4) == "1101") logoText += "D";
else if (pixelBits.substr(j, 4) == "1110") logoText += "E";
else if (pixelBits.substr(j, 4) == "1111") logoText += "F";
else return "pixelProcessingError";
}
}
logoText += "\n";
}
}
for (int y = 0; y < height[0]; y++) {
for (int x = 0; x < width[0]; x++) {
png::rgba_pixel pixel = image[0].get_pixel(x, y);
if (pixel.alpha == 0) logoText += "00";
else logoText += "FF";
}
logoText += "\n";
}
logoText += "</OS-BADGE-ICONS>\n";
return logoText;
}
int main() {
string chrpScript = "<CHRP-BOOT>\n"
"<COMPATIBLE>\n"
"MacRISC MacRISC3 MacRISC4\n"
"</COMPATIBLE>\n"
"<DESCRIPTION>\n"
"PowerPC GNU/Linux First Stage Bootstrap\n"
"</DESCRIPTION>\n"
"<BOOT-SCRIPT>\n"
": .printf fb8-write drop ;\n"
": bootgrub \" Loading GRUB...\" .printf 100 ms load-base release-load-area \" &device;:&partition;,\\grub\" $boot ;\n";
int otherOsCount;
cout << "How many non-Linux operating systems are there?\nNon-Linux OS Count: ";
cin >> otherOsCount;
string osNames[otherOsCount];
for (int i = 0; i < otherOsCount; i++) {
cout << "\nWhat is the name of OS " << i+1 << "?\nOS " << i+1 << " name: ";
getline(cin >> ws, osNames[i]);
int osPartition;
cout << "\nWhat partition is " << osNames[i] << "'s bootloader located on?\nOS " << i+1 << " Bootloader partition: ";
cin >> osPartition;
chrpScript += ": bootother" + to_string(i+1) + " \" Booting " + osNames[i] + "...\" .printf 100 ms load-base release-load-area \" &device;:" + to_string(osPartition) + ",\\\\:tbxi\" $boot ;\n";
}
int usbPorts;
cout << "\nHow many USB ports does the machine have? If it cannot boot from USB, put 0.\nBootable USB port count: ";
cin >> usbPorts;
if (usbPorts < 0 || usbPorts > 4) {
cout << "Error: Only 0-4 USB ports allowed.\n";
return 1;
}
for (int i = 0; i < usbPorts; i++) {
chrpScript += ": bootusb" + to_string(i+1) + " \" Booting USB " + to_string(i+1) + "...\" .printf 100 ms load-base release-load-area \" usb" + to_string(i) + "/disk@1:,\\\\:tbxi\" $boot ;\n";
}
chrpScript += ": bootcd \" Booting CDROM...\" .printf 100 ms load-base release-load-area \" cd:,\\:tbxi\" $boot ;\n"
"\" screen\" output\n"
"variable interactive\n"
"1 interactive !\n\n"
"0 interactive @ = if\n"
" bootgrub\n"
"then\n\n"
"dev screen\n"
"\" \"(0000000000aa00aa0000aaaaaa0000aa00aaaa5500aaaaaa)\" drop 0 7 set-colors\n"
"\" \"(5555555555ff55ff5555ffffff5555ff55ffffff55ffffff)\" drop 8 15 set-colors\n"
"device-end\n"
"f to foreground-color\n"
"0 to background-color\n"
"\" \"(0C)\" .printf\n\n"
"\" First Stage Debian GNU/Linux Bootstrap\"(0d 0a)\" .printf\n"
"\" \"(0d 0a)\" .printf\n"
"\" Press g for GNU/Linux,\"(0d 0a)\" .printf\n";
for (int i = 0; i < otherOsCount; i++) {
chrpScript += "\" " + to_string(i+1) + " for " + osNames[i] + ",\"(0d 0a)\" .printf\n";
}
const string usbPortLetters[4] = {"u", "s", "b", "a"};
for (int i = 0; i < usbPorts; i++) {
chrpScript += "\" " + usbPortLetters[i] + " for USB " + to_string(i+1) + ".\"(0d 0a)\" .printf\n";
}
chrpScript += "\" c for CDROM.\"(0d 0a)\" .printf\n"
"\" \"(0d 0a)\" .printf\n"
"\" Stage 1 Boot: \" .printf\n"
"get-msecs d# 10 3E8 * +\n"
"begin\n"
" key? if\n"
" key case\n"
" ascii g of \" g \"(0d 0a)\" .printf bootgrub endof\n";
for (int i = 0; i < otherOsCount; i++) {
chrpScript += " ascii " + to_string(i+1) + " of \" " + to_string(i+1) + " \"(0d 0a)\" .printf bootother" + to_string(i+1) + " endof\n";
}
for (int i = 0; i < usbPorts; i++) {
chrpScript += " ascii " + usbPortLetters[i] + " of \" " + usbPortLetters[i] + " \"(0d 0a)\" .printf bootusb" + to_string(i+1) + " endof\n";
}
chrpScript += " ascii c of \" c \"(0d 0a)\" .printf bootcd endof\n"
" endcase\n"
" then\n"
" dup get-msecs &lt;\n"
"until\n"
"drop\n"
"\" \"(0d 0a)\" .printf bootgrub\n"
"</BOOT-SCRIPT>\n";
string logoPath;
string hoverPath;
cout << "\nWhere is your logo png located?\nLogo icon path: ";
cin >> logoPath;
cout << "\nThere is an option for a second icon that will be shown when clicking on the boot option. Where is this icon png located? The same logo png as before can also be used.\nHover icon path: ";
cin >> hoverPath;
string logoText = generateLogo(logoPath, hoverPath);
if (logoText == "logoSizeError") {
cout << "Error: Logo must be 16x16 or 52x52.\n";
return 1;
} else if (logoText == "hoverSizeError") {
cout << "Error: Hover logo must be 16x16 or 52x52.\n";
return 1;
} else if (logoText == "pixelProcessingError") {
cout << "Error: Something went wrong while processing the pixels.";
return 1;
}
chrpScript += logoText + "</CHRP-BOOT>\n";
cout << chrpScript;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment