/*
Frame reader and converter for the BAD APPLE 2 movie rendition for
the ZX-UNO.
(C)2018 Miguel Angel Rodriguez Jodar. ZX Projects. ZX-UNO Team.
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 .
*/
#include
#include
#include
#define KEY_FRAME 0
#define DELTA_FRAME 1
#define END_FRAME 255
#define PIXELES_BORDE 892
unsigned char bfa1[6144], bfn1[6144]; // storage for current and next frame for screen page 5
unsigned char bfa2[6144], bfn2[6144]; // storage for current and next frame for screen page 7
unsigned char bsound[512]; // 40ms of sound at 12820 Hz
int borderactual = 0; // current border colour that go with the current frame
int procesa (unsigned short mask, unsigned char *oldframe, unsigned char *newframe, FILE *fs, FILE *fo);
void keyframe (unsigned char *img, FILE *f);
int border (unsigned char *img);
int cuentabits1 (unsigned char byte);
int main(int argc, char *argv[])
{
FILE *ff,*fs,*fo;
int ifile;
char nombre[256];
int frsiz;
char nfile[80];
char *prefix;
char *audio_f;
prefix = argv[1];
audio_f = argv[2];
if (argc<3)
{
fprintf (stderr, "USO: frame_converter prefijo audio\n\n");
fprintf (stderr, "Donde: 'prefijo' es el comienzo del nombre de cada uno de los ficheros.\n");
fprintf (stderr, " 'audio'\n");
fprintf (stderr, "El nombre completo de cada fichero sera prefijo + codigo de 5 digitos\ncomenzando en 0 + .BMP\n\n");
fprintf (stderr, "Ejemplo: con el prefijo 'vid' se procesaran los ficheros:\nvid00000.bmp , vid00001.bmp , vid00002.bmp , etc...\n");
fprintf (stderr, "Si se indica un valor para paso distinto de 1, por ejemplo 2, se\nprocesaran los ficheros: vid00000.bmp , vid00002.bmp , vid00004.bmp , etc...\n");
return 0;
}
memset (bfa1, 0, sizeof bfa1);
memset (bfa2, 0, sizeof bfa2);
ifile=0;
// PCM audio, 8-bit, unsigned, 12820 Hz
fs = fopen (audio_f, "rb");
if (!fs)
{
fprintf (stderr, "ERROR. Sound file not found\n");
return 0;
}
fseek (fs, 44, SEEK_SET); // skip the WAV header part
sprintf (nfile, "%s.mvz", prefix);
fo = fopen (nfile, "wb"); // file to store our movie into.
while(1)
{
// this composes file names prefixXXXX.bmp for reading
sprintf (nombre, "%s%04.4d.bmp", prefix, ifile);
// bitmap files must be 1bit per pixel, vertical mirrored
// bit 0 = black, bit 1 = white. 256x192 pixels.
// You can use Irfanview to batch convert images to this format
ff = fopen (nombre, "rb");
if (!ff) // no more files? end of movie file
break;
fseek (ff, 62, SEEK_SET); // skip the BITMAPINFOHEADER structure
fread (bfn1, 6144, 1, ff); // and read the actual picture
fclose (ff);
// process this frame for memory page 5 (which is also at 0x4000)
frsiz = procesa (0x4000, bfa1, bfn1, fs, fo);
ifile++;
// Do the same as above for page 7 (0xC000 once it is paged in)
sprintf (nombre, "%s%04.4d.bmp", prefix, ifile);
ff = fopen (nombre, "rb");
if (!ff)
break;
fseek (ff, 62, SEEK_SET);
fread (bfn2, 6144, 1, ff);
fclose (ff);
frsiz = procesa (0xC000, bfa2, bfn2, fs, fo);
ifile++;
// some feedback for the user...
if ((frsiz%10)== 0) printf ("Processed frame %04.4d (%d)... \r", ifile, frsiz);
}
printf ("Processed frame %04.4d (%d)... \n", ifile-1, frsiz);
memset (bsound, 0x80, 512); // fill last audio block with 0x80 (silence)
fwrite (bsound, 512, 1, fo);
memset (bsound, END_FRAME, 4); // mark end of video (4 bytes with value 0xFF)
fwrite (bsound, 4, 1, fo);
fclose (fs);
fclose (fo);
}
int procesa (unsigned short mask, unsigned char *oldframe, unsigned char *newframe, FILE *fs, FILE *fo)
{
unsigned char *bcomp = NULL; // memory block to store the compressed frame
int icomp = 0;
int sizebcomp = 0;
int row,col,scan;
unsigned short addr;
int pixelblanco;
// Add 40ms of sound (512 8-bit samples at 12820 Hz)
memset (bsound, 0x80, 512);
fread (bsound, 512, 1, fs);
fwrite (bsound, 512, 1, fo);
sizebcomp += 4;
icomp += 4;
bcomp = realloc (bcomp, sizebcomp); // initial allocation of 4 bytes
// get number of white pixels at the edge of the frame
pixelblanco = border (newframe);
// if there is more than 60% white pixels, change border to white (7)
if (borderactual == 0 && pixelblanco >= (PIXELES_BORDE*6/10))
borderactual = 7;
// else if there is less than 40% white pixels, change border to black (0)
else if (borderactual == 7 && pixelblanco <= (PIXELES_BORDE*4/10))
borderactual = 0;
bcomp[1] = borderactual; // store border value at offset 1 of compressed frame
// offset 0 will indicate the type of frame:
// 0x00 = KEY FRAME (complete frame, 6144 bytes)
// 0x01 = DELTA_FRAME (only changes from previous frame (in the same page) are stored
// 0xFF = END_FRAME. No further info. Just 4 bytes with this value
// Scan the image using 8x8 pixel blocks (characters in the Spectrum)
// and for each character, compare it against the same character in the
// previous frame. If there is a difference, store the complete character
for (row=0;row<24;row++)
{
for (col=0;col<32;col++)
{
for (scan=0;scan<8;scan++)
{
if (newframe[(row*8+scan)*32+col] != oldframe[(row*8+scan)*32+col])
break;
}
if (scan<8) // if there was a difference, store the new character
{
sizebcomp += 10;
bcomp = realloc (bcomp, sizebcomp);
addr = mask + (row/8)*2048 + 32*(row%8) + col; // address in the Spectrum screen of this character
bcomp[icomp++] = addr&0xFF; // store it
bcomp[icomp++] = (addr>>8)&0xFF;
for (scan=0;scan<8;scan++) // store the eight scan lines for this character
{
bcomp[icomp++] = newframe[(row*8+scan)*32+col];
}
}
}
}
memcpy (oldframe, newframe, 6144); // make old frame to be this current frame
bcomp[2] = (sizebcomp-4)&0xFF; // offsets 2 and 3 store the length of the compressed frame
bcomp[3] = ((sizebcomp-4)>>8)&0xFF;
if (sizebcomp > 5632) // if the compressed frame exceeds a certain threshold...
{
bcomp[0] = KEY_FRAME; // then there is no point to store it compressed, instead...
bcomp[2] = 0x00; // store it as a KEY FRAME, and update length to be 6144 bytes
bcomp[3] = 0x18; // 1800h bytes = 6144 bytes
fwrite (bcomp, 4, 1, fo); // store info for this key frame
keyframe (newframe, fo); // and then store the actual frame in Spectrum format
}
else
{
bcomp[0] = DELTA_FRAME; // mark this frame as delta frame
fwrite (bcomp, sizebcomp, 1, fo); // and store it
if (((sizebcomp-4)%10)!=0) printf ("Ouch!!\n"); // DEBUG only: (sizebcomp-4) should be divisible by 10
}
free (bcomp);
return sizebcomp;
}
// store 6144 bytes that make up a linear bitmap file in Spectrum format
void keyframe (unsigned char *img, FILE *f)
{
int scan, addr;
for (scan=0;scan<192;scan++)
{
addr = (scan/64)*2048+(scan%8)*256+((scan/8)%8)*32;
fwrite (img+addr, 32, 1, f);
}
}
// Gets how many 1-bits there is in a byte
int cuentabits1 (unsigned char byte)
{
int b, cont=0;
for (b=0;b<8;b++)
{
if (byte&1)
cont++;
byte = byte>>1;
}
return cont;
}
// Counts how many white pixels there are at the edge of the image
int border (unsigned char *img)
{
int cont=0;
int i;
for (i=0;i<32;i++)
{
cont += cuentabits1 (img[i]);
cont += cuentabits1 (img[191*32+i]);
}
for (i=1;i<=191;i++)
{
if (img[i*32]&0x80)
cont++;
if (img[i*32+31]&1)
cont++;
}
return cont;
}