Gopher - the demo player

1 - How does it work?

Gopher works a bit like a conventional music tracker:  it "plays" a file row
by row except instead of mixing samples it calls functions with interpolated
parameters corresponding to the current row.


2 - Why do I care?

You can write your effects once and then [re-]use them in different parts
of your demo, in different demos or share them with other sceners.

Another advantage is that you'll never have to write synchronization
code again.  I simply got sick of breaking up my demo in a whole bunch
of small "while" loops and hardcoding the sequence and parameters of
my effects.

Portability:  the player is written using SDL so your demo should be
portable everywhere SDL is supported.  Portability also means if a
member of your demogroup only uses Linux and your other coders use
Windows, he'll still be able to contribute.

Lastly:  you'll never have to recompile anything to change how your
demo behaves, what texture it should use, when some effect starts and
eventually, a demo editor that looks and acts somewhat like Adobe Premiere
can be written to make demo editing a visual process with realtime previews
and a tracker-like interface.


3 - How do I use it?

This assumes your development computer is configured to use SDL and that you
already know how to create DLLs and to operate a C++ compiler.

-> Create a Gopher-compatible library with all your effects in it (Section 4)
	- Create a DLL
	- Create and export Gopher functions
	- Create and export your effects
	- Make your library externally configurable

-> Create data your DLL needs (Section 5)

-> Create a Gopher Demo File (Section 6)
	- Create a Library section
	- Create a Demo section
	- Create a Summary section
	- Create a Settings tag
	- Create Track sections


4 - Creating a Gopher-compatible library

4.1 - Create a DLL
	- with your favorite C++ development tools, create a DLL
	- have your DLL like to sdl.lib
	- make sure the debug version uses the "Debug Multithreaded DLL" version of
	the run-time library and the release version uses the "Multithreaded DLL"
	version (in VC6:  Project/Settings/C++/Code Generation/Use run-time library)
	- perform the necessary steps to have your compiler understand you wish to
	export functions by name (for run-time linking)
		- in VC, this is done by creating a .DEF file with the same name as the
		main .cpp file and the resulting .dll file and adding a line that reads
		"EXPORTS" at the top.  Add this file to your project.

4.2 - Create and export Gopher functions
	- in order to create a "Gopher plugin", you need to implement these 3
	functions:

BOOL Gopher_Init ( SDL_Surface ** outputSurfaces, SDL_Surface ** inputSurfaces );
BOOL Gopher_BeginNewFrame ( long currentOutputSurface );
BOOL Gopher_Quit ( void );

	- Gopher_Init is called once per demo by Gopher to give you a chance to
	create lookup tables, load data and otherwise prepare your effects.
	You get an array of outputSurfaces that you must keep a copy of because
	for every frame, one of them is going to be what gets put on the screen.
	You also get an array of inputSurfaces, which is the size you request
	in your Gopher Demo File and you can load whatever images you wish in
	there as this is the main mechanism you will use to pass the result of
	one effect on to the next.  (except for pure OpenGL demos)

	- Gopher_BeginNewFrame will be called by Gopher once per frame to tell you
	some of your functions are about to be called and that if you need to write
	anything to the buffer that will be thrown on the screen, you should use
	outputSurfaces[currentOutputSurface]

	- Gopher_Quit is the opposite of Gopher_Init and is called when the demo is
	over (either time has run out or the user hit ESC, Alt+F4, etc..) to give
	you a chance to uninitialize, close files, etc...

	- perform the necessary steps to have your compiler export these three
	functions
		- in VC, this is done by appending the lines
	Gopher_Init
	Gopher_BeginNewFrame
	Gopher_Quit
		...to the .DEF file you created in section 4.1


4.3 - Create and export your effects
	- unless your library simply loads images at Gopher_Init (like Mystic), you
	will want to create some effects that Gopher will execute

	- your effects must be contained to single functions that will take as input
	parameters what input surface(s) to use (if any), what values to use to
	drive the effects themselves and what surface(s) you would like to output
	to (either the screen backbuffer itself, or one of the "input" surfaces so
	that the output of one effect can be the input of the next)

	- your functions have to return a BOOL and all their parameters must be
	"signed long"

	- for example:

BOOL Plasma ( signed long wave1, signed long wave2,
signed long wave3, signed long outputSurfaceIndex );

...this function takes three parameters that determine what the effect looks
like (wave1-3) and one parameter that determines where the effect is rendered
(outputSurfaceIndex)

I use the convention that if one of my effects receives '-1' as
outputSurfaceIndex, the effect is rendered to one of the outputSurfaces
(specifically, outputSurfaces[currentOutputSurface]) otherwise, the effect
is rendered in inputSurfaces[outputSurfaceIndex]

This convention isn't necessary if you would rather have one of your functions
take an index into inputSurfaces as a parameter and blit it to the current
output surface, but this is an extra operation...

	- perform the necessary steps to have your compiler export all the functions
	you would like to use in a demo
		- in VC, this is done by appending the names of the functions
	Plasma
		...to the .DEF file you created in section 4.1


4.4 - Make your library externally configurable
	- Gopher is designed so that when it starts calling effect functions in your
	library, they execute very quickly and do exactly what they are told

	- it may happen that a particular frame took longer to render than an
	another one, in this case that frame might have been the last one to call
	a specific effect function.  This means you have no guarantee a given
	set of functions at a given millisecond in your track will ever get
	executed, because it is possible an execution prior to that took longer
	than expected (the system has to keep up:  it was deemed more important to
	have it keep up and be synchronized than to produce perfect, fixed framerate
	output.  This type of output could be achieved by an offline version of
	Gopher that outputs a video file or something..)

	- effect functions are therefore not recommended to keep ANY state for
	future calls nor to expect to be called at all during a demo, in case some
	user cranks up the detail level on their computer and the demo runs at 1
	frame per second

	- effect functions should have all their data initialized in Gopher_Init,
	which would preferentially read from an external file what special
	parameters should be set, so that changing what texture is loaded or
	what image maps are used is something that can be done without recompiling
	the library, hence extending its re-use and making it simpler for the person
	putting the demo together (especially if an editor is used in the future)



5.  Create data your DLL needs
	- Hopefully the suggestions in Section 4.4 were followed and your DLL is
	now externally configurable
	- Create some sample data so that you can test your code


6.  Create a Gopher Demo File
	- start a text file (ie: demo.xml) and insert the following text:

<?xml version="1.0" encoding="windows-1252"?>
<!DOCTYPE GopherDemoFile SYSTEM "http://gopher.sourceforge.net/Gopher.dtd">
<GopherDemoFile>
</GopherDemoFile>


6.1 Create a Libraries section
	- insert the following lines between "GopherDemoFile" tags:

	<Libraries>
	</Libraries>

	- throw a section for each of your Gopher libraries between the "Libraries"
	tags:

		<Library FileName = "blarg.dll">
		</Library>

	- if a given library does not expose any run-time functions, you can simply
	use an empty element and skip to section 6.2.  For example:

		<Library FileName = "mystic.dll" />

	- insert a "Function" section for every run-time function your Gopher
	library supports:  (only insert the function under the corresponding Library
	section!)

			<Function Name = "Plasma">
				<Parameter>
					wave1
				</Parameter>
				<Parameter>
					wave2
				</Parameter>
				<Parameter>
					wave3
				</Parameter>
				<Parameter>
					destSurface
				</Parameter>
			</Function>

	- your file should now look somewhat like this:

<?xml version="1.0" encoding="windows-1252"?>
<!DOCTYPE GopherDemoFile SYSTEM "http://gopher.sourceforge.net/Gopher.dtd">
<GopherDemoFile>
	<Libraries>
		<Library FileName = "blarg.dll">
			<Function Name = "Plasma">
				<Parameter>
					wave1
				</Parameter>
				<Parameter>
					wave2
				</Parameter>
				<Parameter>
					wave3
				</Parameter>
				<Parameter>
					destSurface
				</Parameter>
			</Function>
			<Function Name = "Blend">
				<Parameter>
					sourceSurface
				</Parameter>
				<Parameter>
					blendAmount
				</Parameter>
				<Parameter>
					destSurface
				</Parameter>
			</Function>
		</Library>
		<Library FileName = "mystic.dll" />
	</Libraries>
</GopherDemoFile>


6.2 Create a Demo section
	- insert the following lines between "/Libraries" and "/GopherDemoFile"
	tags:

	<Demo>
	</Demo>


6.3 Create a Summary section
	- insert the following lines between "Demo" tags:

		<Summary>
		</Summary>

	- insert at least one "Author" tag between "Summary" tags:

			<Author>
				Purple Motion
			</Author>

	- the "Group" tag is optional, but follows "Author" tags:

			<Group>
				Future Crew
			</Group>

	- the "ExtraInfo" tag is also optional, put after "Group" (or "Author"
	if "Group" isn't specified):

			<ExtraInfo>
				1st place at Assembly 1993
			</ExtraInfo>


	- you should have something similar to this:

		<Summary>
			<Author>
				Purple Motion
			</Author>
			<Author>
				Psi
			</Author>
			<Group>
				Future Crew
			</Group>
			<ExtraInfo>
				Unreal:  1st place at Assembly 1992  Thanks to the organizers
				for a great party.  Visit us on the web www.futurecrew.com
			</ExtraInfo>
		</Summary>


6.4 Create a Settings tag
	- only one of these are necessary (and allowed) and it goes after "Summary":

		<Settings RunningTime = "56500"
                UseOpenGL = "false"
                NumberOfInputSurfaces = "32"
                InputMusicFile = "data\omf-ar0.s3m" />

	- RunningTime is the number of milliseconds your demo will run

	- set UseOpenGL to "true" if you would like the player be initialized for
	OpenGL calls

	- set NumberOfInputSurfaces to be the number of buffers you need to read and
	write to with your effects.  Remember that each buffer consumes a fixed
	amount of RAM, which is:  w * h * bpp  (width and height in pixels, and
	number of bytes per pixel)  For example, 640x480x4 is 1 228 800 bytes per
	surface.  1024x768x4 is 3 145 728 bytes per surface.  32 surfaces would
	consume, in this case, 100 663 296 bytes of RAM!

	- the player will probably eventually determine how much RAM is required
	and give the option to the user to reduce their resolution if they don't
	have enough physical RAM

	- set InputMusicFile to point to a MOD file (MOD, S3M, XM, IT, etc..)
	Other music file types (ie: MIDI, OGG, MP3) will probably be supported



6.5 Create Track sections
	- for each Track you'll want to use (you need at least one) insert "Track"
	tags after the "Settings" tag:

		<Track>
		</Track>

	- an important thing to know:  for every frame, tracks are executed from
	last to first.  This makes it easier to supply a "blit" function throughtout
	the demo as you only need to stick it in the first track and have one event
	execute throughout the demo as opposed to trying to use whatever track
	is free for a given event.  (trust me on this one, I tried it the other way
	and it made the Gopher demo file very ugly and hard to use)

	- just think of it like a music tracker and at the low level, channels are
	being played from right to left (ie:  4, 3, 2, 1)

	- a track is composed of one or more "Events".  An Event is composed of
	fixed start and end points, a function name and a list of what the
	parameters should be equal to at the start of the event and a list of what
	the parameters should be equal to at the end of the event

	- since more than one library may expose a function with the same name,
	you need to specify the file name of the library that contains the function
	you want to use.

	- StartTime and EndTime are in milliseconds.  Be careful not to overlap
	times for events within the same track.  (times can overlap for events in
	different tracks)

			<Event LibraryFileName = "blarg.dll" FunctionName = "Plasma"
					 StartTime = "0" EndTime = "14000">
			</Event>

	- supply as many "StartParameter" tags as the function needs parameters:

				<StartParameter>
					0
				</StartParameter>
				<StartParameter>
					10000
				</StartParameter>
				<StartParameter>
					250
				</StartParameter>
				<StartParameter>
					31
				</StartParameter>

	- supply as many "EndParameter" tags as the function needs parameters:

				<EndParameter>
					1000
				</EndParameter>
				<EndParameter>
					0
				</EndParameter>
				<EndParameter>
					750
				</EndParameter>
				<EndParameter>
					31
				</EndParameter>

	- in this example, the "Plasma" function in the library "blarg.dll" will be
	executed for every frame between 0 and 14000 miliseconds (14 seconds), with
	the first parameter going from 0 to 1000, the second from 1000 to 0, the
	third from 250 to 750 and the last staying put at 31 for the entire time

			<Event LibraryFileName = "blarg.dll" FunctionName = "Plasma"
					 StartTime = "0" EndTime = "14000">
				<StartParameter>
					0
				</StartParameter>
				<StartParameter>
					10000
				</StartParameter>
				<StartParameter>
					250
				</StartParameter>
				<StartParameter>
					31
				</StartParameter>
				<EndParameter>
					1000
				</EndParameter>
				<EndParameter>
					0
				</EndParameter>
				<EndParameter>
					750
				</EndParameter>
				<EndParameter>
					31
				</EndParameter>
			</Event>


That's it!  Look at the supplied example (corel.xml) for what a simple,
complete demo looks like and how files are typically organized for a demo.