Converting an .fmv to a .vmv is slighly more complicated than converting in the other direction. Before you convert, you need to make a movie that was recorded from reset. It's easy though, just go to Options->Movie in VirtuaNES, and check the box "Start a record from reset". Open the ROM, record a movie, stop the movie, then exit.
Once you have that, then here's in the C code that will do the conversion:
// convert .fmv to .vmv
// example:
// convert C:\GAMES\fam510\genisto-smb2.fmv "C:\GAMES\VirtuaNes\movie\Super Mario Bros 2 (U) (PRG 0).vmv" smb2.vmv
// convert C:\GAMES\fam510\sleepzteam-zelda.fmv "C:\GAMES\VirtuaNes\movie\Legend of Zelda, The (U) (PRG 0).vmv" zelda.vmv
static unsigned long crc_table[256];
void gen_crc_table(void)
{
unsigned long crc, poly;
int i, j;
poly=0x04c11db7L;
for(i=0; i<256; i++)
{
crc=i<<24;
for(j=8; j>0; j--)
{
if(crc&0x80000000L)
crc=(crc<<1)^poly;
else
crc<<=1;
}
crc_table[i]=crc;
}
}
unsigned long get_crc(unsigned char* buf, int length)
{
register unsigned long crc;
int i;
crc=0xffffffffL;
for(i=0; i<length; ++i)
{
crc=(crc<<8)^crc_table[(crc>>24)^buf[i]];
}
return crc^0xffffffffL;
}
static const char* vmv_header_string="VirtuaNES MV\x00\x03\x78\x00";
int main(int argc, char** argv)
{
if(argc<4)
{
printf("usage: convert <input_fmv_file> <input_vmv_from_reset> <output_vmv_file>\n");
exit(0);
}
FILE* f_in=fopen(argv[2], "rb");
if(!f_in)
{
perror("fopen\n");
exit(0);
}
unsigned char vmv_header_buf[0x40];
fread(vmv_header_buf, 1, 0x40, f_in);
unsigned long save_state_len=*(unsigned long*)(&vmv_header_buf[0x34])-0x40;
unsigned long save_state_chksum=*(unsigned long*)(&vmv_header_buf[0x14]);
unsigned char* save_state_buf=(unsigned char*)malloc(save_state_len);
fread(save_state_buf, 1, save_state_len, f_in);
fclose(f_in);
memset(vmv_header_buf, 0, 0x40);
memcpy(vmv_header_buf, vmv_header_string, 0x10);
f_in=fopen(argv[1], "rb");
if(!f_in)
{
perror("fopen\n");
exit(0);
}
unsigned char ctr_flags;
unsigned long rerecord_count;
unsigned long num_samples;
fseek(f_in, 5, SEEK_SET);
fread(&ctr_flags, 1, 1, f_in);
fseek(f_in, 10, SEEK_SET);
fread(&rerecord_count, 1, 4, f_in);
fseek(f_in, 0, SEEK_END);
num_samples=ftell(f_in)-0x90;
unsigned long num_frames=num_samples;
if(ctr_flags & 0x40)
{
num_frames=num_samples>>1;
}
vmv_header_buf[0x10]=(ctr_flags & 0x40) ? 0x83 : 0x81;
*(unsigned long*)(&vmv_header_buf[0x14])=save_state_chksum;
*(unsigned long*)(&vmv_header_buf[0x1c])=rerecord_count+1;
memcpy(&vmv_header_buf[0x20], "\x03\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00", 0x10);
*(unsigned long*)(&vmv_header_buf[0x30])=0x40+save_state_len+num_samples;
*(unsigned long*)(&vmv_header_buf[0x34])=0x40+save_state_len;
*(unsigned long*)(&vmv_header_buf[0x38])=num_frames;
gen_crc_table();
*(unsigned long*)(&vmv_header_buf[0x3c])=get_crc(vmv_header_buf, 0x3c);
FILE* f_out=fopen(argv[3], "wb");
if(!f_out)
{
perror("fopen\n");
exit(0);
}
fwrite(vmv_header_buf, 1, 0x40, f_out);
fwrite(save_state_buf, 1, save_state_len, f_out);
unsigned char buf[0x1000];
fseek(f_in, 0x90, SEEK_SET);
unsigned long i;
for(i=0; i<num_samples; )
{
long n=(num_samples-i)>0x1000 ? 0x1000 : (num_samples-i);
fread(buf, 1, n, f_in);
long x;
for(x=0; x<n; ++x)
{
unsigned char c=0;
if(buf[x]&0x01) c|=0x80;
if(buf[x]&0x02) c|=0x40;
if(buf[x]&0x04) c|=0x10;
if(buf[x]&0x08) c|=0x20;
if(buf[x]&0x10) c|=0x02;
if(buf[x]&0x20) c|=0x01;
if(buf[x]&0x40) c|=0x04;
if(buf[x]&0x80) c|=0x08;
buf[x]=c;
}
fwrite(buf, 1, n, f_out);
i+=n;
}
fwrite(save_state_buf, 1, save_state_len, f_out);
fclose(f_out);
fclose(f_in);
free(save_state_buf);
return 0;
}
Give the .fmv file as the first argument, the short movie you created above as the second argument, and the output .vmv filename as the last argument.
I've only tested this method on two movies so far: Genisto's SMB2 run and Sleepz' Zelda run. Genisto's movie plays back fine, but Sleepz' movie desyncs before Link even gets the wooden sword. :(
If you manage to make a .vmv that doesn't desync, you should consider yourself lucky.
Good luck!