using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Windows.Forms;
using System.IO;
using System.Data;
using SCJMapper_V2.SC;
using SCJMapper_V2.Table;
using SCJMapper_V2.Devices;
using SCJMapper_V2.Devices.Keyboard;
using SCJMapper_V2.Devices.Mouse;
using SCJMapper_V2.Devices.Gamepad;
using SCJMapper_V2.Devices.Joystick;
using SCJMapper_V2.Devices.Options;
using System.Linq;
using System.Xml.Linq;
namespace SCJMapper_V2.Actions
{
///
/// Maintains the complete ActionMaps - something like:
///
/// // AC2
///
///
///
///
/// ...
///
///
///
class ActionMapsCls : List
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod( ).DeclaringType );
#region Static Part of ActionMaps
// actionmap names to gather (do we need them to be cofigurable ??)
public static string[] ActionMaps = { };
public static void LoadSupportedActionMaps( SCActionMapList aml )
{
// load actionmaps
ActionMaps = aml.ActionMaps;
}
#endregion Static Part of ActionMaps
private const string ACM_VERSION = "version=\"1\" optionsVersion=\"2\" rebindVersion=\"2\""; //AC2 the FIXED version
private string version { get; set; }
private UICustHeader m_uiCustHeader = null;
private Tuningoptions m_tuningOptions = null;
private Deviceoptions m_deviceOptions = null;
// own additions for JS mapping - should not harm..
private string[] m_js;
private string[] m_product_GUIDs;
private string[] m_instance_GUIDs;
///
/// get/set jsN assignment (use 0-based index i.e. js1 -> [0])
///
public string[] jsN
{
get { return m_js; }
}
///
/// get/set jsN product GUID assignment (use 0-based index i.e. js1GUID -> [0])
///
public string[] jsN_prodGUID
{
get { return m_product_GUIDs; }
}
///
/// get/set jsN instance GUID assignment (use 0-based index i.e. js1GUID -> [0])
///
public string[] jsN_instGUID
{
get { return m_instance_GUIDs; }
}
///
/// Clears a read but not longer known entry
///
public void Clear_jsEntry( int index )
{
m_js[index] = "";
m_instance_GUIDs[index] = "";
m_product_GUIDs[index] = "";
}
// provide access to Tuning items of the Options obj to the owner
///
/// Returns the device tuning items - the OptionTree
///
public Tuningoptions TuningOptions { get { return m_tuningOptions; } }
///
/// Returns the DeviceOptions containing the deadzones and saturation
///
public Deviceoptions DeviceOptions { get { return m_deviceOptions; } }
///
/// Copy return all ActionMaps while reassigning the JsN Tag
///
/// The JsN reassign list
/// The ActionMaps copy with reassigned input
public ActionMapsCls ReassignJsN( JsReassingList newJsList )
{
var newMaps = new ActionMapsCls( this ) {
m_uiCustHeader = (UICustHeader)this.m_uiCustHeader.Clone( ),
m_tuningOptions = (Tuningoptions)this.m_tuningOptions.Clone( ),
m_deviceOptions = (Deviceoptions)this.m_deviceOptions.Clone( )
};
foreach ( ActionMapCls am in this ) {
newMaps.Add( am.ReassignJsN( newJsList ) ); // creates the deep copy of the tree
}
// remap the tuning options
newMaps.m_tuningOptions.ReassignJsN( newJsList );
return newMaps;
}
///
/// cTor: private copy constructor
///
///
private ActionMapsCls( ActionMapsCls other )
{
this.version = other.version;
this.m_js = other.m_js;
this.m_instance_GUIDs = other.m_instance_GUIDs;
this.m_product_GUIDs = other.m_product_GUIDs;
// other ref objects are not populated here
}
///
/// cTor: plain, initializes values
///
public ActionMapsCls()
{
version = ACM_VERSION;
// create the Joystick assignments
Array.Resize( ref m_js, JoystickCls.JSnum_MAX + 1 );
Array.Resize( ref m_instance_GUIDs, JoystickCls.JSnum_MAX + 1 );
Array.Resize( ref m_product_GUIDs, JoystickCls.JSnum_MAX + 1 );
for ( int i = 0; i < JoystickCls.JSnum_MAX; i++ ) {
m_js[i] = ""; m_instance_GUIDs[i] = ""; m_product_GUIDs[i] = "";
}
// create the default mapped optiontree
// CreateNewOptions( );
}
///
/// Helper to create all needed objs
///
public void CreateNewOptions()
{
// create options objs
m_uiCustHeader = new UICustHeader( );
m_tuningOptions = new Tuningoptions( );
m_deviceOptions = new Deviceoptions( );
}
///
/// Merge the given Map with this Map
/// new ones are ignored - we don't learn from XML input for the time beeing
///
///
private void Merge( ActionMapCls newAcm )
{
// do we find an actionmap like the new one in our list ?
ActionMapCls ACM = this.Find( delegate ( ActionMapCls acm ) {
return acm.MapName == newAcm.MapName;
} );
if ( ACM == null ) {
; // this.Add( newAcm ); // no, add new
}
else {
ACM.Merge( newAcm ); // yes, merge it
}
}
///
/// Converts all maps into a DataSet
///
/// The dataset to populate
public void ToDataSet( DS_ActionMaps dsa )
{
dsa.Clear( );
if ( dsa.HasChanges( ) ) dsa.T_ActionMap.AcceptChanges( );
int AMcount = 1;
foreach ( ActionMapCls am in this ) {
DS_ActionMaps.T_ActionMapRow amr = dsa.T_ActionMap.NewT_ActionMapRow( );
string amShown = DS_ActionMap.ActionMapShown( SCUiText.Instance.Text( am.MapName), AMcount++ );
amr.ID_ActionMap = amShown;
dsa.T_ActionMap.AddT_ActionMapRow( amr );
foreach ( ActionCls ac in am ) {
int ilIndex = 0;
while ( ac.InputList.Count > ilIndex ) {
DS_ActionMaps.T_ActionRow ar = dsa.T_Action.NewT_ActionRow( );
ar.ID_Action = DS_ActionMap.ActionID( am.MapName, ac.Key, ac.InputList[ilIndex].NodeIndex ); // make a unique key
ar.AddBind = ( ilIndex > 0 ); // all but the first are addbinds
ar.REF_ActionMap = amShown;
ar.ActionName = ac.ActionName;
ar.ActionText = SCUiText.Instance.Text( ac.ActionName );
ar.Device = ac.Device;
ar.Def_Binding = ac.DefBinding;
ar.Def_Modifier = ac.DefActivationMode.Name;
ar.Usr_Binding = ac.InputList[ilIndex].DevInput;
ar.Usr_Modifier = ac.InputList[ilIndex].ActivationMode.Name;
ar.Disabled = DeviceCls.IsDisabledInput( ac.InputList[ilIndex].Input );
dsa.T_Action.AddT_ActionRow( ar );
ilIndex++;
}
}// each Action
}// each ActionMap
// finally
if ( dsa.HasChanges( ) )
dsa.AcceptChanges( );
}
///
/// Update the dataset
///
/// The dataset
/// The actionID to update from
public void UpdateDataSet( DS_ActionMaps dsa, string actionID )
{
foreach ( ActionMapCls am in this ) {
DS_ActionMaps.T_ActionMapRow amr = dsa.T_ActionMap.NewT_ActionMapRow( );
foreach ( ActionCls ac in am ) {
int ilIndex = 0;
while ( ac.InputList.Count > ilIndex ) {
if ( actionID == DS_ActionMap.ActionID( am.MapName, ac.Key, ac.InputList[ilIndex].NodeIndex ) ) {
DS_ActionMaps.T_ActionRow ar = dsa.T_Action.FindByID_Action( actionID );
ar.Usr_Binding = ac.InputList[ilIndex].DevInput;
ar.Usr_Modifier = ac.InputList[ilIndex].ActivationMode.Name;
ar.Disabled = DeviceCls.IsDisabledInput( ac.InputList[ilIndex].Input );
ar.AcceptChanges( );
return;
}
ilIndex++;
}
}// each Action
}// each ActionMap
}
///
/// Dump the ActionMaps as partial XML nicely formatted
///
/// the action as XML fragment
public string toXML( string fileName )
{
log.Debug( "ActionMapsCls.toXML - Entry" );
AppSettings appSettings = AppSettings.Instance; // shortcut only
// *** HEADER
// handle the versioning of the actionmaps
// AC2 do not longer support ignoreversion... enter the new fixed header
string r = "\n" );
// *** CustomisationUIHeader
// and dump the option contents - prepare with new data
m_uiCustHeader.ClearInstances( );
UICustHeader.DevRec dr = new UICustHeader.DevRec( );
dr.devType = KeyboardCls.DeviceClass; dr.instNo = 1; m_uiCustHeader.AddInstances( dr );
dr.devType = MouseCls.DeviceClass; dr.instNo = 1; m_uiCustHeader.AddInstances( dr );
// do we use Gamepad ??
if ( GamepadCls.RegisteredDevices > 0 ) {
dr.devType = GamepadCls.DeviceClass; dr.instNo = 1; m_uiCustHeader.AddInstances( dr );
}
// all Joysticks
for ( int i = 0; i < JoystickCls.JSnum_MAX; i++ ) {
if ( !string.IsNullOrEmpty( jsN[i] ) ) {
dr.devType = JoystickCls.DeviceClass; dr.instNo = i + 1; m_uiCustHeader.AddInstances( dr );
}
}
m_uiCustHeader.Label = fileName.Replace( SCMappings.c_MapStartsWith, "" ); // remove redundant part
r += m_uiCustHeader.toXML( ) + string.Format( "\n" );
// *** OPTIONS
foreach ( KeyValuePair kv in m_tuningOptions ) {
if ( kv.Value.Count > 0 ) r += kv.Value.toXML( ) + string.Format( "\n" );
}
// *** DEVICE OPTIONS
if ( m_deviceOptions.Count > 0 ) r += m_deviceOptions.toXML( ) + string.Format( "\n" );
// *** MODIFIERS
if ( SC.Modifiers.Instance.UserCount > 0 ) r += SC.Modifiers.Instance.ToXML( ) + string.Format( "\n" );
// *** ACTION MAPS
foreach ( ActionMapCls amc in this ) {
r += string.Format( "{0}\n", amc.toXML( ) );
}
r += string.Format( "\n" );
// tidy up..
return r.Replace( string.Format( "\n\n" ), string.Format( "\n" ) );
}
///
/// Read an ActionMaps from XML - do some sanity check
///
/// the XML action fragment
/// True if an action was decoded
public bool fromXML( string xml )
{
log.Debug( "ActionMapsCls.fromXML - Entry" );
var settings = new XmlReaderSettings {
ConformanceLevel = ConformanceLevel.Fragment,
IgnoreWhitespace = true,
IgnoreComments = true
};
using ( var reader = XmlReader.Create( new StringReader( xml ), settings ) ) {
reader.MoveToContent( );
if ( reader.EOF ) return false;
if ( XNode.ReadFrom( reader ) is XElement el ) {
// read the header element
bool jsMapFound = false;
if ( el.Name.LocalName == "ActionMaps" ) {
if ( el.HasAttributes ) {
version = (string)el.Attribute( "version" );
if ( version == "0" ) version = ACM_VERSION; // update from legacy to actual version
// get the joystick mapping if there is one
for ( int i = 0; i < JoystickCls.JSnum_MAX; i++ ) {
jsN[i] = (string)el.Attribute( $"js{i + 1}" );
jsN_instGUID[i] = (string)el.Attribute( $"js{i + 1}G" );
if ( !string.IsNullOrEmpty( jsN_instGUID[i] ) ) jsMapFound = true; // any found will do
}
}
else {
return false;
}
}
// now handle the js assignment from the map
// Reset with the found mapping
if (jsMapFound) DeviceInst.JoystickListRef.ResetJsNAssignment( jsN_instGUID );
// Only now create the default optiontree for this map, containing included joysticks and the gamepad
CreateNewOptions( );
// now read the CIG content of the map
IEnumerable actionmaps = from x in el.Elements( )
where ( x.Name == "actionmap" )
select x;
foreach ( XElement actionmap in actionmaps ) {
ActionMapCls acm = new ActionMapCls( );
if ( acm.fromXML( actionmap ) ) {
this.Merge( acm ); // merge list
}
}
IEnumerable custHeaders = from x in el.Elements( )
where ( x.Name == UICustHeader.XmlName )
select x;
foreach ( XElement custHeader in custHeaders ) {
m_uiCustHeader.fromXML( custHeader );
}
IEnumerable deviceOptions = from x in el.Elements( )
where ( x.Name == "deviceoptions" )
select x;
foreach ( XElement deviceOption in deviceOptions ) {
m_deviceOptions.fromXML( deviceOption );
}
IEnumerable options = from x in el.Elements( )
where ( x.Name == "options" )
select x;
foreach ( XElement option in options ) {
m_tuningOptions.fromXML( option );
}
IEnumerable modifiers = from x in el.Elements( )
where ( x.Name == "modifiers" )
select x;
foreach ( XElement modifier in modifiers ) {
SC.Modifiers.Instance.FromXML( modifier );
}
}
}
return true;
}
}
}