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; } } }