Blargg snes_spc SPC Export


This blog reveals exporting an SPC from Blargg’s snes_spc 0.9.0, my custom blend of snes_spc 0.9.0 and gme, and its stock limitations.

and now some random but maybe useful info

struct track_info_t
	long track_count;
	/* times in milliseconds; -1 if unknown */
	long length;
	long intro_length;
	long loop_length;
	/* empty string if not available */
	char system    [256];
	char game      [256];
	char song      [256];
	char author    [256];
	char copyright [256];
	char comment   [256];
	char dumper    [256];
enum { gme_max_field = 255 };

SPC Export

Write Header

Basically a home-shopped routine to write the header info will be required. Some combination of the Spc_Emu::header_t will make it happen..


// SPC file header
enum { header_size = 0x100 };
struct header_t
  char tag [35];
  byte format;
  byte version;
  byte pc [2];
  byte a, x, y, psw, sp;
  byte unused [2];
  char song [32];
  char game [32];
  char dumper [16];
  char comment [32];
  byte date [11];
  byte len_secs [3];
  byte fade_msec [4];
  char author [32]; // sometimes first char should be skipped (see official SPC spec)
  byte mute_mask;
  byte emulator;
  byte unused2 [46];
// Header for currently loaded file
header_t const& header() const { return *(header_t const*) file_data; }

The SPC Export routine can use the header() when exporting an SPC that already has a header, but here’s how the GUI should do things:

There will be a File -> Export -> SPC button in the Menu_Bar class. For this, another feature to Expanding_List must be added.. allowing a menu_item to trigger the presentation of another array of menu_items to the right of it. And event processing thereof.

But anyways, when the export SPC button is pressed, a new window must display that allows the user to customize the SPC header entries. Any other customizations that would be necessary?

Writing the Rest of the SPC is easy

Today I discovered how to do this to the best of the emu’s abilities (snes_spc 0.9.0) — I’ll keep saying there is potentially a new version of snes_spc/gme at SNES_GSS google code but I don’t bother to look at it. Here’s what I discovered.

The best it can currently do is export an SPC after having already loaded one, and it exports with a blank header. So some ID666 routines will need to be invented to write a proper header, and inspiration will surely come from this website.

Here is how to do the magic…

void make_save_state( const char* path )
	/* Load SPC */
	long spc_size;
	void* spc = load_file( path, &spc_size );
	error( spc_load_spc( snes_spc, spc, spc_size ) );
	free( spc );
	spc_clear_echo( snes_spc );
	/* Skip several seconds */
	error( spc_skip( snes_spc, 60 * spc_sample_rate * 2) );
	/* Save state to file */
		static unsigned char state [spc_file_size]; /* buffer large enough for data */
		unsigned char* out = state;
// then write header here
		spc_save_spc( snes_spc, out );
		write_file( "state.spc", state, spc_file_size );
	record_wav( "first.wav", 5 );

The problem is that spc_init_header() also writes that the ID666 is not present [lie], so that will have to be set to present.. the value to say ID666 is 27 or 0x1a and that DOES get written. Then, the spc_file_t entry importance is text[212], which includes the first two bytes of which are unused. so text[2] begins the song_title etc.. and is where the… no wait, FYI spc_file_t and header_t point to the same memory structure 🙂


struct spc_file_t
char    signature [signature_size];
uint8_t has_id666;
uint8_t version;
uint8_t pcl, pch;
uint8_t a;
uint8_t x;
uint8_t y;
uint8_t psw;
uint8_t sp;
char    text [212];
uint8_t ram [0x10000];
uint8_t dsp [128];
uint8_t unused [0x40];
uint8_t ipl_rom [0x40];

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s