Skip to content

Instantly share code, notes, and snippets.

@Xkeeper0
Created September 24, 2017 17:48
Show Gist options
  • Save Xkeeper0/573425de8cf722541978c06855608def to your computer and use it in GitHub Desktop.
Save Xkeeper0/573425de8cf722541978c06855608def to your computer and use it in GitHub Desktop.
a hastily-written puggsy prototype video data decoder
<?php
/*
JonTT's Puggsy prototype video decoder
Hastily written at one in the morning
Get the video
$ youtube-dl https://www.youtube.com/watch?v=2ZdXmhwcbhQ
Give it a more reasonable name
$ mv WE\ REACHED\ 3\,000\ SUBS\,\ SO\ HERE\'S\ A\ CHALLENGE\!\ Do\ you\ accept-2ZdXmhwcbhQ.mp4 burp.mp4
Extract frames into individual images
$ ffmpeg -i burp.mp4 -f image2 images%06d.png
Delete images before images000807 and after images000902 (not part of data)
Then run this thing. It will dump out.bin into your directory
*/
// Tracking pages of data
$page = 0; // Current page
$pagel = false; // Last image was separator
$pages = []; // Page data
for ($ii = 807; $ii <= 902; $ii++) {
$file = sprintf("/tmp/burp/images%06d.png", $ii);
print $file ."\n";
// Gets a string of 0 and 1 (or null if separator)
$d = decodeimage($file);
if ($d === null && $pagel !== true) {
// If first not-a-page image, move to the next page
$page++;
$pagel = true;
print "Separator image\n";
continue;
} elseif ($d === null) {
// If still not a page, keep going
print "Separator image\n";
continue;
}
$pagel = false;
// Page, probably. Go ahead and copy it
if (!isset($pages[$page])) {
// New page, copy it in
$pages[$page] = $d;
print "Decoded page $page\n";
} else {
if ($pages[$page] === $d) {
// Different image, same data, looks good
print "Verified page $page\n";
} else {
// This page was already decoded, but for some reason
// this particular attempt didn't match
// No attempt at resolution is made
print "Page $page MISMATCH!\n";
}
}
}
// Concat every page
$total = "";
foreach ($pages as $page) {
$total .= $page;
}
$len = strlen($total);
print "Len: ". $len ." (". ($len / 8) .")\n";
// Convert every 8 characters into one byte
$out = "";
for ($cp = 0; $cp < $len; $cp += 8) {
$bin = substr($total, $cp, 8);
$out .= chr(bindec($bin));
}
// Dump to file
file_put_contents("out.bin", $out);
// The magic happens here
function decodeimage($file) {
$img = imagecreatefrompng($file);
// The top left pixel of all data pages is red
if (pixel(ica($img, 0, 0)) !== null) {
return null;
}
// Padding area
$px = 6;
$py = 4;
// Approximate size of pixels
// I assume these were an even 4 before the padding was added
$sx = 3.9632;
$sy = 3.962;
// Count of pixels
$cx = 320;
$cy = 180;
// Old cruft
$cf = 0;
$c1 = 0;
$c0 = 0;
$str = "";
for ($y = 0; $y < $cy; $y++) {
for ($x = 0; $x < $cx; $x++) {
// Based on internal x/y, get image x/y
$ax = ceil($px + ($x * $sx));
$ay = ceil($py + ($y * $sy));
// Get actual color values for our pixel
$ics = ica($img, $ax, $ay);
// Get bit
$z = pixel($ics);
if ($z === -1) {
// Not red, but not solidly black/white either
print "$file: Unknown pixel @ $ax, $ay: $ics[r] $ics[g] $ics[b]\n";
}
if ($z === true) {
$str .= "1";
}
if ($z === false) {
$str .= "0";
}
}
}
return $str;
}
function ica($i, $x, $y) {
$rgb = imagecolorat($i, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
return ['r' => $r, 'g' => $g, 'b' => $b];
}
function pixel($a) {
// Technically these can be whatever, but these values worked for me
$maxBlack = 0x60;
$minWhite = 0xA0;
// Red pixel
if (
$a['r'] > 0xB0 &&
$a['g'] < 0x80 &&
$a['b'] < 0x80
) {
return null;
}
// White pixel ("on")
if (
$a['r'] > $minWhite &&
$a['g'] > $minWhite &&
$a['b'] > $minWhite
) {
return true;
}
// Black pixel
if (
$a['r'] < $maxBlack &&
$a['g'] < $maxBlack &&
$a['b'] < $maxBlack
) {
return false;
}
return -1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment