You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
459 lines
18 KiB
C#
459 lines
18 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Xml;
|
|
using System.IO;
|
|
|
|
using SCJMapper_V2.Actions;
|
|
using System.Xml.Linq;
|
|
using System.Linq;
|
|
using SCJMapper_V2.Devices.Joystick;
|
|
using SCJMapper_V2.Devices.Keyboard;
|
|
using SCJMapper_V2.Devices.Mouse;
|
|
using SCJMapper_V2.Devices.Gamepad;
|
|
using SCJMapper_V2.Devices.Options;
|
|
|
|
namespace SCJMapper_V2.SC
|
|
{
|
|
/// <summary>
|
|
/// should read the default profile - may be replacing the MappingVars once
|
|
/// Reads the profile as is and creates a CSV Map (internal legacy format) for clients
|
|
/// default bindings are read as AC1 style items i.e. not containing the device
|
|
/// </summary>
|
|
class DProfileReader
|
|
{
|
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod( ).DeclaringType );
|
|
|
|
public bool ValidContent { get; set; }
|
|
|
|
// an action map and its actions
|
|
class ProfileAction
|
|
{
|
|
public string Name { get; set; } // the action name
|
|
public string UILabel { get; set; } // the action name translated
|
|
public string DevID { get; set; } // the input method K,J,X,P
|
|
private string m_defBinding = ""; // NOTE: this is AC1 style in the Profile - need to conver later when dumping out
|
|
public string DefBinding { get { return m_defBinding; } set { m_defBinding = value; } } // DONT! need to clean this one, found spaces...
|
|
|
|
private ActivationMode m_defActivationMode = ActivationMode.Default;
|
|
public ActivationMode DefActivationMode { get { return m_defActivationMode; } set { m_defActivationMode = value; } }
|
|
|
|
public string KeyName
|
|
{ get { return DevID + Name; } } // prep for TreView usage - create a key from input+name
|
|
}
|
|
|
|
|
|
class ActionMap : List<ProfileAction> // carries the action list
|
|
{
|
|
public string Name { get; set; } // the map name
|
|
public string UILabel { get; set; } // the map label
|
|
|
|
static int ContainsLoop( List<ProfileAction> list, string value )
|
|
{
|
|
for ( int i = 0; i < list.Count; i++ ) {
|
|
if ( list[i].KeyName == value ) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public new int IndexOf( ProfileAction pact )
|
|
{
|
|
return ContainsLoop( this, pact.KeyName );
|
|
}
|
|
|
|
public new bool Contains( ProfileAction pact )
|
|
{
|
|
return ( ContainsLoop( this, pact.KeyName ) >= 0 );
|
|
}
|
|
|
|
public new void Add( ProfileAction pact )
|
|
{
|
|
// only get the latest ..
|
|
if ( this.Contains( pact ) )
|
|
this.RemoveAt( IndexOf( pact ) );
|
|
|
|
base.Add( pact );
|
|
}
|
|
};
|
|
|
|
|
|
Dictionary<string, ActionMap> m_aMap = null; // key would be the actionmap name
|
|
ActionMap m_currentMap = null;
|
|
|
|
|
|
|
|
// ****************** CLASS **********************
|
|
|
|
/// <summary>
|
|
/// cTor
|
|
/// </summary>
|
|
public DProfileReader()
|
|
{
|
|
ValidContent = false; // default
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Returns the collected actionmaps as CSV (same format as MappingVars)
|
|
/// i.e. one line per actionmap where the first element is the actionmap and following are actions;defaultBinding lead by the input Key in uppercase (JKXP)
|
|
///
|
|
/// actmap;actmaplabel;action;actionlabel;defBinding;defActMode;defActMultitap;
|
|
///
|
|
/// </summary>
|
|
public string CSVMap
|
|
{
|
|
get {
|
|
log.Debug( "DProfileReader.CSVMap - Entry" );
|
|
|
|
string buf = "";
|
|
foreach ( ActionMap am in m_aMap.Values ) {
|
|
buf += am.Name + ";"+ am.UILabel + ";";
|
|
foreach ( ProfileAction a in am ) {
|
|
buf += a.KeyName + ";" + a.UILabel + ";"
|
|
+ a.DefBinding + ";"
|
|
+ a.DefActivationMode.Name + ";" + a.DefActivationMode.MultiTap.ToString( ) + ";"; // add default binding + activation mode to the CSV
|
|
}
|
|
buf += string.Format( "\n" );
|
|
}
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// INPUT processing of the xml nodes
|
|
|
|
private void ActModeInput( ref ProfileAction pa, XElement xml )
|
|
{
|
|
string actModeName = (string)xml.Attribute( "ActivationMode" );
|
|
string multiTap = (string)xml.Attribute( "multiTap" );
|
|
if ( string.IsNullOrEmpty( actModeName ) ) {
|
|
actModeName = pa.DefActivationMode.Name; // from store
|
|
}
|
|
if ( string.IsNullOrEmpty( multiTap ) ) {
|
|
multiTap = ActivationModes.Instance.MultiTapFor( actModeName ).ToString( ); // given by the already collected items
|
|
}
|
|
pa.DefActivationMode = new ActivationMode( actModeName, int.Parse( multiTap ) ); // renew
|
|
}
|
|
|
|
private void JInput( ref ProfileAction pa, XElement xml, string input )
|
|
{
|
|
ActModeInput( ref pa, xml );
|
|
if ( !string.IsNullOrEmpty( input ) ) {
|
|
pa.DefBinding = input;
|
|
if ( pa.DefBinding == " " )
|
|
pa.DefBinding = JoystickCls.DisabledInput;
|
|
else if ( !string.IsNullOrEmpty( pa.DefBinding ) )
|
|
pa.DefBinding = "js1_" + pa.DefBinding; // extend with device for mapping use
|
|
}
|
|
}
|
|
|
|
private void KInput( ref ProfileAction pa, XElement xml, string input )
|
|
{
|
|
ActModeInput( ref pa, xml );
|
|
if ( !string.IsNullOrEmpty( input ) ) {
|
|
pa.DefBinding = input;
|
|
if ( pa.DefBinding == " " )
|
|
pa.DefBinding = KeyboardCls.DisabledInput;
|
|
else if ( !string.IsNullOrEmpty( pa.DefBinding ) )
|
|
pa.DefBinding = "kb1_" + pa.DefBinding; // extend with device for mapping use
|
|
}
|
|
}
|
|
|
|
private void MInput( ref ProfileAction pa, XElement xml, string input )
|
|
{
|
|
ActModeInput( ref pa, xml );
|
|
if ( !string.IsNullOrEmpty( input ) ) {
|
|
pa.DefBinding = input;
|
|
if ( pa.DefBinding == " " )
|
|
pa.DefBinding = MouseCls.DisabledInput;
|
|
else if ( !string.IsNullOrEmpty( pa.DefBinding ) )
|
|
pa.DefBinding = "mo1_" + pa.DefBinding; // extend with device for mapping use
|
|
}
|
|
}
|
|
|
|
private void XInput( ref ProfileAction pa, XElement xml, string input )
|
|
{
|
|
ActModeInput( ref pa, xml );
|
|
if ( !string.IsNullOrEmpty( input ) ) {
|
|
pa.DefBinding = input;
|
|
if ( pa.DefBinding == " " )
|
|
pa.DefBinding = GamepadCls.DisabledInput;
|
|
else if ( !string.IsNullOrEmpty( pa.DefBinding ) )
|
|
pa.DefBinding = "xi1_" + pa.DefBinding; // extend with device for mapping use
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read all from an Action XElement
|
|
/// </summary>
|
|
/// <param name="action">The action XElement</param>
|
|
/// <returns>True if successfull</returns>
|
|
private bool ReadAction( XElement action )
|
|
{
|
|
/* A variety exists here...
|
|
<action name="v_eject_cinematic" onPress="0" onHold="1" onRelease="1" keyboard="ralt+l" xboxpad="shoulderl+shoulderr+y" joystick=" " />
|
|
<action name="v_exit" onPress="1" onHold="0" onRelease="1" multiTap="1" multiTapBlock="1" pressTriggerThreshold="1.0" releaseTriggerThreshold="-1" releaseTriggerDelay="0" keyboard="f" xboxpad="y" UILabel="@ui_CIExit" UIDescription="@ui_CIExitDesc" >
|
|
<joystick ActivationMode="press" input=" " />
|
|
</action>
|
|
<action name="v_throttle_100" onPress="1" xboxpad=" " joystick=" " UILabel="@ui_CIThrottleMax" UIDescription="@ui_CIThrottleMaxDesc" >
|
|
<xboxpad multiTap="2" input="thumbl_up" />
|
|
<keyboard multiTap="2" input=" " />
|
|
</action>
|
|
|
|
<action name="v_target_deselect_component" ActivationMode="delayed_press" pressTriggerThreshold="0.75" joystick="" >
|
|
<keyboard >
|
|
<inputdata input="lbracket" />
|
|
<inputdata input="rbracket" />
|
|
</keyboard>
|
|
</action>
|
|
|
|
*/
|
|
log.Debug( "DProfileReader.ReadAction - Entry" );
|
|
// a complete actionmap arrives here
|
|
bool retVal = true;
|
|
|
|
string name = (string)action.Attribute( "name" );
|
|
string uiLabel = (string)action.Attribute( "UILabel" );
|
|
if ( string.IsNullOrEmpty( uiLabel ) )
|
|
uiLabel = name; // subst if not found in Action node
|
|
SCUiText.Instance.Add( name, uiLabel ); // Register item for translation
|
|
|
|
// prep all kinds
|
|
var jAC = new ProfileAction( ) { Name = name, UILabel = uiLabel, DevID = Act.DevTag( JoystickCls.DeviceClass ) };
|
|
var kAC = new ProfileAction( ) { Name = name, UILabel = uiLabel, DevID = Act.DevTag( KeyboardCls.DeviceClass ) };
|
|
var mAC = new ProfileAction( ) { Name = name, UILabel = uiLabel, DevID = Act.DevTag( MouseCls.DeviceClass ) };
|
|
var xAC = new ProfileAction( ) { Name = name, UILabel = uiLabel, DevID = Act.DevTag( GamepadCls.DeviceClass ) };
|
|
|
|
// process element items
|
|
JInput( ref jAC, action, (string)action.Attribute( JoystickCls.DeviceClass ) );
|
|
KInput( ref kAC, action, (string)action.Attribute( KeyboardCls.DeviceClass ) );
|
|
MInput( ref mAC, action, (string)action.Attribute( MouseCls.DeviceClass ) );
|
|
XInput( ref xAC, action, (string)action.Attribute( GamepadCls.DeviceClass ) );
|
|
XInput( ref xAC, action, (string)action.Attribute( GamepadCls.DeviceClass_3_5 ) );// Handle either or Alpha 3.5+
|
|
|
|
// then nested ones - they may or may not exist from the initial scan
|
|
foreach ( XElement l1action in action.Elements( ) ) {
|
|
// comes with the name of the device class
|
|
switch ( l1action.Name.LocalName ) {
|
|
case JoystickCls.DeviceClass: {
|
|
JInput( ref jAC, l1action, (string)l1action.Attribute( "input" ) ); // may have attributed ones
|
|
foreach ( XElement l2action in l1action.Elements( ) ) {
|
|
if ( l2action.Name.LocalName == "inputdata" ) {
|
|
JInput( ref jAC, l2action, (string)l2action.Attribute( "input" ) ); // or in the inputdata nesting
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case KeyboardCls.DeviceClass: {
|
|
KInput( ref kAC, l1action, (string)l1action.Attribute( "input" ) ); // may have attributed ones
|
|
foreach ( XElement l2action in l1action.Elements( ) ) {
|
|
if ( l2action.Name.LocalName == "inputdata" ) {
|
|
KInput( ref kAC, l2action, (string)l2action.Attribute( "input" ) ); // or in the inputdata nesting
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MouseCls.DeviceClass: {
|
|
MInput( ref mAC, l1action, (string)l1action.Attribute( "input" ) ); // may have attributed ones
|
|
foreach ( XElement l2action in l1action.Elements( ) ) {
|
|
if ( l2action.Name.LocalName == "inputdata" ) {
|
|
MInput( ref mAC, l2action, (string)l2action.Attribute( "input" ) ); // or in the inputdata nesting
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GamepadCls.DeviceClass: {
|
|
XInput( ref xAC, l1action, (string)l1action.Attribute( "input" ) ); // may have attributed ones
|
|
foreach ( XElement l2action in l1action.Elements( ) ) {
|
|
if ( l2action.Name.LocalName == "inputdata" ) {
|
|
XInput( ref xAC, l2action, (string)l2action.Attribute( "input" ) ); // or in the inputdata nesting
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
if ( !string.IsNullOrEmpty( jAC.DefBinding ) ) m_currentMap.Add( jAC ); // finally add it to the current map if it was bound
|
|
if ( !string.IsNullOrEmpty( kAC.DefBinding ) ) m_currentMap.Add( kAC ); // finally add it to the current map if it was bound
|
|
if ( !string.IsNullOrEmpty( mAC.DefBinding ) ) m_currentMap.Add( mAC ); // finally add it to the current map if it was bound
|
|
if ( !string.IsNullOrEmpty( xAC.DefBinding ) ) m_currentMap.Add( xAC ); // finally add it to the current map if it was bound
|
|
|
|
return retVal;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Read all from an Actionmap XElement
|
|
/// </summary>
|
|
/// <param name="actionmap">The Actionmap XElement</param>
|
|
/// <returns>True if successfull</returns>
|
|
private bool ReadActionmap( XElement actionmap )
|
|
{
|
|
log.Debug( "DProfileReader.ReadActionmap - Entry" );
|
|
// a complete actionmap arrives here
|
|
bool retVal = true;
|
|
|
|
// check for a valid one
|
|
string mapName = (string)actionmap.Attribute( "name" ); // mandatory
|
|
string uiLabel = (string)actionmap.Attribute( "UILabel" );
|
|
if ( string.IsNullOrEmpty( uiLabel ) )
|
|
uiLabel = mapName; // subst if not found in Action node
|
|
SCUiText.Instance.Add( mapName, uiLabel ); // Register item for translation
|
|
|
|
string item = Array.Find( ActionMapsCls.ActionMaps, delegate ( string sstr ) { return sstr == mapName; } );
|
|
if ( !string.IsNullOrEmpty( item ) ) {
|
|
// finally.... it is a valid actionmap
|
|
m_currentMap = new ActionMap { Name = mapName, UILabel = uiLabel };
|
|
if ( !m_aMap.ContainsKey( mapName ) ) { //20170325 - fix multiple map names - don't add the second, third etc. (CIG bug..)
|
|
m_aMap.Add( mapName, m_currentMap ); // add to our inventory
|
|
IEnumerable<XElement> actions = from x in actionmap.Elements( )
|
|
where ( x.Name == "action" )
|
|
select x;
|
|
foreach ( XElement action in actions ) {
|
|
// one action
|
|
retVal &= ReadAction( action );
|
|
}
|
|
}
|
|
else {
|
|
log.DebugFormat( "ReadActionmap: IGNORED duplicate map with name: {0}", mapName );
|
|
}
|
|
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Read all from ActivationMode XElement
|
|
/// </summary>
|
|
/// <param name="actmodes">The Activatiomodes XElement</param>
|
|
/// <returns>True if successfull</returns>
|
|
private bool ReadActivationModes( XElement actmodes )
|
|
{
|
|
/*
|
|
//<ActivationModes >
|
|
// <ActivationMode name="tap" onPress="0" onHold="0" onRelease="1" multiTap="1" multiTapBlock="1" pressTriggerThreshold="-1" releaseTriggerThreshold="0.25" releaseTriggerDelay="0" />
|
|
...
|
|
//</ActivationModes>
|
|
*/
|
|
log.Debug( "DProfileReader.ReadActivationModes - Entry" );
|
|
|
|
IEnumerable<XElement> activationmodes = from x in actmodes.Elements( )
|
|
where ( x.Name == "ActivationMode" )
|
|
select x;
|
|
foreach ( XElement activationmode in activationmodes ) {
|
|
string name = (string)activationmode.Attribute( "name" );
|
|
string mTap = (string)activationmode.Attribute( "multiTap" );
|
|
if ( !string.IsNullOrEmpty( name ) ) ActivationModes.Instance.Add( new ActivationMode( name, int.Parse( mTap ) ) );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Read the defaultProfile.xml - do some sanity check
|
|
/// </summary>
|
|
/// <param name="xml">the XML action defaultProfile Content</param>
|
|
/// <returns>True if an action was decoded</returns>
|
|
public bool fromXML( string xml )
|
|
{
|
|
log.Debug( "DProfileReader.fromXML - Entry" );
|
|
|
|
// make sure we have them loaded ( refactoring to get a singleton or so...)
|
|
if ( ActionMapsCls.ActionMaps.Length == 0 ) ActionMapsCls.LoadSupportedActionMaps( ActionMapList( xml ) );
|
|
|
|
// read the content of the xml
|
|
XmlReaderSettings settings = new XmlReaderSettings {
|
|
ConformanceLevel = ConformanceLevel.Fragment,
|
|
IgnoreWhitespace = true,
|
|
IgnoreComments = true
|
|
};
|
|
|
|
using ( XmlReader reader = XmlReader.Create( new StringReader( xml ), settings ) ) {
|
|
m_aMap = new Dictionary<string, ActionMap>( );
|
|
// init the activation modes singleton
|
|
ActivationModes.Instance.Clear( );
|
|
ActivationModes.Instance.Add( ActivationMode.Default );
|
|
|
|
ValidContent = true; // init
|
|
|
|
reader.MoveToContent( );
|
|
if ( XNode.ReadFrom( reader ) is XElement el ) {
|
|
|
|
IEnumerable<XElement> activationModes = from x in el.Elements( )
|
|
where ( x.Name == "ActivationModes" )
|
|
select x;
|
|
foreach ( XElement activationMode in activationModes ) {
|
|
ValidContent &= ReadActivationModes( activationMode );
|
|
}
|
|
|
|
|
|
Modifiers.Instance.Clear( );
|
|
IEnumerable<XElement> modifiers = from x in el.Elements( )
|
|
where ( x.Name == "modifiers" )
|
|
select x;
|
|
foreach ( XElement modifier in modifiers ) {
|
|
ValidContent &= Modifiers.Instance.FromXML( modifier, true );
|
|
}
|
|
|
|
OptionTree.InitOptionReader( );
|
|
IEnumerable<XElement> optiontrees = from x in el.Elements( )
|
|
where ( x.Name == "optiontree" )
|
|
select x;
|
|
foreach ( XElement optiontree in optiontrees ) {
|
|
ValidContent &= OptionTree.fromProfileXML( optiontree );
|
|
}
|
|
|
|
IEnumerable<XElement> actionmaps = from x in el.Elements( )
|
|
where ( x.Name == "actionmap" )
|
|
select x;
|
|
foreach ( XElement actionmap in actionmaps ) {
|
|
ValidContent &= ReadActionmap( actionmap );
|
|
}
|
|
|
|
}
|
|
}
|
|
return ValidContent;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the acionmals contained in the profile xml string
|
|
/// </summary>
|
|
/// <param name="xml">A default profile XML string</param>
|
|
/// <returns>A filled SCActionMapList object</returns>
|
|
public SCActionMapList ActionMapList( string xml )
|
|
{
|
|
log.Debug( "DProfileReader.ActionMapList - Entry" );
|
|
|
|
SCActionMapList aml = new SCActionMapList( );
|
|
XmlReaderSettings settings = new XmlReaderSettings {
|
|
ConformanceLevel = ConformanceLevel.Fragment,
|
|
IgnoreWhitespace = true,
|
|
IgnoreComments = true
|
|
};
|
|
using ( XmlReader reader = XmlReader.Create( new StringReader( xml ), settings ) ) {
|
|
reader.MoveToContent( );
|
|
if ( XNode.ReadFrom( reader ) is XElement el ) {
|
|
IEnumerable<XElement> actionmaps = from x in el.Elements( )
|
|
where ( x.Name == "actionmap" )
|
|
select x;
|
|
foreach ( XElement actionmap in actionmaps ) {
|
|
aml.AddActionMap( (string)actionmap.Attribute( "name" ) );
|
|
}
|
|
}
|
|
}
|
|
return aml;
|
|
}
|
|
|
|
}
|
|
}
|