-------------------------------------------------------
          Altus Plugin Development Guide v1.0
                      by DarkNeo
-------------------------------------------------------

------------------------
 Setting up the Project
------------------------

Create a new Windows Class Library project
Add a reference to the Altus "Shared.dll" (this contains the plugin class, as well as a useful static Helper class, and the ErrorLog class)
Add "System.IO", "Altus.Shared", and "Altus.Plugins" to your includes
Add a PluginDescription attribute
Add an inheritence from Plugin
Add a constructor

Here's an example of the last three steps (taken from the XIP Plugin):

	[PluginDescription("XIP Plugin", "Xbox ZIP File (*.xip)|*.xip", 0x30504958)]
	public class XIPPlugin : Plugin
	{

	        public XIPPlugin(RequestLoad requestLoad)
        	    : base(requestLoad)
        	{
        	}
	}

There's a few things that need to be pointed out here. First I want to say remember to make the class public, during development of every plugin I've always forgotten, then got really frustrated when it wouldn't load! The constructor takes a "RequestLoad" delegate, this is for plugins that need their UI controls to cause a new file to be loaded. Just call the requestLoad() with the filename you want to load.

Now for the structure of the Plugin description. There's just three simple parts:
-Name
-File type filter
-Magic

The name is self explanatory, the file type filter is the type of files the plugin will handle, in the normal format "Descriptive text|*.extension". To add more extensions just add a "|" then repeat the structure. The magic is for file formats that have a four byte magic value at the start. In the example this is "XIP0".

----------------
 Critical Stuff
----------------

For a plugin to function properly it must do three things:

-Override the Load() method
-Provide a directory/file structure
-Provide a stream to read files from

The Load() method is called when a file is loaded into the plugin. This method *must* create the "rootDir" Hashtable (which holds the entire directory/file structure), and *must* create the "pluginStream" IO Stream.

--------------------------
 Directory/File Structure
--------------------------

The directory structure used in Altus uses Hashtables to represent directories, with GenericFileDescriptor's for files. With a structure like this:

rootDir (Hashtable)
|- Key: "gamedata" (Hashtable)
||- Key: "models" (Hashtable)
|||- Key: "character1.mod" (GenericFileDescriptor)

Directories and files have their name as their key, *not* the full path. The actual GenericFileDescriptor, however, does have the entire path as the filename. The properties of a GenericFileDescriptor are as follows:

-Filename
-FileSize
-Offset

There is a "rootDirName" field to control the name of the topmost directory. Usually it's best to just set it like this:
	rootDirName = Path.GetFileName(filename);

To make things easier, a file structure read as a flat array of GenericFileDescriptor's can be passed to ExpandFlatTable(), which will return a Hashtable that can be assigned to rootDir. ExpandFlatTable() will create the entire directory structure from the filenames.

---------------
 Plugin Stream
---------------

A plugin must contain a usable Plugin Stream. This needs to be a readable, decompressed, stream (for compressed files it's best to create a new stream class, then extract the right bits in the Read() method.)

Files are read by using their offset and size properties. So once the stream is set up, you don't have to worry about extracting.

------------------
 Advanced Plugins
------------------

Plugins of course can be more advanced. The most common methods you'll want to override are:

Add(string filename) - Called when a user tries to add a file into the container
AddDirectory(string directory) - Called when a user tries to add a directory into the container
Remove(GenericFileDescriptor file) - Called when a user tries to remove a file from the container
Remove(Hashtable directory) - Called when a user tries to remove a directory from the container
Save(string filename) - Called when a user wants to save changes (note: it's best practice to store the desired changes of Add() and Remove() but not change the file until Save() is called)
Close() - Called when the user is closing the container file, override this to close any open streams you may have open (pluginStream will automatically get closed by Close() unless you override, in which case either do it yourself or call base.Close())
GetToolStripItems() and GetMenuItems() - Provide UI elements to be shown on the form

If you add support for adding/removing files, you need to override the SupportsAddition, and SupportsRemoval properties, and set them to true. If you override the add/remove directory methods you need to override the SupportsDirectories as well.

Plugins can even get more advanced than this. If for some reason the generic extraction doesn't work for your plugin, you can override the ExtractFile(GenericFileDescriptor file) and ExtractDirectory(Hashtable directory, bool recurse).
I won't go into how the event system works for this, since I don't think it should be necessary for any plugins to need to override these. However if you do, PM me and I'll write a guide for it (or you could just look at the existing code :P)

-----------------
 Troubleshooting
-----------------

Problem: Plugin doesn't load (not listed in file type filter, buttons don't show up, etc)
Solution: First thing I check is that you haven't made a PICNIC error (Problem In Chair, Not In Code). Check the little things, like making the class public, that the DLL is in the right place, and that the plugin description is correct.

Problem: A DLL I've referenced to my plugin can't be found
Solution: Copy all extra DLLs into the main program folder.

------------
 Shared.dll
------------

Helper Class:

The Helper class contains many useful methods including endian swap, character based string reading (like ReadToNull()), check the meta data of the DLL for usage information.

ErrorLog Class:

You shouldn't need to access this class directly unless you want to provide warnings. If you encounter an error during loading, just throw an exception and it will be caught and logged. ErrorLog.Log() can be called with either an exception or a message.