On Thu, Jan 10, 2008 at 5:45 PM, Giel van Schijndel <[EMAIL PROTECTED]> wrote:

> At Wed, 19 Apr 2006 16:05:46 +0100 Angus Lees wrote:
> > So I figured the best way forward with FMV sequences was to keep the
> > released content, but avoid the unreleased "codec130" video codec.  Once
> > we aren't keeping the original files, we may as well dump RPL and use a
> > better known format.  As a step in that direction, here's a tool that
> > will use the binary dlls to extract RPL content into a standard
> > (uncompressed) AVI file.
> >
> > Compiles against libwine, might even work on windows with a different
> > Makefile.  Requires winstr.dll, dec130.dll, edec.dll, winsdec.dll in the
> > same directory as the executable (or in the wine dll search path).
> > streamer.h from original source release is included.
> >
> > This is my first "windows" code, but it seems to actually work  :P
> >
>
> The attached rpl2avi.c mentions "GPL - Angus Lees <mail-address-here>
> April 2006" as it's licensing header.
>
> Which version of the GPL does that mean? Can I use this:
> > 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 2 of the License, or
> > (at your option) any later version.
>
> ??
>
> That is to say, would you agree with attaching the standard GPLv2 boiler
> plate message to that file (rpl2avi.c) as its license header? (I'm
> assuming GPLv2 since the GPLv3 didn't exist yet at that point).
>

(digging through old mail)

Yes, you can absolutely do whatever you want with the code.  Relicense it as
you see fit, realising that I'm compiling against the original 'proprietary'
streamer.h header and (now) have some adpcm code that I believe came from
alsa.

I found I had trouble getting the audio to decode correctly for large files
(devastation.rpl) - so ended up hacking in my own adpcm code (I just ripped
the warzone code in fact).  I've attached the hacked up version which you
should probably use - I think it has a few other annoyances fixed too.

I also have the full set of warzone fmvs cleanly converted to avi/ogg if you
just want them.

-- 
- Gus
/* Convert ARMovie/Eidos' Replay (.RPL) file to uncompressed AVI
 * GPL - Angus Lees <[EMAIL PROTECTED]>  April 2006
 */

/* There seems to be a bug somewhere in the streamer adpcm decode code
   (at least under wine).  It throws a whole bunch of '-1's into the
   decoded audio stream for long files, which sounds like lots of
   static.  Work around this by doing the decoding ourselves. */
#define USE_MY_CODE 1

#define WIN32_LEAN_AND_MEAN	/* heh */
#include <windows.h>
#include <vfw.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <assert.h>

#include "streamer.h"

#define STREAMER_AUDIOOUT_BITSIZE  16 /* streamer always outputs SLE16 */

#ifdef __WINE__
/* This throws a stub warning on wine at present (Wine 0.9.9) */
#define AVIFileExit() do {} while (0)
#endif


#ifdef __WINE__
/* HACK HACK HACK: We need to increase cbIdxRecords to work around an
 * index size limitation in current (0.9.12) wine versions */

#define MAX_AVISTREAMS 8

typedef struct _IAVIFileImpl IAVIFileImpl;

typedef struct _EXTRACHUNKS {
  LPVOID lp;
  DWORD  cb;
} EXTRACHUNKS, *LPEXTRACHUNKS;

typedef struct _IPersistFileImpl {
  /* IUnknown stuff */
  const void *lpVtbl;

  /* IPersistFile stuff */
  void     *paf;
} IPersistFileImpl;

struct _IAVIFileImpl {
  /* IUnknown stuff */
  const void     *lpVtbl;
  LONG              ref;

  /* IAVIFile stuff... */
  IPersistFileImpl  iPersistFile;

  AVIFILEINFOW      fInfo;
  void   *ppStreams[MAX_AVISTREAMS];

  EXTRACHUNKS       fileextra;

  DWORD             dwMoviChunkPos;  /* some stuff for saving ... */
  DWORD             dwIdxChunkPos;
  DWORD             dwNextFramePos;
  DWORD             dwInitialFrames;

  MMCKINFO          ckLastRecord;
  AVIINDEXENTRY    *idxRecords;      /* won't be updated while loading */
  DWORD             nIdxRecords;     /* current fill level */
  DWORD             cbIdxRecords;    /* size of idxRecords */

  /* IPersistFile stuff ... */
  HMMIO             hmmio;
  LPWSTR            szFileName;
  UINT              uMode;
  BOOL              fDirty;
};
#endif


static const char *avierror(HRESULT err) {
  switch (err) {
  case AVIERR_OK: return "OK";
  case AVIERR_UNSUPPORTED: return "UNSUPPORTED";
  case AVIERR_BADFORMAT: return "BADFORMAT";
  case AVIERR_MEMORY: return "MEMORY";
  case AVIERR_INTERNAL: return "INTERNAL";
  case AVIERR_BADFLAGS: return "BADFLAGS";
  case AVIERR_BADPARAM: return "BADPARAM";
  case AVIERR_BADSIZE: return "BADSIZE";
  case AVIERR_BADHANDLE: return "BADHANDLE";
  case AVIERR_FILEREAD: return "FILEREAD";
  case AVIERR_FILEWRITE: return "FILEWRITE";
  case AVIERR_FILEOPEN: return "FILEOPEN";
  case AVIERR_COMPRESSOR: return "COMPRESSOR";
  case AVIERR_NOCOMPRESSOR: return "NOCOMPRESSOR";
  case AVIERR_READONLY: return "READONLY";
  case AVIERR_NODATA: return "NODATA";
  case AVIERR_BUFFERTOOSMALL: return "BUFFERTOOSMALL";
  case AVIERR_CANTCOMPRESS: return "CANTCOMPRESS";
  case AVIERR_USERABORT: return "USERABORT";
  case AVIERR_ERROR: return "ERROR";
  default: return  "Unknown?";
  }
}

static const char *winstr_error(LONG err) {
  switch (err) {
  case STREAMER_OK: return "OK";
  case STREAMER_NO_SOUND_DRIVER: return "STREAMER_NO_SOUND_DRIVER";
  case STREAMER_NO_SOUND_IN_RPL: return "STREAMER_NO_SOUND_IN_RPL";
  case STREAMER_BAD_ALLOC_SOUND_HANDLE: return "STREAMER_BAD_ALLOC_SOUND_HANDLE";
  case STREAMER_BAD_ALLOC_DBUFFERS: return "STREAMER_BAD_ALLOC_DBUFFERS";
  case STREAMER_BAD_ALLOC_SAMPLE_HANDLE: return "STREAMER_BAD_ALLOC_SAMPLE_HANDLE";
  case STREAMER_BAD_ALLOC_TIMER: return "STREAMER_BAD_ALLOC_TIMER";
  case STREAMER_BAD_ALLOC_TIMER_HANDLE: return "STREAMER_BAD_ALLOC_TIMER_HANDLE";
  case STREAMER_BAD_ALLOC_VIDEO_BUFFER: return "STREAMER_BAD_ALLOC_VIDEO_BUFFER";
  case STREAMER_BAD_ALLOC_GLCW_LUT: return "STREAMER_BAD_ALLOC_GLCW_LUT";
  case STREAMER_BAD_ALLOC_LSCW_LUT: return "STREAMER_BAD_ALLOC_LSCW_LUT";
  case STREAMER_BAD_ALLOC_GSCW_LUT: return "STREAMER_BAD_ALLOC_GSCW_LUT";
  case STREAMER_BAD_ALLOC_VIDEO_HANDLE: return "STREAMER_BAD_ALLOC_VIDEO_HANDLE";
  case STREAMER_VESA_ERROR: return "STREAMER_VESA_ERROR";
  case STREAMER_BAD_ALLOC_SB_OFFSETS: return "STREAMER_BAD_ALLOC_SB_OFFSETS";
  case STREAMER_BAD_ALLOC_MOVIE_HANDLE: return "STREAMER_BAD_ALLOC_MOVIE_HANDLE";
  case STREAMER_BAD_ALLOC_MOVIE_INDEX: return "STREAMER_BAD_ALLOC_MOVIE_INDEX";
  case STREAMER_FILE_OPEN_ERROR: return "STREAMER_FILE_OPEN_ERROR";
  case STREAMER_BAD_PLAYBACK_STATE: return "STREAMER_BAD_PLAYBACK_STATE";
  case STREAMER_FILE_OPEN_ERROR_HIGH: return "STREAMER_FILE_OPEN_ERROR_HIGH";
  case STREAMER_BAD_MOVIE_HEADER: return "STREAMER_BAD_MOVIE_HEADER";
  case STREAMER_CHUNK_OUT_FILE_ERROR: return "STREAMER_CHUNK_OUT_FILE_ERROR";
  case STREAMER_BAD_ALLOC_OPTIONS: return "STREAMER_BAD_ALLOC_OPTIONS";
  case STREAMER_BAD_ALIGN_SEEK: return "STREAMER_BAD_ALIGN_SEEK";
  case STREAMER_PRELOAD_KEY_PRESS: return "STREAMER_PRELOAD_KEY_PRESS";
  case STREAMER_FILE_READ_ERROR: return "STREAMER_FILE_READ_ERROR";
  case STREAMER_BAD_ALLOC_SCREEN_BUFFER: return "STREAMER_BAD_ALLOC_SCREEN_BUFFER";
  case STREAMER_BAD_SOUND_CALLBACK: return "STREAMER_BAD_SOUND_CALLBACK";
  case STREAMER_BADTHREADCREATE: return "STREAMER_BADTHREADCREATE";
  case STREAMER_BADDATARATE: return "STREAMER_BADDATARATE";
  case STREAMER_BADMOVIEHANDLE: return "STREAMER_BADMOVIEHANDLE";
  case STREAMER_NOEFFECTS: return "STREAMER_NOEFFECTS";
  case STREAMER_FINISHEDVIDEO: return "STREAMER_FINISHEDVIDEO";
  case STREAMER_FINISHEDAUDIO: return "STREAMER_FINISHEDAUDIO";
  case STREAMER_BAD_ALLOC_SOUND_BUFFER: return "STREAMER_BAD_ALLOC_SOUND_BUFFER";
  case STREAMER_NOTINGRAPHICSMODE: return "STREAMER_NOTINGRAPHICSMODE";
  case STREAMER_BAD_MOVIEHANDLE: return "STREAMER_BAD_MOVIEHANDLE";
  case STREAMER_BAD_VIDEOHANDLE: return "STREAMER_BAD_VIDEOHANDLE";
  case STREAMER_BAD_SOUNDHANDLE: return "STREAMER_BAD_SOUNDHANDLE";
  case STREAMER_BADSOUNDBUFFERNO: return "STREAMER_BADSOUNDBUFFERNO";
  case STREAMER_NODATADATASTREAM: return "STREAMER_NODATADATASTREAM";
  case STREAMER_BADMOVIEFORMAT: return "STREAMER_BADMOVIEFORMAT";
  case STREAMER_130CODECOPENERROR: return "STREAMER_130CODECOPENERROR";
  default: return "Unknown?";
  }
}

static signed char index_adjust[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };

static short step_size[89] = {
	7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
	19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
	50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
	130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
	337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
	876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
	2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
	5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
	15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};

struct adpcm_state {
  int pred_val, step_idx;
};

/* This code is "borrowed" from the ALSA library 
   http://www.alsa-project.org */
static unsigned short
adpcm_decode_sample(struct adpcm_state *state, char code) {
	short pred_diff;	/* Predicted difference to next sample */
	short step;		/* holds previous step_size value */
	char sign;

	int i;

	/* Separate sign and magnitude */
	sign = code & 0x8;
	code &= 0x7;

	/*
	 * Computes pred_diff = (code + 0.5) * step / 4,
	 * but see comment in adpcm_coder.
	 */

	step = step_size[state->step_idx];

	/* Compute difference and new predicted value */
	pred_diff = step >> 3;
	for (i = 0x4; i; i >>= 1, step >>= 1) {
		if (code & i) {
			pred_diff += step;
		}
	}
	state->pred_val += (sign) ? -pred_diff : pred_diff;

	/* Clamp output value */
	if (state->pred_val > 32767) {
		state->pred_val = 32767;
	} else if (state->pred_val < -32768) {
		state->pred_val = -32768;
	}

	/* Find new step_size index value */
	state->step_idx += index_adjust[(int) code];

	if (state->step_idx < 0) {
		state->step_idx = 0;
	} else if (state->step_idx > 88) {
		state->step_idx = 88;
	}
	return state->pred_val;
}

void adpcm_init(struct adpcm_state *state) {
	state->pred_val = 0;
	state->step_idx = 0;
}

void adpcm_decode(struct adpcm_state *state,
		  unsigned char* input, unsigned int input_size,
		  unsigned short* output) {
	unsigned int i;

	for (i = 0; i < input_size; ++i) {
		unsigned char two_samples = input[i];
		*(output++) = adpcm_decode_sample(state, two_samples >> 4);
		*(output++) = adpcm_decode_sample(state, two_samples & 15);
	}
}

static long last_updated;
static PAVISTREAM audio;
static size_t audiobuffersize;
static size_t audiosamplesize;

static void sound_callback(LPSOUNDHANDLE shandle) {
  HRESULT res;
  long state;

  /* printf("sound_callback called!\n"); */

  state = Streamer_GetSoundDecodeMode(shandle);

#ifndef USE_MY_CODE
  res = AVIStreamWrite(audio, -1, audiobuffersize / audiosamplesize,
		       Streamer_GetSoundBuffer(shandle, state),
		       audiobuffersize, 0, 0, 0);
  if (FAILED(res)) {
    fprintf(stderr, "AVIStreamWrite(audio) returned %s\n", avierror(res));
    return;
  }
#endif

  Streamer_SetSoundDecodeMode(shandle, SSDM_IDLE);
  last_updated = state;
}

#ifdef __WINE__
static char *convert_path(const char *path) {
  WCHAR *dospath;
  char *newpath;

  dospath = wine_get_dos_file_name(path);
  if (!dospath) {
    fprintf(stderr, "Wine can't get a DOS path for %s\n", path);
    exit(1);
  }

  newpath = malloc(MAX_PATH);	/* leak */
  newpath[0] = '\0';
  WideCharToMultiByte(CP_UNIXCP, 0, dospath, -1, newpath, MAX_PATH, NULL, NULL);

  return newpath;
}
#else
#define convert_path(p) (p)
#endif


struct RPL {
  FILE* f;
  int loop;
  size_t nchunks, current_chunk;
  int width, height, bpp;
  float fps;
  int frames_per_chunk;
  int even_chunk_size, odd_chunk_size;
  int channels;

  long chunk_catalog_off, sprite_off, key_frame_off;

  struct rpl_chunk {
    unsigned long offset;
    size_t video_size, audio_size;
  } *chunks;

  int sound_format, sound_bits, audio_freq;
  unsigned char *audio_buffer;
  unsigned short *audio_decode_buffer;
} rpl;


static char *downcase_string(char *str)
{
  size_t i;
  for (i = 0; str[i]; i++) {
    if (isupper(str[i]))
      str[i] = tolower(str[i]);
  }
  return str;
}

static char *readline(FILE *f, char *linebuf, size_t len) {
	char c;
	size_t i = 0;

	for (i = 0; i < len-1; i++) {
	  if (fread(&c, 1, 1, f) != 1) {
	    perror("Error reading from sequence file");
	    break;
	  }
	  if (c == '\n' || !isprint(c))
	    break;
	  linebuf[i] = c;
	}

	linebuf[i] = '\0';

	return linebuf;
}

static int readint(FILE *f, char *linebuf, size_t len, char **rest) {
  int num, numread;
	readline(f, linebuf, len);
	if (sscanf(linebuf, "%u %n", &num, &numread) < 1) {
	  if (rest) *rest = "";
	  num = 0;
	}
	else if (rest)
	  *rest = downcase_string(&linebuf[numread]);

	return num;
}

static float readfloat(FILE *f, char *linebuf, size_t len, char **rest) {
	float num;
	int numread;
	readline(f, linebuf, len);
	if (sscanf(linebuf, "%f %n", &num, &numread) < 1) {
	  if (rest) *rest = "";
	  num = 0.0;
	} else if (rest)
	  *rest = downcase_string(&linebuf[numread]);

	return num;
}

static void init_rpl(const char* path) {
  char buf[80], *comment;
  int tmp;
  size_t i, len = sizeof(buf);
  size_t max_audio_size = 0;
  FILE* f = fopen(path, "rb");
  rpl.f = f;
  if (!f) {
    perror("Unable to open rpl file");
    exit(1);
  }

  rpl.chunks = NULL;
  rpl.current_chunk = 0;
  rpl.loop = 0;
  rpl.audio_buffer = NULL;
  rpl.audio_decode_buffer = NULL;

  if (strcmp(readline(f, buf, len), "ARMovie") != 0)
    fprintf(stderr, "Missing RPL magic number\n");

  readline(f, buf, len);
  /* fprintf(stderr, "Header filename: %s\n", buf); */
  readline(f, buf, len);
  /* fprintf(stderr, "Copyright: %s\n", buf); */
  readline(f, buf, len);
  /* fprintf(stderr, "Author: %s\n", buf); */

  tmp = readint(f, buf, len, NULL);
  switch (tmp) {
  case 130:
    /* init_dec130(&rpl->dec130_state);
       rpl->decode_video = rpl_dec130_decode; */
    break;
  case 13: /* mpeg? */
  case 18: /* h263? */
  default:
    fprintf(stderr, "Unknown video format %i\n", tmp);
    break;
  }

  rpl.width = readint(f, buf, len, NULL);
  rpl.height = readint(f, buf, len, NULL);
  rpl.bpp = readint(f, buf, len, &comment);
#if 0
  /* Eidos RPL files lie about this part of the 'standard' */
  if (!strstr(comment, "yuv"))
    debug(LOG_VIDEO, "%s: Header doesn't declare this as YUV."
	  " We only support YUV at present..\n", m->filename);
#endif

  rpl.fps = readfloat(f, buf, len, NULL);

  rpl.sound_format = readint(f, buf, len, &comment);
  rpl.audio_freq = readint(f, buf, len, NULL);
  rpl.channels = readint(f, buf, len, NULL);
  rpl.sound_bits = readint(f, buf, len, NULL);

#if 0
  switch (sound_format) {
  case 1: /* PCM */
    if (sound_bits == 8)
      m->audio_format =
	rpl->channels == 1 ? AL_FORMAT_MONO8 : AL_FORMAT_STEREO8;
    else if (sound_bits == 16)
      m->audio_format =
	rpl->channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
    else
      break;
    rpl->decode_audio = rpl_passthru_audio;
    break;
  case 101:
    if (al_supports_adpcm) {
      debug(LOG_VIDEO, "Using native OpenAL adpcm support (channels=%d)\n",
	    rpl->channels);
      m->audio_format =	rpl->channels == 1
	? AL_FORMAT_IMA_ADPCM_MONO16_EXT
	: AL_FORMAT_IMA_ADPCM_STEREO16_EXT;
      rpl->decode_audio = rpl_passthru_audio;
    }
    else {
      m->audio_format =	rpl->channels == 1
	? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
      rpl->decode_audio = rpl_adpcm_decode;
    }
    break;
  }

  if (sound_format && !rpl->decode_audio)
    debug(LOG_VIDEO, "Unknown RPL sound format %i (bits=%d, channels=%d)\n",
	  sound_format, sound_bits, rpl->channels);
#endif

  rpl.frames_per_chunk = readint(f, buf, len, NULL);
  rpl.nchunks = readint(f, buf, len, &comment);
  if (strstr(comment, "forever")) rpl.loop = 1;

  rpl.even_chunk_size = readint(f, buf, len, NULL);
  rpl.odd_chunk_size = readint(f, buf, len, NULL);
  rpl.chunk_catalog_off = readint(f, buf, len, NULL);
  rpl.sprite_off = readint(f, buf, len, NULL);
  rpl.key_frame_off = readint(f, buf, len, NULL);

  rpl.chunks = calloc(rpl.nchunks+1, sizeof(struct rpl_chunk));

  fseek(f, rpl.chunk_catalog_off, SEEK_SET);
  for (i = 0; i < rpl.nchunks+1; i++) {
    struct rpl_chunk *c = &(rpl.chunks[i]);
    readline(f, buf, len);
    if (sscanf(buf, "%lu , %zu ; %zu",
	       &c->offset, &c->video_size, &c->audio_size) != 3)
      fprintf(stderr, "Bad catalog line: %s\n", buf);

    if (c->audio_size > max_audio_size)
      max_audio_size = c->audio_size;
  }

  rpl.audio_buffer = malloc(max_audio_size);
  rpl.audio_decode_buffer = malloc(4*max_audio_size);
}

int main(int argc, char **argv) {
  char *inputfilename, *outputfilename, *unixinfile, *unixoutfile,
    *metafilename = NULL;
  LONG ret;
  LPMOVIEHANDLE mhandle;
  LPVIDEOHANDLE vhandle;
  LPSOUNDHANDLE shandle = NULL;
  LONG buffer_pixel_width, buffer_pixel_height;	/* what are these for?? */
  BYTE ap, ac, rp, rc, gp, gc, bp, bc;
  LPBYTE videobuffer, flipbuffer, soundbuffer;
  BOOL audio_finished;
  PAVIFILE avi;
  PAVISTREAM video;
  AVISTREAMINFO info;
  BITMAPINFOHEADER bi;
  HRESULT res;
  int pixelsize;
  size_t rowsize;
  struct adpcm_state adpcm_state;
  int arg;

  if (argc >= 3 && strcmp(argv[1], "--meta")==0) {
    metafilename = argv[2];
    arg = 3;
  }
  else
    arg = 1;

  if (argc - arg != 2) {
    fprintf(stderr, "Usage: %s [--meta FILE] <in.rpl> <out.avi>\n", argv[0]);
    exit(1);
  }

  unixinfile = argv[arg];
  unixoutfile = argv[arg+1];

  inputfilename = convert_path(unixinfile);
  outputfilename = convert_path(unixoutfile);


  ret = Streamer_InitMovie(&mhandle, NULL, 0, inputfilename,
			   STREAMER_BUFFERSIZE, SIM_NONE);

  if (ret != STREAMER_OK) {
    fprintf(stderr, "Streamer_InitMovie(%s) returned %s\n",
	    inputfilename, winstr_error(ret));
    exit(1);
  }

  printf("Name: %s\n", Movie_GetName(mhandle));
  printf("Author: %s\n", Movie_GetAuthor(mhandle));
  printf("Copyright: %s\n", Movie_GetCopyright(mhandle));
  if (metafilename) {
    FILE *f = fopen(metafilename, "w");
    if (!f) {
      perror(metafilename);
      exit(1);
    }
    fprintf(f, "--title='%s'\n", Movie_GetName(mhandle));
    fprintf(f, "--artist='%s'\n", Movie_GetAuthor(mhandle));
    fprintf(f, "--copyright='%s'\n", Movie_GetCopyright(mhandle));
    fclose(f);
  }

  AVIFileInit();

  res = AVIFileOpen(&avi, outputfilename, OF_WRITE|OF_CREATE, 0);
  if (FAILED(res)) {
    AVIFileExit();
    fprintf(stderr, "Error writing %s: %s\n", outputfilename, avierror(res));
    exit(1);
  }


  ret = Streamer_InitVideo(&vhandle, mhandle,
			   Movie_GetXSize(mhandle), Movie_GetYSize(mhandle),
			   0, 0, 0, 0, 0, 0,
			   DFLAG_INVIEWPORT,
			   &buffer_pixel_width, &buffer_pixel_height);
  if (ret != STREAMER_OK) {
    fprintf(stderr, "Streamer_InitVideo() returned %s\n", winstr_error(ret));
    exit(1);
  }
  Streamer_SetVideoPitch(vhandle,
			 Movie_GetXSize(mhandle), Movie_GetYSize(mhandle));


  switch (Movie_GetColourDepth(mhandle)) {
  default:
    fprintf(stderr, "Don't cope with native depth %ld yet, using 24bit..\n",
	    Movie_GetColourDepth(mhandle));
    /* fall through */
  case 32:			/* top byte unused, so really 24bit.. */
  case 24:
    /* BGR */
    pixelsize = 32;
    ap = 24;
    ac = 0;
    rp = 16;
    rc = 8;
    gp = 8;
    gc = 8;
    bp = 0;
    bc = 8;
    break;
  case 16:
    /* 555 RGB */
    pixelsize = 16;
    ap = 15;
    ac = 1;
    rp = 10;
    rc = 5;
    gp = 5;
    gc = 5;
    bp = 0;
    bc = 5;
    break;
  }

  printf("Found %ldx%ldx%ldbpp %gfps video stream\n",
	 Movie_GetXSize(mhandle), Movie_GetYSize(mhandle),
	 Movie_GetColourDepth(mhandle), Movie_GetFrameRate(mhandle));

  ret = Streamer_SetPixelFormat(vhandle, Movie_GetColourDepth(mhandle),
				ap, ac, rp, rc, gp, gc, bp, bc);
  if (ret != STREAMER_OK) {
    fprintf(stderr, "Streamer_SetPixelFormat returned %s\n", winstr_error(ret));
    exit(1);
  }


  memset(&info, 0, sizeof(info));
  info.fccType = streamtypeVIDEO;
  info.dwScale = 10000;
  info.dwRate = Movie_GetFrameRate(mhandle) * 10000;
  info.dwSuggestedBufferSize =
    Movie_GetXSize(mhandle) * Movie_GetYSize(mhandle) * pixelsize / 8;
  SetRect(&info.rcFrame, 0, 0,
	  Movie_GetXSize(mhandle), Movie_GetYSize(mhandle));
  res = AVIFileCreateStream(avi, &video, &info);
  if (FAILED(res)) {
    fprintf(stderr, "AVIFileCreateStream(video) returned %s\n", avierror(res));
    exit(1);
  }

  memset(&bi, 0, sizeof(bi));
  bi.biSize = sizeof(bi);
  bi.biWidth = Movie_GetXSize(mhandle);
  bi.biHeight = Movie_GetYSize(mhandle);
  bi.biBitCount = pixelsize;
  bi.biSizeImage = bi.biWidth * bi.biHeight * bi.biBitCount / 8;
  bi.biPlanes = 1;
  bi.biCompression = BI_RGB;
  res = AVIStreamSetFormat(video, 0, &bi, bi.biSize);
  if (FAILED(res)) {
    fprintf(stderr, "AVIStreamSetFormat(video) returned %s\n", avierror(res));
    exit(1);
  }



  if (Movie_GetSoundChannels(mhandle) &&
      Movie_GetSoundPrecision(mhandle) &&
      Movie_GetSoundRate(mhandle)) {
    long compression;
    BOOL pcm;
    long precision = Movie_GetSoundPrecision(mhandle);
    long channels = Movie_GetSoundChannels(mhandle);
    WAVEFORMATEX wf;

    if (precision == 4) {
      compression = 4;
      pcm = FALSE;
    } else {
      compression = 1;
      pcm = TRUE;
    }

    printf("Found %ldbit %ldHz %s audio stream\n",
	   precision, Movie_GetSoundRate(mhandle),
	   channels == 1 ? "mono" : "stereo");

    audiosamplesize = channels * STREAMER_AUDIOOUT_BITSIZE / 8;

    /* Should be one video frame's worth of (uncompressed) audio, so
     * we get a nicely interleaved AVI file.. */
    audiobuffersize = Movie_GetSoundRate(mhandle) * audiosamplesize /
      Movie_GetFrameRate(mhandle);

    ret = Streamer_InitSound(sound_callback, &shandle,
			     audiobuffersize,
			     compression, pcm, 4096, channels);
    if (ret != STREAMER_OK) {
      fprintf(stderr, "Streamer_InitSound() returned %s\n", winstr_error(ret));
      exit(1);
    }

    memset(&info, 0, sizeof(info));
    info.fccType = streamtypeAUDIO;
    info.dwSampleSize = audiosamplesize;
    info.dwScale = 1;
    info.dwRate = Movie_GetSoundRate(mhandle);
    info.dwQuality = -1;

    res = AVIFileCreateStream(avi, &audio, &info);
    if (FAILED(res)) {
      fprintf(stderr, "AVIFileCreateStream(audio) returned %s\n", avierror(res));
      exit(1);
    }

    memset(&wf, 0, sizeof(wf));
    wf.wFormatTag = WAVE_FORMAT_PCM;
    wf.nChannels = channels;
    wf.nSamplesPerSec = Movie_GetSoundRate(mhandle);
    wf.wBitsPerSample = STREAMER_AUDIOOUT_BITSIZE;
    wf.nBlockAlign = audiosamplesize;
    wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
    wf.cbSize = 0;

#if USE_MY_CODE
    init_rpl(unixinfile);
    adpcm_init(&adpcm_state);
#endif

    AVIStreamSetFormat(audio, 0, &wf, sizeof(WAVEFORMATEX));

    soundbuffer = malloc(audiobuffersize * 2);
    if (!soundbuffer) {
      fprintf(stderr, "Malloc failed, out of memory\n");
      exit(1);
    }

    Streamer_SetSoundBuffer(shandle, 0, soundbuffer);
    Streamer_SetSoundBuffer(shandle, 1, soundbuffer + audiobuffersize);
  }

  if (shandle)
    Streamer_SetSoundDecodeMode(shandle, SSDM_IDLE);
  last_updated = SSDM_SECONDBUFFER;

  videobuffer = malloc(Movie_GetXSize(mhandle) * Movie_GetYSize(mhandle) *
		       pixelsize / 8);
  flipbuffer = malloc(Movie_GetXSize(mhandle) * Movie_GetYSize(mhandle) *
		      pixelsize / 8);
  if (!videobuffer || !flipbuffer) {
    fprintf(stderr, "Out of memory allocating video buffer\n");
    exit(1);
  }

  /* used during image copy below */
  rowsize = pixelsize * Movie_GetXSize(mhandle) / 8;

#ifdef __WINE__
  /* HACK HACK HACK - work around 1024 index entry limit in wine 0.9.12.
   * See http://bugs.winehq.com/show_bug.cgi?id=5137 */
  if (Movie_GetTotalFrames(mhandle) > 1024)
    ((IAVIFileImpl*)avi)->cbIdxRecords =
      Movie_GetTotalFrames(mhandle) * sizeof(AVIINDEXENTRY);
#endif


  /* kicks the file into interleaved mode */
  AVIFileEndRecord(avi);


  /* This prefills audio buffer, etc */
  ret = Streamer_InitStreaming(mhandle, vhandle, shandle);
  if (ret != STREAMER_OK) {
    fprintf(stderr, "Streamer_InitStreaming returned %s\n", winstr_error(ret));
    exit(1);
  }


  audio_finished = (shandle == NULL);

  /* main loop */
  while (1) {
    LONG i;

    ret = Streamer_Stream(mhandle, vhandle, shandle, NULL,
			  1, /* framestoplay */
			  videobuffer, NULL,
			  NULL, 0);
    if (ret == STREAMER_BADDATARATE) {
      /* couldn't read enough in one go */
      continue;
    }
    else if (ret == STREAMER_FINISHEDVIDEO) {
      break;
    } else if (ret == STREAMER_FINISHEDAUDIO) {
      audio_finished = 1;
    } else if (ret != STREAMER_OK) {
      fprintf(stderr, "Streamer_Stream returned %s\n", winstr_error(ret));
      exit(1);
    }

    /* Streamer calls the audio callback during the *next* _Stream()
     * call, so we need to write the rec marker here, between the call
     * to _Stream() and writing out the "next" video frame */
    AVIFileEndRecord(avi);

    printf("[%ld] ", Movie_GetCurrentFrame(mhandle));

    /* Flip video frame vertically for AVI file.
     * NB: Can't flip videobuffer in-place, since Streamer seems to
     * only make incremental changes to buffer, rather than redrawing
     * completely each time */
    for (i = 0; i < Movie_GetYSize(mhandle); i++) {
      memcpy(flipbuffer + (Movie_GetYSize(mhandle) - i - 1) * rowsize,
	     videobuffer + i * rowsize,
	     rowsize);
    }

    res = AVIStreamWrite(video, -1, 1, flipbuffer,
			 Movie_GetXSize(mhandle) * Movie_GetYSize(mhandle) *
			 pixelsize / 8,
			 AVIIF_KEYFRAME, 0, 0);
    if (FAILED(res))
      fprintf(stderr, "AVIStreamWrite(video) returned %s\n", avierror(res));

    if (!audio_finished) {
#ifdef USE_MY_CODE
      HRESULT res;
      struct rpl_chunk *c = &rpl.chunks[Movie_GetCurrentFrame(mhandle)-1];
      fseek(rpl.f, c->offset + c->video_size, SEEK_SET);
      fread(rpl.audio_buffer, c->audio_size, 1, rpl.f);
      if (rpl.sound_format == 101 && rpl.sound_bits == 4) { /* adpcm */
	adpcm_decode(&adpcm_state, rpl.audio_buffer, c->audio_size,
		     rpl.audio_decode_buffer);
	size_t outbuflen = c->audio_size * 4;
	res = AVIStreamWrite(audio, -1, outbuflen/audiosamplesize,
			     rpl.audio_decode_buffer, outbuflen,
			     0, 0, 0);
      } else if (rpl.sound_format == 1 && rpl.sound_bits == 16) { /* sle16 */
	res = AVIStreamWrite(audio, -1, c->audio_size/audiosamplesize,
			     rpl.audio_buffer, c->audio_size,
			     0, 0, 0);
      } else {
	fprintf(stderr, "Unknown sound codec (fmt=%d bits=%d)\n",
		rpl.sound_format, rpl.sound_bits);
	exit(1);
      }
      if (FAILED(res)) {
	fprintf(stderr, "AVIStreamWrite(audio) returned %s\n", avierror(res));
	exit(1);
      }
#endif
      if (Streamer_GetSoundDecodeMode(shandle) == SSDM_IDLE) {
	/* No need for double buffering, since we're synchronously
	 * writing out the entire buffer to the AVI file.  Left here,
	 * since it shows the general mechanism Streamer expects to
	 * deal with. */
	LONG mode = last_updated == SSDM_FIRSTBUFFER ?
	  SSDM_SECONDBUFFER : SSDM_FIRSTBUFFER;
	Streamer_SetSoundDecodeMode(shandle, mode);
      }
    }
  }

  AVIFileEndRecord(avi);

  printf("Done.\n");

  AVIStreamRelease(video);
  if (shandle) AVIStreamRelease(audio);
  AVIFileRelease(avi);

  Streamer_ShutDownSound(&shandle);
  Streamer_ShutDownVideo(&vhandle);
  Streamer_ShutDownMovie(&mhandle);

  AVIFileExit();

  return 0;
}
_______________________________________________
Warzone-dev mailing list
Warzone-dev@gna.org
https://mail.gna.org/listinfo/warzone-dev

Reply via email to