Skip to content

Instantly share code, notes, and snippets.

@idolpx
Last active October 31, 2025 20:12
Show Gist options
  • Select an option

  • Save idolpx/ab8874f8396b6fa0d89cc9bab1e4dee2 to your computer and use it in GitHub Desktop.

Select an option

Save idolpx/ab8874f8396b6fa0d89cc9bab1e4dee2 to your computer and use it in GitHub Desktop.
Meatloaf Server Script - To create commodore basic PRG directory listing of files on server
<?php
//
// Meatloaf - A Commodore 64/128 multi-device emulator
// https://github.com/idolpx/meatloaf
// Copyright(C) 2022 James Johnston
//
// Meatloaf Server Script-----------------------------------------
// Create a directory listing as a Commodore Basic Program
// Responds with binary PRG file ready to load and list
// ---------------------------------------------------------------
//
// Meatloaf 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.
//
// Meatloaf 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 Meatloaf. If not, see <http://www.gnu.org/licenses/>.
//
//
// https://gist.github.com/idolpx/ab8874f8396b6fa0d89cc9bab1e4dee2
//
//
// Requirments: Apache (mod_rewrite), PHP 7+
//
// Instructions:
// 1. Save this script as "index.php" in the root of your web server.
// 2. Create a ".htaccess" file in the root of your web server with the following lines
//
// RewriteEngine on
// # If a directory contains an index.prg file, serve it
// RewriteCond %{REQUEST_FILENAME}/index.prg -f
// RewriteRule ^(.*)$ $1/index.prg
// # If the file does not exist, route all requests to index.php in /
// RewriteCond %{REQUEST_FILENAME} !-f
// RewriteRule ^.*$ /index.php [L,QSA]
// # MIME types for binary file extensions
// AddType application/octet-stream .bas .prg .p00
// AddType application/octet-stream .bin .rom .crt
// AddType application/octet-stream .bbt .d8b .dfi .rp9
// AddType application/octet-stream .d64 .d71 .d80 .d81 .d82 .d90 .dnp
// AddType application/octet-stream .g41 .g64 .g71 .nib .nbz
// AddType application/octet-stream .t64 .tcrt .tap .htap
//
// Or if serving from a folder instead of root use the following.
// Place this .htaccess in your sub folder. This example uses "/ml/"
// as the sub folder. Adjust the lines below to match your folder name.
//
// RewriteEngine on
// RewriteBase /ml/
// # If a directory contains an index.prg file, serve it
// RewriteCond %{REQUEST_FILENAME}/index.prg -f
// RewriteRule ^(.*)$ /ml/$1/index.prg
// # If the file does not exist, route all requests to index.php in /
// RewriteCond %{REQUEST_FILENAME} !-f
// RewriteRule ^.*$ /ml/index.php [L,QSA]
// # MIME types for binary file extensions
// AddType application/octet-stream .bas .prg .p00
// AddType application/octet-stream .bin .rom .crt
// AddType application/octet-stream .bbt .d8b .dfi .rp9
// AddType application/octet-stream .d64 .d71 .d80 .d81 .d82 .d90 .dnp
// AddType application/octet-stream .g41 .g64 .g71 .nib .nbz
// AddType application/octet-stream .t64 .tcrt .tap .htap
//
// 3. Add all of your files and organize them into sub folders if you like.
// Rename all files and folders to lowercase.
//
// This header will be displayed at the top of directory listings
// It should be no longer than 16 characters
$header = substr("MEATLOAF ARCHIVE", 0, 16);
/////////////////////////////////////////////////////////////////
$basic_start = 0x0401;
$next_entry = $basic_start;
$root = $_SERVER["DOCUMENT_ROOT"]."/";
$url = $_SERVER['SERVER_NAME'];
$dir = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$dir = urldecode($dir);
function get_type($name)
{
global $root;
if(is_dir($root.$name))
{
$ext = "DIR";
}
else
{
$ext = pathinfo($root.$name, PATHINFO_EXTENSION);
if (strlen($ext) < 3)
$ext = "PRG";
}
return strtoupper($ext);
}
function sendLine($blocks, $line)
{
global $next_entry;
$line .= "\x00";
//$next_entry = $next_entry + 4 + strlen($line);
//echo pack('v', $next_entry);
echo pack('v', 0x0101);
echo pack('v', $blocks);
echo strtoupper("$line");
}
function sendListing($dir, $exp)
{
global $url, $root, $basic_start, $header;
// Send basic load address
echo pack('v', $basic_start);
// Send List HEADER
sendLine(0, "\x12\"$header\" 08 2A");
//echo "[$dir]"; exit();
$directory = preg_filter($exp, '$0', scandir($root.$dir));
// Send Extra INFO
sendLine(0, sprintf("\"%-19s\" NFO", "[URL]"));
sendLine(0, sprintf("\"%-19s\" NFO", $url));
if (strlen($dir) > 1)
{
sendLine(0, sprintf("\"%-19s\" NFO", "[PATH]"));
sendLine(0, sprintf("\"%-19s\" NFO", $dir));
}
sendLine(0, "\"-------------------\" NFO");
// Send file entries
foreach($directory as $key => $file) {
$stat = stat("$root$dir/$file");
$type = get_type("$dir/$file");
$blocks = 0;
$block_spc = 3;
if ( $type != "DIR" ) {
$blocks = ceil($stat['size']/256);
if ($blocks > 9) $block_spc--;
if ($blocks > 99) $block_spc--;
}
$line = sprintf("%s%-18s %s", str_repeat(" ", $block_spc), "\"".$file."\"", $type);
sendLine( $blocks, $line );
}
sendLine( 65535, "BLOCKS FREE" );
// Send 0000 to end basic program
echo "\x00\x00";
}
if (strstr($_SERVER['HTTP_USER_AGENT'], "MEATLOAF"))
{
//Set Content Type
header('Content-Type: application/octet-stream');
//Use Content-Disposition: attachment to specify the filename
header('Content-Disposition: attachment; filename="index.prg"');
header('Meatloaf-Debug: '.$dir);
sendListing($dir, '/(?!^\..*?$|^.*?.html|^.*?.php|^api$|^web.config$)^.*?$/i');
exit();
}
?>
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34
MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<style>
#text {
border-radius: 20px;
padding: 20px;
background-color: white;
width: 75%;
margin: 0 auto;
}
div.social {
background-color: white;
margin: auto;
padding: 8px;
border-radius: 8px 0 0 0;
text-align: center;
position: fixed;
bottom: 0;
right: 0;
opacity: .8;
}
div.social img {
width: 125px !important;
margin: 0 5px;
}
html, body {
height: 100%;
background-color: #4D4D4D;
background-position: cover;
}
.link {
position: absolute;
width: 100%;
height: 100%;
}
.fullscreen-container {
position: absolute;
top: 20%;
left: 10%;
background-repeat: no-repeat;
background-size: contain;
background-image: url(https://meatloaf.cc/media/meatloaf.logo.svg);
width: 80%;
height: 80%;
}
</style>
<title>Meatloaf C64</title>
</head>
<body>
<a href="https://meatloaf.cc">
<div class="link"></div>
<div class="fullscreen-container">
</div>
<div class="social">
<a href="https://discord.gg/FwJUe8kQpS" target="_blank"><img src="https://meatloaf.cc/media/discord.sm.png" class="img-fluid" /></a>
</div>
</a>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment