Remove long-unmaintained SNES-specific dump format
This commit is contained in:
parent
2995be7f12
commit
b9fedf0dde
2 changed files with 0 additions and 1157 deletions
|
@ -1,922 +0,0 @@
|
||||||
#include <cstdint>
|
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#define FLAG_WIDTH 1
|
|
||||||
#define FLAG_HEIGHT 2
|
|
||||||
#define FLAG_FRAMERATE 4
|
|
||||||
#define FLAG_FULLRANGE 8
|
|
||||||
#define FLAG_ITU601 0
|
|
||||||
#define FLAG_ITU709 16
|
|
||||||
#define FLAG_SMPTE240M 32
|
|
||||||
#define FLAG_CS_MASK 48
|
|
||||||
#define FLAG_8BIT 64
|
|
||||||
#define FLAG_FAKENLARGE 128
|
|
||||||
#define FLAG_DEDUP 256
|
|
||||||
#define FLAG_OFFSET2 512
|
|
||||||
#define FLAG_10FRAMES 1024
|
|
||||||
#define FLAG_AR_CORRECT 2048
|
|
||||||
|
|
||||||
#define CMD_HIRES 1
|
|
||||||
#define CMD_INTERLACED 2
|
|
||||||
#define CMD_OVERSCAN 4
|
|
||||||
#define CMD_PAL 8
|
|
||||||
|
|
||||||
#define MAX_DEDUP 9
|
|
||||||
|
|
||||||
//This buffer needs to be big enough to store 640x480 16-bit YCbCr 4:4:4 (6 bytes per pixel) image.
|
|
||||||
#define MAXYUVSIZE (640 * 480 * 6)
|
|
||||||
unsigned char yuv_buffer[MAXYUVSIZE];
|
|
||||||
unsigned char old_yuv_buffer[MAXYUVSIZE];
|
|
||||||
|
|
||||||
//30 bit values.
|
|
||||||
uint32_t ymatrix[0x80000];
|
|
||||||
uint32_t cbmatrix[0x80000];
|
|
||||||
uint32_t crmatrix[0x80000];
|
|
||||||
|
|
||||||
|
|
||||||
#define TOYUV(src, idx) do {\
|
|
||||||
uint32_t c = (static_cast<uint32_t>(src[(idx) + 0]) << 24) |\
|
|
||||||
(static_cast<uint32_t>(src[(idx) + 1]) << 16) |\
|
|
||||||
(static_cast<uint32_t>(src[(idx) + 2]) << 8) |\
|
|
||||||
static_cast<uint32_t>(src[(idx) + 3]);\
|
|
||||||
Y += ymatrix[c & 0x7FFFF];\
|
|
||||||
Cb += cbmatrix[c & 0x7FFFF];\
|
|
||||||
Cr += crmatrix[c & 0x7FFFF];\
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define RGB2YUV_SHIFT 14
|
|
||||||
|
|
||||||
class sox_output
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
sox_output(std::ostream& soxs, uint32_t apurate, bool silence2);
|
|
||||||
void close();
|
|
||||||
void add_sample(unsigned char* buf);
|
|
||||||
uint64_t get_samples();
|
|
||||||
private:
|
|
||||||
std::ostream& strm;
|
|
||||||
uint64_t samples;
|
|
||||||
};
|
|
||||||
|
|
||||||
class sdmp_input_stream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
sdmp_input_stream(std::istream& sdmp);
|
|
||||||
uint32_t get_cpurate();
|
|
||||||
uint32_t get_apurate();
|
|
||||||
int read_command();
|
|
||||||
void read_linepair(unsigned char* buffer);
|
|
||||||
void copy_audio_sample(sox_output& audio_out);
|
|
||||||
private:
|
|
||||||
std::istream& strm;
|
|
||||||
uint32_t cpurate;
|
|
||||||
uint32_t apurate;
|
|
||||||
};
|
|
||||||
|
|
||||||
class time_tracker
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
time_tracker(uint32_t _cpurate);
|
|
||||||
void add_2s();
|
|
||||||
void advance(bool pal, bool interlaced);
|
|
||||||
uint64_t get_ts();
|
|
||||||
private:
|
|
||||||
uint32_t cpurate;
|
|
||||||
uint64_t w;
|
|
||||||
uint64_t n;
|
|
||||||
};
|
|
||||||
|
|
||||||
class dup_tracker
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
dup_tracker(std::ostream& _tcfile, uint32_t _flags);
|
|
||||||
uint32_t process(unsigned char* buffer, size_t bufsize, int pkt_type, time_tracker& ts);
|
|
||||||
private:
|
|
||||||
std::ostream& tcfile;
|
|
||||||
uint32_t flags;
|
|
||||||
uint32_t counter;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct store16
|
|
||||||
{
|
|
||||||
static const size_t esize = 2;
|
|
||||||
static void store(unsigned char* buffer, size_t idx, size_t psep, uint32_t v1, uint32_t v2,
|
|
||||||
uint32_t v3)
|
|
||||||
{
|
|
||||||
*reinterpret_cast<uint16_t*>(buffer + idx) = (v1 >> RGB2YUV_SHIFT);
|
|
||||||
*reinterpret_cast<uint16_t*>(buffer + idx + psep) = (v2 >> RGB2YUV_SHIFT);
|
|
||||||
*reinterpret_cast<uint16_t*>(buffer + idx + 2 * psep) = (v3 >> RGB2YUV_SHIFT);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct store8
|
|
||||||
{
|
|
||||||
static const size_t esize = 1;
|
|
||||||
static void store(unsigned char* buffer, size_t idx, size_t psep, uint32_t v1, uint32_t v2,
|
|
||||||
uint32_t v3)
|
|
||||||
{
|
|
||||||
buffer[idx] = (v1 >> (RGB2YUV_SHIFT + 8));
|
|
||||||
buffer[idx + psep] = (v2 >> (RGB2YUV_SHIFT + 8));
|
|
||||||
buffer[idx + 2 * psep] = (v3 >> (RGB2YUV_SHIFT + 8));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class store, size_t ioff1, size_t ioff2, size_t ioff3, size_t ooff1, size_t ooff2, size_t ooff3>
|
|
||||||
struct loadstore
|
|
||||||
{
|
|
||||||
static const size_t esize = store::esize;
|
|
||||||
static void convert(unsigned char* obuffer, size_t oidx, const unsigned char* ibuffer, size_t iidx,
|
|
||||||
size_t psep)
|
|
||||||
{
|
|
||||||
//Compiler should be able to eliminate every if out of this.
|
|
||||||
uint32_t Y = 0;
|
|
||||||
uint32_t Cb = 0;
|
|
||||||
uint32_t Cr = 0;
|
|
||||||
TOYUV(ibuffer, iidx);
|
|
||||||
if(ioff1 > 0)
|
|
||||||
TOYUV(ibuffer, iidx + ioff1 * 4);
|
|
||||||
if(ioff2 > 0)
|
|
||||||
TOYUV(ibuffer, iidx + ioff2 * 4);
|
|
||||||
if(ioff3 > 0)
|
|
||||||
TOYUV(ibuffer, iidx + ioff3 * 4);
|
|
||||||
if(ioff1 > 0 && ioff2 > 0 && ioff3 > 0) {
|
|
||||||
Y >>= 2;
|
|
||||||
Cb >>= 2;
|
|
||||||
Cr >>= 2;
|
|
||||||
} else if(ioff1 > 0) {
|
|
||||||
Y >>= 1;
|
|
||||||
Cb >>= 1;
|
|
||||||
Cr >>= 1;
|
|
||||||
}
|
|
||||||
store::store(obuffer, oidx, psep, Y, Cb, Cr);
|
|
||||||
if(ooff1 > 0)
|
|
||||||
store::store(obuffer, oidx + ooff1 * store::esize, psep, Y, Cb, Cr);
|
|
||||||
if(ooff2 > 0)
|
|
||||||
store::store(obuffer, oidx + ooff2 * store::esize, psep, Y, Cb, Cr);
|
|
||||||
if(ooff3 > 0)
|
|
||||||
store::store(obuffer, oidx + ooff3 * store::esize, psep, Y, Cb, Cr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class proc, size_t lim, size_t igap, size_t ogap>
|
|
||||||
struct loop
|
|
||||||
{
|
|
||||||
static void f(unsigned char* buffer, const unsigned char* src, size_t psep)
|
|
||||||
{
|
|
||||||
for(size_t i = 0; i < lim; i++)
|
|
||||||
proc::convert(buffer, proc::esize * ogap * i, src, 4 * igap * i, psep);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//Render a line pair of YUV with 256x224/240
|
|
||||||
template<class store>
|
|
||||||
void render_yuv_256_240(unsigned char* buffer, const unsigned char* src, size_t psep, bool hires, bool interlaced)
|
|
||||||
{
|
|
||||||
if(hires)
|
|
||||||
if(interlaced)
|
|
||||||
loop<loadstore<store, 1, 512, 513, 0, 0, 0>, 256, 2, 1>::f(buffer, src, psep);
|
|
||||||
else
|
|
||||||
loop<loadstore<store, 1, 0, 0, 0, 0, 0>, 256, 2, 1>::f(buffer, src, psep);
|
|
||||||
else
|
|
||||||
if(interlaced)
|
|
||||||
loop<loadstore<store, 512, 0, 0, 0, 0, 0>, 256, 1, 1>::f(buffer, src, psep);
|
|
||||||
else
|
|
||||||
loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 256, 1, 1>::f(buffer, src, psep);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Render a line pair of YUV with 512x224/240
|
|
||||||
template<class store>
|
|
||||||
void render_yuv_512_240(unsigned char* buffer, const unsigned char* src, size_t psep, bool hires, bool interlaced)
|
|
||||||
{
|
|
||||||
if(hires)
|
|
||||||
if(interlaced)
|
|
||||||
loop<loadstore<store, 512, 0, 0, 0, 0, 0>, 512, 1, 1>::f(buffer, src, psep);
|
|
||||||
else
|
|
||||||
loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 512, 1, 1>::f(buffer, src, psep);
|
|
||||||
else
|
|
||||||
if(interlaced)
|
|
||||||
loop<loadstore<store, 512, 0, 0, 1, 0, 0>, 256, 1, 2>::f(buffer, src, psep);
|
|
||||||
else
|
|
||||||
loop<loadstore<store, 0, 0, 0, 1, 0, 0>, 256, 1, 2>::f(buffer, src, psep);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Render a line pair of YUV with 256x448/480
|
|
||||||
template<class store>
|
|
||||||
void render_yuv_256_480(unsigned char* buffer, const unsigned char* src, size_t psep, bool hires, bool interlaced)
|
|
||||||
{
|
|
||||||
if(hires)
|
|
||||||
if(interlaced) {
|
|
||||||
loop<loadstore<store, 1, 0, 0, 0, 0, 0>, 256, 2, 1>::f(buffer, src, psep);
|
|
||||||
loop<loadstore<store, 1, 0, 0, 0, 0, 0>, 256, 2, 1>::f(buffer + 256 * store::esize,
|
|
||||||
src + 2048, psep);
|
|
||||||
} else
|
|
||||||
loop<loadstore<store, 1, 0, 0, 256, 0, 0>, 256, 2, 1>::f(buffer, src, psep);
|
|
||||||
else
|
|
||||||
if(interlaced) {
|
|
||||||
loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 256, 1, 1>::f(buffer, src, psep);
|
|
||||||
loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 256, 1, 1>::f(buffer + 256 * store::esize,
|
|
||||||
src + 2048, psep);
|
|
||||||
} else
|
|
||||||
loop<loadstore<store, 0, 0, 0, 256, 0, 0>, 256, 1, 1>::f(buffer, src, psep);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Render a line pair of YUV with 512x448/480
|
|
||||||
template<class store>
|
|
||||||
void render_yuv_512_480(unsigned char* buffer, const unsigned char* src, size_t psep, bool hires, bool interlaced)
|
|
||||||
{
|
|
||||||
if(hires)
|
|
||||||
if(interlaced) {
|
|
||||||
loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 512, 1, 1>::f(buffer, src, psep);
|
|
||||||
loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 512, 1, 1>::f(buffer + 512 * store::esize,
|
|
||||||
src + 2048, psep);
|
|
||||||
} else
|
|
||||||
loop<loadstore<store, 0, 0, 0, 512, 0, 0>, 512, 1, 1>::f(buffer, src, psep);
|
|
||||||
else
|
|
||||||
if(interlaced) {
|
|
||||||
loop<loadstore<store, 0, 0, 0, 1, 0, 0>, 256, 1, 2>::f(buffer, src, psep);
|
|
||||||
loop<loadstore<store, 0, 0, 0, 1, 0, 0>, 256, 1, 2>::f(buffer + 512 * store::esize,
|
|
||||||
src + 2048, psep);
|
|
||||||
} else
|
|
||||||
loop<loadstore<store, 0, 0, 0, 1, 512, 513>, 256, 1, 2>::f(buffer, src, psep);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Render a line pair of YUV with 512x448/480 fakeexpand
|
|
||||||
template<class store>
|
|
||||||
void render_yuv_fe(unsigned char* buffer, const unsigned char* src, size_t psep, bool hires, bool interlaced)
|
|
||||||
{
|
|
||||||
if(hires)
|
|
||||||
if(interlaced) {
|
|
||||||
loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 512, 1, 1>::f(buffer, src, psep);
|
|
||||||
loop<loadstore<store, 0, 0, 0, 0, 0, 0>, 512, 1, 1>::f(buffer + 512 * store::esize,
|
|
||||||
src + 2048, psep);
|
|
||||||
} else
|
|
||||||
loop<loadstore<store, 1, 0, 0, 1, 512, 513>, 256, 2, 2>::f(buffer, src, psep);
|
|
||||||
else
|
|
||||||
if(interlaced) {
|
|
||||||
loop<loadstore<store, 0, 0, 0, 1, 0, 0>, 256, 1, 2>::f(buffer, src, psep);
|
|
||||||
loop<loadstore<store, 0, 0, 0, 1, 0, 0>, 256, 1, 2>::f(buffer + 512 * store::esize,
|
|
||||||
src + 2048, psep);
|
|
||||||
} else
|
|
||||||
loop<loadstore<store, 0, 0, 0, 1, 256, 257>, 256, 1, 2>::f(buffer, src, psep);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef void (*render_yuv_t)(unsigned char* buffer, const unsigned char* src, size_t psep, bool hires,
|
|
||||||
bool interlaced);
|
|
||||||
|
|
||||||
render_yuv_t get_renderer_for(int32_t flags)
|
|
||||||
{
|
|
||||||
int32_t mode = flags & (FLAG_WIDTH | FLAG_HEIGHT | FLAG_8BIT | FLAG_FAKENLARGE);
|
|
||||||
if(mode == 0)
|
|
||||||
return render_yuv_256_240<store16>;
|
|
||||||
if(mode == FLAG_WIDTH)
|
|
||||||
return render_yuv_512_240<store16>;
|
|
||||||
if(mode == FLAG_HEIGHT)
|
|
||||||
return render_yuv_256_480<store16>;
|
|
||||||
if(mode == (FLAG_WIDTH | FLAG_HEIGHT))
|
|
||||||
return render_yuv_512_480<store16>;
|
|
||||||
if(mode == (FLAG_WIDTH | FLAG_HEIGHT | FLAG_FAKENLARGE))
|
|
||||||
return render_yuv_fe<store16>;
|
|
||||||
if(mode == FLAG_8BIT)
|
|
||||||
return render_yuv_256_240<store8>;
|
|
||||||
if(mode == (FLAG_WIDTH | FLAG_8BIT))
|
|
||||||
return render_yuv_512_240<store8>;
|
|
||||||
if(mode == (FLAG_HEIGHT | FLAG_8BIT))
|
|
||||||
return render_yuv_256_480<store8>;
|
|
||||||
if(mode == (FLAG_WIDTH | FLAG_HEIGHT | FLAG_8BIT))
|
|
||||||
return render_yuv_512_480<store8>;
|
|
||||||
if(mode == (FLAG_WIDTH | FLAG_HEIGHT | FLAG_FAKENLARGE | FLAG_8BIT))
|
|
||||||
return render_yuv_fe<store8>;
|
|
||||||
throw std::runtime_error("get_renderer_for: Unknown flags combination");
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_matrix(double Kb, double Kr, bool fullrange)
|
|
||||||
{
|
|
||||||
double RY = Kr;
|
|
||||||
double GY = 1 - Kr - Kb;
|
|
||||||
double BY = Kb;
|
|
||||||
double RPb = -0.5 * Kr / (1 - Kb);
|
|
||||||
double GPb = -0.5 * (1 - Kr - Kb) / (1 - Kb);
|
|
||||||
double BPb = 0.5;
|
|
||||||
double RPr = 0.5;
|
|
||||||
double GPr = -0.5 * (1 - Kr - Kb) / (1 - Kr);
|
|
||||||
double BPr = -0.5 * Kb / (1 - Kr);
|
|
||||||
for(uint32_t i = 0; i < 0x80000; i++) {
|
|
||||||
uint32_t l = 1 + ((i >> 15) & 0xF);
|
|
||||||
//Range of (r,g,b) is 0...496.
|
|
||||||
uint32_t r = (l * ((i >> 0) & 0x1F));
|
|
||||||
uint32_t g = (l * ((i >> 5) & 0x1F));
|
|
||||||
uint32_t b = (l * ((i >> 10) & 0x1F));
|
|
||||||
double Y = (RY * r + GY * g + BY * b) / 496 * (fullrange ? 255 : 219) + (fullrange ? 0 : 16);
|
|
||||||
double Cb = (RPb * r + GPb * g + BPb * b) / 496 * (fullrange ? 255 : 224) + 128;
|
|
||||||
double Cr = (RPr * r + GPr * g + BPr * b) / 496 * (fullrange ? 255 : 224) + 128;
|
|
||||||
ymatrix[i] = static_cast<uint32_t>(Y * 4194304 + 0.5);
|
|
||||||
cbmatrix[i] = static_cast<uint32_t>(Cb * 4194304 + 0.5);
|
|
||||||
crmatrix[i] = static_cast<uint32_t>(Cr * 4194304 + 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Load RGB to YUV conversion matrix.
|
|
||||||
void load_rgb2yuv_matrix(uint32_t flags)
|
|
||||||
{
|
|
||||||
switch(flags & (FLAG_CS_MASK))
|
|
||||||
{
|
|
||||||
case FLAG_ITU601:
|
|
||||||
init_matrix(0.114, 0.229, flags & FLAG_FULLRANGE);
|
|
||||||
break;
|
|
||||||
case FLAG_ITU709:
|
|
||||||
init_matrix(0.0722, 0.2126, flags & FLAG_FULLRANGE);
|
|
||||||
break;
|
|
||||||
case FLAG_SMPTE240M:
|
|
||||||
init_matrix(0.087, 0.212, flags & FLAG_FULLRANGE);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
init_matrix(0.114, 0.229, flags & FLAG_FULLRANGE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t double_to_ieeefp(double v)
|
|
||||||
{
|
|
||||||
unsigned mag = 1023;
|
|
||||||
while(v >= 2) {
|
|
||||||
mag++;
|
|
||||||
v /= 2;
|
|
||||||
}
|
|
||||||
while(v < 1) {
|
|
||||||
mag--;
|
|
||||||
v *= 2;
|
|
||||||
}
|
|
||||||
uint64_t v2 = mag;
|
|
||||||
v -= 1;
|
|
||||||
for(unsigned i = 0; i < 52; i++) {
|
|
||||||
v *= 2;
|
|
||||||
v2 = 2 * v2 + ((v >= 1) ? 1 : 0);
|
|
||||||
if(v >= 1)
|
|
||||||
v -= 1;
|
|
||||||
}
|
|
||||||
return v2;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
sox_output::sox_output(std::ostream& soxs, uint32_t apurate, bool silence2)
|
|
||||||
: strm(soxs)
|
|
||||||
{
|
|
||||||
uint64_t sndrateR = double_to_ieeefp(static_cast<double>(apurate) / 768.0);
|
|
||||||
unsigned char sox_header[32] = {0};
|
|
||||||
sox_header[0] = 0x2E; //Magic
|
|
||||||
sox_header[1] = 0x53; //Magic
|
|
||||||
sox_header[2] = 0x6F; //Magic
|
|
||||||
sox_header[3] = 0x58; //Magic
|
|
||||||
sox_header[4] = 0x1C; //Magic
|
|
||||||
sox_header[16] = sndrateR;
|
|
||||||
sox_header[17] = sndrateR >> 8;
|
|
||||||
sox_header[18] = sndrateR >> 16;
|
|
||||||
sox_header[19] = sndrateR >> 24;
|
|
||||||
sox_header[20] = sndrateR >> 32;
|
|
||||||
sox_header[21] = sndrateR >> 40;
|
|
||||||
sox_header[22] = sndrateR >> 48;
|
|
||||||
sox_header[23] = sndrateR >> 56;
|
|
||||||
sox_header[24] = 2;
|
|
||||||
strm.write(reinterpret_cast<char*>(sox_header), 32);
|
|
||||||
if(!strm)
|
|
||||||
throw std::runtime_error("Can't write audio header");
|
|
||||||
samples = 0;
|
|
||||||
if(silence2) {
|
|
||||||
uint64_t nullsamples = apurate / 384;
|
|
||||||
samples = nullsamples;
|
|
||||||
const size_t bufsz = 512;
|
|
||||||
char nbuffer[8 * bufsz] = {0};
|
|
||||||
while(nullsamples > bufsz) {
|
|
||||||
strm.write(nbuffer, 8 * bufsz);
|
|
||||||
nullsamples -= bufsz;
|
|
||||||
}
|
|
||||||
strm.write(nbuffer, 8 * nullsamples);
|
|
||||||
if(!strm)
|
|
||||||
throw std::runtime_error("Can't write 2 second silence");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sox_output::close()
|
|
||||||
{
|
|
||||||
//Sox internally multiplies sample count by channel count.
|
|
||||||
unsigned char sox_header[8];
|
|
||||||
sox_header[0] = samples << 1;
|
|
||||||
sox_header[1] = samples >> 7;
|
|
||||||
sox_header[2] = samples >> 15;
|
|
||||||
sox_header[3] = samples >> 23;
|
|
||||||
sox_header[4] = samples >> 31;
|
|
||||||
sox_header[5] = samples >> 39;
|
|
||||||
sox_header[6] = samples >> 47;
|
|
||||||
sox_header[7] = samples >> 55;
|
|
||||||
strm.seekp(8, std::ios::beg);
|
|
||||||
if(!strm)
|
|
||||||
throw std::runtime_error("Can't seek to fix .sox header");
|
|
||||||
strm.write(reinterpret_cast<char*>(sox_header), 8);
|
|
||||||
if(!strm)
|
|
||||||
throw std::runtime_error("Can't fix audio header");
|
|
||||||
}
|
|
||||||
|
|
||||||
void sox_output::add_sample(unsigned char* buf)
|
|
||||||
{
|
|
||||||
strm.write(reinterpret_cast<char*>(buf), 8);
|
|
||||||
if(!strm)
|
|
||||||
throw std::runtime_error("Can't write audio sample");
|
|
||||||
samples++;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t sox_output::get_samples()
|
|
||||||
{
|
|
||||||
return samples;
|
|
||||||
}
|
|
||||||
|
|
||||||
sdmp_input_stream::sdmp_input_stream(std::istream& sdmp)
|
|
||||||
: strm(sdmp)
|
|
||||||
{
|
|
||||||
unsigned char header[12];
|
|
||||||
strm.read(reinterpret_cast<char*>(header), 12);
|
|
||||||
if(!strm)
|
|
||||||
throw std::runtime_error("Can't read sdump header");
|
|
||||||
if(header[0] != 'S' || header[1] != 'D' || header[2] != 'M' || header[3] != 'P')
|
|
||||||
throw std::runtime_error("Bad sdump magic");
|
|
||||||
cpurate = (static_cast<uint32_t>(header[4]) << 24) |
|
|
||||||
(static_cast<uint32_t>(header[5]) << 16) |
|
|
||||||
(static_cast<uint32_t>(header[6]) << 8) |
|
|
||||||
static_cast<uint32_t>(header[7]);
|
|
||||||
apurate = (static_cast<uint32_t>(header[8]) << 24) |
|
|
||||||
(static_cast<uint32_t>(header[9]) << 16) |
|
|
||||||
(static_cast<uint32_t>(header[10]) << 8) |
|
|
||||||
static_cast<uint32_t>(header[11]);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t sdmp_input_stream::get_cpurate()
|
|
||||||
{
|
|
||||||
return cpurate;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t sdmp_input_stream::get_apurate()
|
|
||||||
{
|
|
||||||
return apurate;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sdmp_input_stream::read_command()
|
|
||||||
{
|
|
||||||
unsigned char cmd;
|
|
||||||
strm >> cmd;
|
|
||||||
if(!strm)
|
|
||||||
return -1;
|
|
||||||
return cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sdmp_input_stream::read_linepair(unsigned char* buffer)
|
|
||||||
{
|
|
||||||
strm.read(reinterpret_cast<char*>(buffer), 4096);
|
|
||||||
if(!strm)
|
|
||||||
throw std::runtime_error("Can't read picture payload");
|
|
||||||
}
|
|
||||||
|
|
||||||
void sdmp_input_stream::copy_audio_sample(sox_output& audio_out)
|
|
||||||
{
|
|
||||||
unsigned char ibuf[4];
|
|
||||||
unsigned char obuf[8];
|
|
||||||
strm.read(reinterpret_cast<char*>(ibuf), 4);
|
|
||||||
if(!strm)
|
|
||||||
throw std::runtime_error("Can't read sound packet payload");
|
|
||||||
obuf[0] = 0;
|
|
||||||
obuf[1] = 0;
|
|
||||||
obuf[2] = ibuf[1];
|
|
||||||
obuf[3] = ibuf[0];
|
|
||||||
obuf[4] = 0;
|
|
||||||
obuf[5] = 0;
|
|
||||||
obuf[6] = ibuf[3];
|
|
||||||
obuf[7] = ibuf[2];
|
|
||||||
audio_out.add_sample(obuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
time_tracker::time_tracker(uint32_t _cpurate)
|
|
||||||
{
|
|
||||||
cpurate = _cpurate;
|
|
||||||
w = n = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void time_tracker::add_2s()
|
|
||||||
{
|
|
||||||
w += 2000;
|
|
||||||
}
|
|
||||||
|
|
||||||
void time_tracker::advance(bool pal, bool interlaced)
|
|
||||||
{
|
|
||||||
uint64_t tcc = pal ? 425568000 : (interlaced ? 357368000 : 357366000);
|
|
||||||
w += tcc / cpurate;
|
|
||||||
n += tcc % cpurate;
|
|
||||||
w += n / cpurate;
|
|
||||||
n %= cpurate;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t time_tracker::get_ts()
|
|
||||||
{
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
|
|
||||||
dup_tracker::dup_tracker(std::ostream& _tcfile, uint32_t _flags)
|
|
||||||
: tcfile(_tcfile)
|
|
||||||
{
|
|
||||||
flags = _flags;
|
|
||||||
counter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t dup_tracker::process(unsigned char* buffer, size_t bufsize, int pkt_type, time_tracker& ts)
|
|
||||||
{
|
|
||||||
if(flags & FLAG_DEDUP) {
|
|
||||||
if(memcmp(buffer, old_yuv_buffer, bufsize)) {
|
|
||||||
memcpy(old_yuv_buffer, buffer, bufsize);
|
|
||||||
counter = 0;
|
|
||||||
} else
|
|
||||||
counter = (counter + 1) % MAX_DEDUP;
|
|
||||||
if(counter)
|
|
||||||
return 0;
|
|
||||||
else {
|
|
||||||
tcfile << ts.get_ts() << std::endl;
|
|
||||||
if(!tcfile)
|
|
||||||
throw std::runtime_error("Can't write frame timestamp");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(pkt_type & CMD_PAL)
|
|
||||||
return 1; //No wrong framerate correction in PAL mode.
|
|
||||||
bool framerate_flag = (flags & FLAG_FRAMERATE);
|
|
||||||
bool interlaced = (pkt_type & CMD_INTERLACED);
|
|
||||||
if(!framerate_flag && interlaced) {
|
|
||||||
//This uses 357368 TU instead of 357366 TU.
|
|
||||||
//-> Every 178683rd frame is duplicated.
|
|
||||||
counter = (counter + 1) % 178683;
|
|
||||||
if(counter)
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
if(framerate_flag && !interlaced) {
|
|
||||||
//This uses 357366 TU instead of 357368 TU.
|
|
||||||
//-> Every 178684th frame is dropped.
|
|
||||||
counter = (counter + 1) % 178684;
|
|
||||||
if(!counter)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t calculate_line_separation(int32_t flags, int pkt_type)
|
|
||||||
{
|
|
||||||
size_t s = 256;
|
|
||||||
if(flags & FLAG_AR_CORRECT)
|
|
||||||
if(pkt_type & CMD_PAL)
|
|
||||||
s = (flags & FLAG_WIDTH) ? 640 : 320;
|
|
||||||
else
|
|
||||||
s = (flags & FLAG_WIDTH) ? 598 : 298;
|
|
||||||
else if(flags & FLAG_WIDTH)
|
|
||||||
s *= 2;
|
|
||||||
if(flags & FLAG_HEIGHT)
|
|
||||||
s *= 2;
|
|
||||||
if(!(flags & FLAG_8BIT))
|
|
||||||
s *= 2;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t calculate_plane_separation(int32_t flags, int pkt_type)
|
|
||||||
{
|
|
||||||
size_t s = calculate_line_separation(flags, pkt_type);
|
|
||||||
if(pkt_type & CMD_PAL)
|
|
||||||
s *= 240;
|
|
||||||
else
|
|
||||||
s *= 224;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_renderable_line(int pkt_type, unsigned line_pair, unsigned rendered)
|
|
||||||
{
|
|
||||||
switch(pkt_type & (CMD_OVERSCAN | CMD_PAL)) {
|
|
||||||
case 0:
|
|
||||||
return (line_pair >= 9 && rendered < 224);
|
|
||||||
case CMD_OVERSCAN:
|
|
||||||
return (line_pair >= 16 && rendered < 224);
|
|
||||||
case CMD_PAL:
|
|
||||||
return (line_pair >= 1 && rendered < 239);
|
|
||||||
case CMD_PAL | CMD_INTERLACED:
|
|
||||||
return (line_pair >= 9 && rendered < 239);
|
|
||||||
};
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void lanczos_256_298_16(unsigned short* dst, unsigned short* src);
|
|
||||||
void lanczos_256_320_16(unsigned short* dst, unsigned short* src);
|
|
||||||
void lanczos_512_598_16(unsigned short* dst, unsigned short* src);
|
|
||||||
void lanczos_512_640_16(unsigned short* dst, unsigned short* src);
|
|
||||||
void lanczos_256_298_8(unsigned char* dst, unsigned char* src);
|
|
||||||
void lanczos_256_320_8(unsigned char* dst, unsigned char* src);
|
|
||||||
void lanczos_512_598_8(unsigned char* dst, unsigned char* src);
|
|
||||||
void lanczos_512_640_8(unsigned char* dst, unsigned char* src);
|
|
||||||
|
|
||||||
void do_lanczos(unsigned char* dst, unsigned char* src, bool xhi, bool yhi, bool pal, bool lc, size_t psep)
|
|
||||||
{
|
|
||||||
unsigned char* dstN[3];
|
|
||||||
unsigned short* dstW[3];
|
|
||||||
unsigned char* srcN[3];
|
|
||||||
unsigned short* srcW[3];
|
|
||||||
dstN[0] = dst;
|
|
||||||
dstN[1] = dst + psep;
|
|
||||||
dstN[2] = dst + 2 * psep;
|
|
||||||
dstW[0] = reinterpret_cast<unsigned short*>(dst);
|
|
||||||
dstW[1] = reinterpret_cast<unsigned short*>(dst + psep);
|
|
||||||
dstW[2] = reinterpret_cast<unsigned short*>(dst + 2 * psep);
|
|
||||||
srcN[0] = src;
|
|
||||||
srcN[1] = src + 2048;
|
|
||||||
srcN[2] = src + 4096;
|
|
||||||
srcW[0] = reinterpret_cast<unsigned short*>(src);
|
|
||||||
srcW[1] = reinterpret_cast<unsigned short*>(src + 2048);
|
|
||||||
srcW[2] = reinterpret_cast<unsigned short*>(src + 4096);
|
|
||||||
unsigned doffset = pal ? (xhi ? 640 : 320) : (xhi ? 598 : 298);
|
|
||||||
unsigned soffset = xhi ? 512 : 256;
|
|
||||||
void (*fn8)(unsigned char* dst, unsigned char* src);
|
|
||||||
void (*fn16)(unsigned short* dst, unsigned short* src);
|
|
||||||
if(xhi && pal) {
|
|
||||||
fn8 = lanczos_512_640_8;
|
|
||||||
fn16 = lanczos_512_640_16;
|
|
||||||
} else if(xhi && !pal) {
|
|
||||||
fn8 = lanczos_512_598_8;
|
|
||||||
fn16 = lanczos_512_598_16;
|
|
||||||
} else if(!xhi && pal) {
|
|
||||||
fn8 = lanczos_256_320_8;
|
|
||||||
fn16 = lanczos_256_320_16;
|
|
||||||
} else if(!xhi && !pal) {
|
|
||||||
fn8 = lanczos_256_298_8;
|
|
||||||
fn16 = lanczos_256_298_16;
|
|
||||||
}
|
|
||||||
if(lc) {
|
|
||||||
for(unsigned i = 0; i < 3; i++)
|
|
||||||
fn8(dstN[i], srcN[i]);
|
|
||||||
if(yhi)
|
|
||||||
for(unsigned i = 0; i < 3; i++)
|
|
||||||
fn8(dstN[i] + doffset, srcN[i] + soffset);
|
|
||||||
} else {
|
|
||||||
for(unsigned i = 0; i < 3; i++)
|
|
||||||
fn16(dstW[i], srcW[i]);
|
|
||||||
if(yhi)
|
|
||||||
for(unsigned i = 0; i < 3; i++)
|
|
||||||
fn16(dstW[i] + doffset, srcW[i] + soffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void call_render_yuv(unsigned char* buffer, unsigned char* buf, size_t psep, int pkt_type, int32_t flags)
|
|
||||||
{
|
|
||||||
render_yuv_t render_yuv = get_renderer_for(flags);
|
|
||||||
unsigned char tmp[6144];
|
|
||||||
if(flags & FLAG_AR_CORRECT) {
|
|
||||||
render_yuv(tmp, buf, 2048, pkt_type & CMD_HIRES, pkt_type & CMD_INTERLACED);
|
|
||||||
bool xhi = (flags & FLAG_WIDTH);
|
|
||||||
bool yhi = (flags & FLAG_HEIGHT);
|
|
||||||
bool pal = (pkt_type & CMD_PAL);
|
|
||||||
bool lc = (flags & FLAG_8BIT);
|
|
||||||
do_lanczos(buffer, tmp, xhi, yhi, pal, lc, psep);
|
|
||||||
} else
|
|
||||||
render_yuv(buffer, buf, psep, pkt_type & CMD_HIRES, pkt_type & CMD_INTERLACED);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t render_frame(sdmp_input_stream& in, int pkt_type, int32_t flags, unsigned char* buffer)
|
|
||||||
{
|
|
||||||
unsigned char buf[4096];
|
|
||||||
unsigned physline = 0;
|
|
||||||
size_t psep = calculate_plane_separation(flags, pkt_type);
|
|
||||||
size_t lsep = calculate_line_separation(flags, pkt_type);
|
|
||||||
for(unsigned i = 0; i < 256; i++) {
|
|
||||||
in.read_linepair(buf);
|
|
||||||
if(!is_renderable_line(pkt_type, i, physline))
|
|
||||||
continue;
|
|
||||||
call_render_yuv(buffer + physline * lsep, buf, psep, pkt_type, flags);
|
|
||||||
physline++;
|
|
||||||
}
|
|
||||||
if(pkt_type & CMD_PAL) {
|
|
||||||
//Render a black line to pad the image.
|
|
||||||
memset(buf, 0, 4096);
|
|
||||||
call_render_yuv(buffer + physline * lsep, buf, psep, pkt_type, flags);
|
|
||||||
}
|
|
||||||
return 3 * psep;
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_frame(std::ostream& yout, unsigned char* buffer, size_t bufsize, unsigned times_ctr, uint64_t& frames)
|
|
||||||
{
|
|
||||||
for(unsigned k = 0; k < times_ctr; k++)
|
|
||||||
yout.write(reinterpret_cast<char*>(buffer), bufsize);
|
|
||||||
if(!yout)
|
|
||||||
throw std::runtime_error("Can't write frame");
|
|
||||||
frames += times_ctr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void sdump2sox(std::istream& in, std::ostream& yout, std::ostream& sout, std::ostream& tout, int32_t flags)
|
|
||||||
{
|
|
||||||
sdmp_input_stream sdmp_in(in);
|
|
||||||
sox_output sox_out(sout, sdmp_in.get_apurate(), flags & FLAG_OFFSET2);
|
|
||||||
time_tracker ts(sdmp_in.get_cpurate());
|
|
||||||
dup_tracker dupt(tout, flags);
|
|
||||||
|
|
||||||
load_rgb2yuv_matrix(flags);
|
|
||||||
|
|
||||||
if(flags & FLAG_OFFSET2)
|
|
||||||
ts.add_2s();
|
|
||||||
if(flags & FLAG_DEDUP) {
|
|
||||||
tout << "# timecode format v2" << std::endl;
|
|
||||||
if(flags & FLAG_10FRAMES)
|
|
||||||
tout << "0\n200\n400\n600\n800\n1000\n1200\n1400\n1600\n1800" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t frames = 0;
|
|
||||||
bool is_pal = false;
|
|
||||||
|
|
||||||
while(true) {
|
|
||||||
bool lf = false;
|
|
||||||
int pkttype = sdmp_in.read_command();
|
|
||||||
if(pkttype < 0)
|
|
||||||
break; //End of stream.
|
|
||||||
if((pkttype & 0xF0) == 0) {
|
|
||||||
//Picture. Read the 1MiB of picture data one line pair at a time.
|
|
||||||
size_t fsize = render_frame(sdmp_in, pkttype, flags, yuv_buffer);
|
|
||||||
is_pal = is_pal || (pkttype & CMD_PAL);
|
|
||||||
uint32_t times = dupt.process(yuv_buffer, fsize, pkttype, ts);
|
|
||||||
write_frame(yout, yuv_buffer, fsize, times, frames);
|
|
||||||
ts.advance(pkttype & CMD_PAL, pkttype & CMD_INTERLACED);
|
|
||||||
lf = true;
|
|
||||||
} else if(pkttype == 16) {
|
|
||||||
sdmp_in.copy_audio_sample(sox_out);
|
|
||||||
} else {
|
|
||||||
std::ostringstream str;
|
|
||||||
str << "Unknown command byte " << static_cast<unsigned>(pkttype);
|
|
||||||
throw std::runtime_error(str.str());
|
|
||||||
}
|
|
||||||
if(lf && frames % 100 == 0) {
|
|
||||||
std::cout << "\e[1G" << frames << " frames, " << sox_out.get_samples() << " samples."
|
|
||||||
<< std::flush;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sox_out.close();
|
|
||||||
std::cout << "Sound sampling rate is " << static_cast<double>(sdmp_in.get_apurate()) / 768.0 << "Hz"
|
|
||||||
<< std::endl;
|
|
||||||
std::cout << "Wrote " << sox_out.get_samples() << " samples." << std::endl;
|
|
||||||
std::cout << "Audio length is " << 768.0 * sox_out.get_samples() / sdmp_in.get_apurate() << "s." << std::endl;
|
|
||||||
double vrate = 0;
|
|
||||||
double vrate2 = 0;
|
|
||||||
if(is_pal)
|
|
||||||
vrate2 = 425568.0;
|
|
||||||
else if(flags & FLAG_FRAMERATE)
|
|
||||||
vrate2 = 357368.0;
|
|
||||||
else
|
|
||||||
vrate2 = 357366.0;
|
|
||||||
vrate = sdmp_in.get_cpurate() / vrate2;
|
|
||||||
std::cout << "Video frame rate is " << sdmp_in.get_cpurate() << "/" << vrate2 << "Hz" << std::endl;
|
|
||||||
std::cout << "Wrote " << frames << " frames." << std::endl;
|
|
||||||
std::cout << "Video length is " << frames / vrate << "s." << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void syntax()
|
|
||||||
{
|
|
||||||
std::cerr << "Syntax: sdump2sox [<options>] <input-file> <yuv-output-file> <sox-output-file> "
|
|
||||||
<< "[<tc-output-file>]" << std::endl;
|
|
||||||
std::cerr << "-W\tDump 512-wide instead of 256-wide." << std::endl;
|
|
||||||
std::cerr << "-H\tDump 448/480-high instead of 224/240-high." << std::endl;
|
|
||||||
std::cerr << "-D\tDedup the output (also uses exact timecodes)." << std::endl;
|
|
||||||
std::cerr << "-h\tDump 512x448/480, doing blending for 512x224/240." << std::endl;
|
|
||||||
std::cerr << "-F\tDump at interlaced framerate instead of non-interlaced (no effect if dedup)." << std::endl;
|
|
||||||
std::cerr << "-l\tOffset timecodes by inserting 10 frames spanning 2 seconds (dedup only)." << std::endl;
|
|
||||||
std::cerr << "-L\tOffset timecodes by 2 seconds (dedup only)." << std::endl;
|
|
||||||
std::cerr << "-A\tDo output AR correction." << std::endl;
|
|
||||||
std::cerr << "-f\tDump using full range instead of TV range." << std::endl;
|
|
||||||
std::cerr << "-7\tDump using ITU.709 instead of ITU.601." << std::endl;
|
|
||||||
std::cerr << "-2\tDump using SMPTE-240M instead of ITU.601." << std::endl;
|
|
||||||
std::cerr << "-8\tDump using 8 bits instead of 16 bits." << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reached_main();
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
reached_main();
|
|
||||||
if(argc < 4) {
|
|
||||||
syntax();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
uint32_t flags = 0;
|
|
||||||
uint32_t idx1 = 0;
|
|
||||||
uint32_t idx2 = 0;
|
|
||||||
uint32_t idx3 = 0;
|
|
||||||
uint32_t idx4 = 0;
|
|
||||||
for(int i = 1; i < argc; i++) {
|
|
||||||
if(argv[i][0] == '-')
|
|
||||||
for(unsigned j = 1; argv[i][j]; j++)
|
|
||||||
switch(argv[i][j]) {
|
|
||||||
case 'W':
|
|
||||||
flags |= FLAG_WIDTH;
|
|
||||||
break;
|
|
||||||
case 'H':
|
|
||||||
flags |= FLAG_HEIGHT;
|
|
||||||
break;
|
|
||||||
case 'F':
|
|
||||||
flags |= FLAG_FRAMERATE;
|
|
||||||
break;
|
|
||||||
case 'D':
|
|
||||||
flags |= FLAG_DEDUP;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
flags |= FLAG_FULLRANGE;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
flags |= (FLAG_FAKENLARGE | FLAG_WIDTH | FLAG_HEIGHT);
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
flags |= (FLAG_10FRAMES | FLAG_OFFSET2);
|
|
||||||
break;
|
|
||||||
case 'L':
|
|
||||||
flags |= FLAG_OFFSET2;
|
|
||||||
break;
|
|
||||||
case 'A':
|
|
||||||
flags |= FLAG_AR_CORRECT;
|
|
||||||
break;
|
|
||||||
case '7':
|
|
||||||
if(flags & FLAG_CS_MASK) {
|
|
||||||
syntax();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
flags |= FLAG_ITU709;
|
|
||||||
break;
|
|
||||||
case '2':
|
|
||||||
if(flags & FLAG_CS_MASK) {
|
|
||||||
syntax();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
flags |= FLAG_SMPTE240M;
|
|
||||||
break;
|
|
||||||
case '8':
|
|
||||||
flags |= FLAG_8BIT;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
syntax();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else if(!idx1)
|
|
||||||
idx1 = i;
|
|
||||||
else if(!idx2)
|
|
||||||
idx2 = i;
|
|
||||||
else if(!idx3)
|
|
||||||
idx3 = i;
|
|
||||||
else if(!idx4)
|
|
||||||
idx4 = i;
|
|
||||||
else {
|
|
||||||
syntax();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(idx4 && !(flags & FLAG_DEDUP)) {
|
|
||||||
syntax();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
std::ifstream in(argv[idx1], std::ios::in | std::ios::binary);
|
|
||||||
if(!in) {
|
|
||||||
std::cerr << "Error: Can't open '" << argv[idx1] << "'" << std::endl;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
std::ofstream yout(argv[idx2], std::ios::out | std::ios::binary);
|
|
||||||
if(!yout) {
|
|
||||||
std::cerr << "Error: Can't open '" << argv[idx2] << "'" << std::endl;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
std::ofstream sout(argv[idx3], std::ios::out | std::ios::binary);
|
|
||||||
if(!sout) {
|
|
||||||
std::cerr << "Error: Can't open '" << argv[idx3] << "'" << std::endl;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
std::ofstream tout;
|
|
||||||
if(flags & FLAG_DEDUP) {
|
|
||||||
if(idx4)
|
|
||||||
tout.open(argv[idx4], std::ios::out);
|
|
||||||
else
|
|
||||||
tout.open(argv[idx2] + std::string(".tc"), std::ios::out);
|
|
||||||
if(!tout) {
|
|
||||||
std::cerr << "Error: Can't open '" << argv[idx2] << ".tc'" << std::endl;
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
sdump2sox(in, yout, sout, tout, flags);
|
|
||||||
in.close();
|
|
||||||
yout.close();
|
|
||||||
sout.close();
|
|
||||||
tout.close();
|
|
||||||
} catch(std::exception& e) {
|
|
||||||
std::cerr << "Error: " << e.what() << std::endl;
|
|
||||||
in.close();
|
|
||||||
yout.close();
|
|
||||||
sout.close();
|
|
||||||
tout.close();
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,235 +0,0 @@
|
||||||
#if defined(BSNES_V084) || defined(BSNES_V085) || defined(BSNES_V086) || defined(BSNES_V087)
|
|
||||||
#include "lsnes.hpp"
|
|
||||||
#include "core/advdumper.hpp"
|
|
||||||
#include "core/dispatch.hpp"
|
|
||||||
#include "core/moviedata.hpp"
|
|
||||||
#include "interface/romtype.hpp"
|
|
||||||
#include "library/serialization.hpp"
|
|
||||||
#include "video/tcp.hpp"
|
|
||||||
|
|
||||||
#include <iomanip>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstring>
|
|
||||||
#include <sstream>
|
|
||||||
#include <zlib.h>
|
|
||||||
#include <sstream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#define CUTOFF 2100000000
|
|
||||||
#define SDUMP_FLAG_HIRES 1
|
|
||||||
#define SDUMP_FLAG_INTERLACED 2
|
|
||||||
#define SDUMP_FLAG_OVERSCAN 4
|
|
||||||
#define SDUMP_FLAG_PAL 8
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
void deleter_fn(void* f)
|
|
||||||
{
|
|
||||||
delete reinterpret_cast<std::ofstream*>(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
class sdmp_avsnoop : public information_dispatch
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
sdmp_avsnoop(const std::string& prefix, const std::string& mode) throw(std::bad_alloc,
|
|
||||||
std::runtime_error)
|
|
||||||
: information_dispatch("dump-sdmp")
|
|
||||||
{
|
|
||||||
enable_send_sound();
|
|
||||||
oprefix = prefix;
|
|
||||||
sdump_ss = (mode != "ms");
|
|
||||||
ssize = 0;
|
|
||||||
next_seq = 0;
|
|
||||||
dumped_pic = false;
|
|
||||||
if(mode == "tcp") {
|
|
||||||
out = &(socket_address(prefix).connect());
|
|
||||||
deleter = socket_address::deleter();
|
|
||||||
} else {
|
|
||||||
out = NULL;
|
|
||||||
deleter = deleter_fn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~sdmp_avsnoop() throw()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
if(out)
|
|
||||||
deleter(out);
|
|
||||||
} catch(...) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_raw_frame(const uint32_t* raw, bool hires, bool interlaced, bool overscan, unsigned region)
|
|
||||||
{
|
|
||||||
unsigned flags = 0;
|
|
||||||
flags |= (hires ? SDUMP_FLAG_HIRES : 0);
|
|
||||||
flags |= (interlaced ? SDUMP_FLAG_INTERLACED : 0);
|
|
||||||
flags |= (overscan ? SDUMP_FLAG_OVERSCAN : 0);
|
|
||||||
flags |= (region == VIDEO_REGION_PAL ? SDUMP_FLAG_PAL : 0);
|
|
||||||
unsigned char tbuffer[2049];
|
|
||||||
if(!out || (ssize > CUTOFF && !sdump_ss)) {
|
|
||||||
std::cerr << "Starting new segment" << std::endl;
|
|
||||||
if(out)
|
|
||||||
deleter(out);
|
|
||||||
std::ostringstream str;
|
|
||||||
if(sdump_ss)
|
|
||||||
str << oprefix;
|
|
||||||
else
|
|
||||||
str << oprefix << "_" << std::setw(4) << std::setfill('0') << (next_seq++)
|
|
||||||
<< ".sdmp";
|
|
||||||
std::string str2 = str.str();
|
|
||||||
out = new std::ofstream(str2.c_str(), std::ios::out | std::ios::binary);
|
|
||||||
if(!*out)
|
|
||||||
throw std::runtime_error("Failed to open '" + str2 + "'");
|
|
||||||
write32ube(tbuffer, 0x53444D50U);
|
|
||||||
auto rates = our_rom->rtype->get_snes_rate();
|
|
||||||
write32ube(tbuffer + 4, rates.first);
|
|
||||||
write32ube(tbuffer + 8, rates.second);
|
|
||||||
out->write(reinterpret_cast<char*>(tbuffer), 12);
|
|
||||||
if(!*out)
|
|
||||||
throw std::runtime_error("Failed to write header to '" + str2 + "'");
|
|
||||||
ssize = 12;
|
|
||||||
}
|
|
||||||
dumped_pic = true;
|
|
||||||
tbuffer[0] = flags;
|
|
||||||
for(unsigned i = 0; i < 512; i++) {
|
|
||||||
for(unsigned j = 0; j < 512; j++)
|
|
||||||
write32ube(tbuffer + (4 * j + 1), raw[512 * i + j]);
|
|
||||||
out->write(reinterpret_cast<char*>(tbuffer + (i ? 1 : 0)), i ? 2048 : 2049);
|
|
||||||
}
|
|
||||||
if(!*out)
|
|
||||||
throw std::runtime_error("Failed to write frame");
|
|
||||||
ssize += 1048577;
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_sample(short l, short r)
|
|
||||||
{
|
|
||||||
if(!out || !dumped_pic)
|
|
||||||
return;
|
|
||||||
unsigned char pkt[5];
|
|
||||||
pkt[0] = 16;
|
|
||||||
write16sbe(pkt + 1, l);
|
|
||||||
write16sbe(pkt + 3, r);
|
|
||||||
out->write(reinterpret_cast<char*>(pkt), 5);
|
|
||||||
if(!*out)
|
|
||||||
throw std::runtime_error("Failed to write sample");
|
|
||||||
ssize += 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
void on_dump_end()
|
|
||||||
{
|
|
||||||
deleter(out);
|
|
||||||
out = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_dumper_flag() throw()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
std::string oprefix;
|
|
||||||
bool sdump_ss;
|
|
||||||
bool dumped_pic;
|
|
||||||
uint64_t ssize;
|
|
||||||
uint64_t next_seq;
|
|
||||||
void (*deleter)(void* f);
|
|
||||||
std::ostream* out;
|
|
||||||
};
|
|
||||||
|
|
||||||
sdmp_avsnoop* vid_dumper;
|
|
||||||
|
|
||||||
class adv_sdmp_dumper : public adv_dumper
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
adv_sdmp_dumper() : adv_dumper("INTERNAL-SDMP") { information_dispatch::do_dumper_update(); }
|
|
||||||
~adv_sdmp_dumper() throw();
|
|
||||||
std::set<std::string> list_submodes() throw(std::bad_alloc)
|
|
||||||
{
|
|
||||||
std::set<std::string> x;
|
|
||||||
x.insert("ss");
|
|
||||||
x.insert("ms");
|
|
||||||
x.insert("tcp");
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned mode_details(const std::string& mode) throw()
|
|
||||||
{
|
|
||||||
if(mode == "ss")
|
|
||||||
return target_type_file;
|
|
||||||
if(mode == "ms")
|
|
||||||
return target_type_prefix;
|
|
||||||
if(mode == "tcp")
|
|
||||||
return target_type_special;
|
|
||||||
return target_type_mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string mode_extension(const std::string& mode) throw()
|
|
||||||
{
|
|
||||||
return "sdmp"; //Ignored anyway in non-ss mode.
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name() throw(std::bad_alloc)
|
|
||||||
{
|
|
||||||
return "SDMP";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string modename(const std::string& mode) throw(std::bad_alloc)
|
|
||||||
{
|
|
||||||
if(mode == "ss")
|
|
||||||
return "Single-Segment";
|
|
||||||
if(mode == "ms")
|
|
||||||
return "Multi-Segment";
|
|
||||||
if(mode == "tcp")
|
|
||||||
return "over TCP/IP";
|
|
||||||
return "What?";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool busy()
|
|
||||||
{
|
|
||||||
return (vid_dumper != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void start(const std::string& mode, const std::string& prefix) throw(std::bad_alloc,
|
|
||||||
std::runtime_error)
|
|
||||||
{
|
|
||||||
if(prefix == "")
|
|
||||||
throw std::runtime_error("Expected target");
|
|
||||||
if(vid_dumper)
|
|
||||||
throw std::runtime_error("SDMP Dump already in progress");
|
|
||||||
try {
|
|
||||||
vid_dumper = new sdmp_avsnoop(prefix, mode);
|
|
||||||
} catch(std::bad_alloc& e) {
|
|
||||||
throw;
|
|
||||||
} catch(std::exception& e) {
|
|
||||||
std::ostringstream x;
|
|
||||||
x << "Error starting SDMP dump: " << e.what();
|
|
||||||
throw std::runtime_error(x.str());
|
|
||||||
}
|
|
||||||
messages << "Dumping SDMP (" << mode << ") to " << prefix << std::endl;
|
|
||||||
information_dispatch::do_dumper_update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void end() throw()
|
|
||||||
{
|
|
||||||
if(!vid_dumper)
|
|
||||||
throw std::runtime_error("No SDMP video dump in progress");
|
|
||||||
try {
|
|
||||||
vid_dumper->on_dump_end();
|
|
||||||
messages << "SDMP Dump finished" << std::endl;
|
|
||||||
} catch(std::bad_alloc& e) {
|
|
||||||
throw;
|
|
||||||
} catch(std::exception& e) {
|
|
||||||
messages << "Error ending SDMP dump: " << e.what() << std::endl;
|
|
||||||
}
|
|
||||||
delete vid_dumper;
|
|
||||||
vid_dumper = NULL;
|
|
||||||
information_dispatch::do_dumper_update();
|
|
||||||
}
|
|
||||||
} adv;
|
|
||||||
|
|
||||||
adv_sdmp_dumper::~adv_sdmp_dumper() throw()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
Loading…
Add table
Reference in a new issue