diff --git a/CloneableItems.cs b/Common/CloneableItems.cs similarity index 100% rename from CloneableItems.cs rename to Common/CloneableItems.cs diff --git a/actions/MyColors.cs b/Common/MyColors.cs similarity index 98% rename from actions/MyColors.cs rename to Common/MyColors.cs index 03d6df7..c518215 100644 --- a/actions/MyColors.cs +++ b/Common/MyColors.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Text; using System.Drawing; -namespace SCJMapper_V2 +namespace SCJMapper_V2.Common { /// /// Provide the colors used diff --git a/MySounds.cs b/Common/MySounds.cs similarity index 96% rename from MySounds.cs rename to Common/MySounds.cs index bc287a7..50d1650 100644 --- a/MySounds.cs +++ b/Common/MySounds.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Media; -namespace SCJMapper_V2 +namespace SCJMapper_V2.Common { public class MySounds { diff --git a/DeviceCls.cs b/Devices/DeviceCls.cs similarity index 98% rename from DeviceCls.cs rename to Devices/DeviceCls.cs index 95847df..6ef4a65 100644 --- a/DeviceCls.cs +++ b/Devices/DeviceCls.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; -namespace SCJMapper_V2 +namespace SCJMapper_V2.Devices { /// diff --git a/DeviceInst.cs b/Devices/DeviceInst.cs similarity index 92% rename from DeviceInst.cs rename to Devices/DeviceInst.cs index de210dc..eda2af8 100644 --- a/DeviceInst.cs +++ b/Devices/DeviceInst.cs @@ -1,14 +1,14 @@ -using SCJMapper_V2.Gamepad; -using SCJMapper_V2.Joystick; -using SCJMapper_V2.Keyboard; -using SCJMapper_V2.Mouse; +using SCJMapper_V2.Devices.Gamepad; +using SCJMapper_V2.Devices.Joystick; +using SCJMapper_V2.Devices.Keyboard; +using SCJMapper_V2.Devices.Mouse; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -namespace SCJMapper_V2 +namespace SCJMapper_V2.Devices { /// /// Collects and provides the device instances throughout the application diff --git a/DeviceList.cs b/Devices/DeviceList.cs similarity index 85% rename from DeviceList.cs rename to Devices/DeviceList.cs index d372a86..3c111a2 100644 --- a/DeviceList.cs +++ b/Devices/DeviceList.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace SCJMapper_V2 +namespace SCJMapper_V2.Devices { public class DeviceList : List { diff --git a/Gamepad/GamepadCls.cs b/Devices/Gamepad/GamepadCls.cs similarity index 99% rename from Gamepad/GamepadCls.cs rename to Devices/Gamepad/GamepadCls.cs index d700fac..ea9aa7a 100644 --- a/Gamepad/GamepadCls.cs +++ b/Devices/Gamepad/GamepadCls.cs @@ -8,7 +8,9 @@ using System.Windows.Forms; using System.Text.RegularExpressions; using System.Collections.Specialized; -namespace SCJMapper_V2.Gamepad +using SCJMapper_V2.Common; + +namespace SCJMapper_V2.Devices.Gamepad { /// /// Handles one JS device as DXInput device diff --git a/Gamepad/UC_GpadPanel.Designer.cs b/Devices/Gamepad/UC_GpadPanel.Designer.cs similarity index 99% rename from Gamepad/UC_GpadPanel.Designer.cs rename to Devices/Gamepad/UC_GpadPanel.Designer.cs index 9836e24..e0913e0 100644 --- a/Gamepad/UC_GpadPanel.Designer.cs +++ b/Devices/Gamepad/UC_GpadPanel.Designer.cs @@ -1,4 +1,4 @@ -namespace SCJMapper_V2.Gamepad +namespace SCJMapper_V2.Devices.Gamepad { partial class UC_GpadPanel { diff --git a/Gamepad/UC_GpadPanel.cs b/Devices/Gamepad/UC_GpadPanel.cs similarity index 98% rename from Gamepad/UC_GpadPanel.cs rename to Devices/Gamepad/UC_GpadPanel.cs index 069665f..479de10 100644 --- a/Gamepad/UC_GpadPanel.cs +++ b/Devices/Gamepad/UC_GpadPanel.cs @@ -7,7 +7,7 @@ using System.Linq; using System.Text; using System.Windows.Forms; -namespace SCJMapper_V2.Gamepad +namespace SCJMapper_V2.Devices.Gamepad { public partial class UC_GpadPanel : UserControl { diff --git a/Gamepad/UC_GpadPanel.resx b/Devices/Gamepad/UC_GpadPanel.resx similarity index 100% rename from Gamepad/UC_GpadPanel.resx rename to Devices/Gamepad/UC_GpadPanel.resx diff --git a/Joystick/FormReassign.Designer.cs b/Devices/Joystick/FormReassign.Designer.cs similarity index 99% rename from Joystick/FormReassign.Designer.cs rename to Devices/Joystick/FormReassign.Designer.cs index 7c8b6a0..74e45d0 100644 --- a/Joystick/FormReassign.Designer.cs +++ b/Devices/Joystick/FormReassign.Designer.cs @@ -1,4 +1,4 @@ -namespace SCJMapper_V2.Joystick +namespace SCJMapper_V2.Devices.Joystick { partial class FormReassign { diff --git a/Joystick/FormReassign.cs b/Devices/Joystick/FormReassign.cs similarity index 98% rename from Joystick/FormReassign.cs rename to Devices/Joystick/FormReassign.cs index 5d1e7b6..57768e4 100644 --- a/Joystick/FormReassign.cs +++ b/Devices/Joystick/FormReassign.cs @@ -7,7 +7,7 @@ using System.Linq; using System.Text; using System.Windows.Forms; -namespace SCJMapper_V2.Joystick +namespace SCJMapper_V2.Devices.Joystick { partial class FormReassign : Form { diff --git a/Joystick/FormReassign.resx b/Devices/Joystick/FormReassign.resx similarity index 100% rename from Joystick/FormReassign.resx rename to Devices/Joystick/FormReassign.resx diff --git a/Joystick/JoystickCls.cs b/Devices/Joystick/JoystickCls.cs similarity index 99% rename from Joystick/JoystickCls.cs rename to Devices/Joystick/JoystickCls.cs index a5ded19..c74f1b7 100644 --- a/Joystick/JoystickCls.cs +++ b/Devices/Joystick/JoystickCls.cs @@ -8,7 +8,9 @@ using System.Windows.Forms; using System.Drawing; using System.Text.RegularExpressions; -namespace SCJMapper_V2.Joystick +using SCJMapper_V2.Common; + +namespace SCJMapper_V2.Devices.Joystick { /// /// Handles one JS device as DXInput device diff --git a/Joystick/JoystickList.cs b/Devices/Joystick/JoystickList.cs similarity index 98% rename from Joystick/JoystickList.cs rename to Devices/Joystick/JoystickList.cs index a2f3abb..aaf04f2 100644 --- a/Joystick/JoystickList.cs +++ b/Devices/Joystick/JoystickList.cs @@ -5,7 +5,9 @@ using System.Linq; using System.Text; using System.Windows.Forms; -namespace SCJMapper_V2.Joystick +using SCJMapper_V2.Common; + +namespace SCJMapper_V2.Devices.Joystick { public class JoystickList : List, IDisposable { diff --git a/Joystick/JsReassingList.cs b/Devices/Joystick/JsReassingList.cs similarity index 96% rename from Joystick/JsReassingList.cs rename to Devices/Joystick/JsReassingList.cs index 4b99de6..e1f896d 100644 --- a/Joystick/JsReassingList.cs +++ b/Devices/Joystick/JsReassingList.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; -namespace SCJMapper_V2.Joystick +namespace SCJMapper_V2.Devices.Joystick { public struct JsReassingR { diff --git a/Joystick/Main.cs b/Devices/Joystick/Main.cs similarity index 100% rename from Joystick/Main.cs rename to Devices/Joystick/Main.cs diff --git a/Joystick/Main.resx b/Devices/Joystick/Main.resx similarity index 100% rename from Joystick/Main.resx rename to Devices/Joystick/Main.resx diff --git a/Joystick/Modifiers.cs b/Devices/Joystick/Modifiers.cs similarity index 98% rename from Joystick/Modifiers.cs rename to Devices/Joystick/Modifiers.cs index c9f5a42..0053534 100644 --- a/Joystick/Modifiers.cs +++ b/Devices/Joystick/Modifiers.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Text; using System.Xml; -namespace SCJMapper_V2.Joystick +namespace SCJMapper_V2.Devices.Joystick { /// /// Maintain actionmap Modifiers diff --git a/Joystick/UC_JoyPanel.Designer.cs b/Devices/Joystick/UC_JoyPanel.Designer.cs similarity index 99% rename from Joystick/UC_JoyPanel.Designer.cs rename to Devices/Joystick/UC_JoyPanel.Designer.cs index 614e011..b31b13e 100644 --- a/Joystick/UC_JoyPanel.Designer.cs +++ b/Devices/Joystick/UC_JoyPanel.Designer.cs @@ -1,4 +1,4 @@ -namespace SCJMapper_V2.Joystick +namespace SCJMapper_V2.Devices.Joystick { partial class UC_JoyPanel { diff --git a/Joystick/UC_JoyPanel.cs b/Devices/Joystick/UC_JoyPanel.cs similarity index 99% rename from Joystick/UC_JoyPanel.cs rename to Devices/Joystick/UC_JoyPanel.cs index 054793f..b904285 100644 --- a/Joystick/UC_JoyPanel.cs +++ b/Devices/Joystick/UC_JoyPanel.cs @@ -7,7 +7,7 @@ using System.Linq; using System.Text; using System.Windows.Forms; -namespace SCJMapper_V2.Joystick +namespace SCJMapper_V2.Devices.Joystick { public partial class UC_JoyPanel : UserControl { diff --git a/Joystick/UC_JoyPanel.resx b/Devices/Joystick/UC_JoyPanel.resx similarity index 100% rename from Joystick/UC_JoyPanel.resx rename to Devices/Joystick/UC_JoyPanel.resx diff --git a/Joystick/UICustHeader.cs b/Devices/Joystick/UICustHeader.cs similarity index 99% rename from Joystick/UICustHeader.cs rename to Devices/Joystick/UICustHeader.cs index 7a809f1..f382a63 100644 --- a/Joystick/UICustHeader.cs +++ b/Devices/Joystick/UICustHeader.cs @@ -6,7 +6,7 @@ using System.Xml; using System.IO; using System.Xml.Linq; -namespace SCJMapper_V2.Joystick +namespace SCJMapper_V2.Devices.Joystick { /// /// Maintains an CustomisationUIHeader - something like: diff --git a/Joystick/xyPoints.cs b/Devices/Joystick/xyPoints.cs similarity index 97% rename from Joystick/xyPoints.cs rename to Devices/Joystick/xyPoints.cs index e839a15..9f68204 100644 --- a/Joystick/xyPoints.cs +++ b/Devices/Joystick/xyPoints.cs @@ -1,11 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - + +using System; using OpenTK; -namespace SCJMapper_V2.Joystick +namespace SCJMapper_V2.Devices.Joystick { /// /// contains the x(in) and y(out) points of the nonlin curve for joysticks (MAX 3 interpolation pts) diff --git a/Keyboard/KeyboardCls.cs b/Devices/Keyboard/KeyboardCls.cs similarity index 92% rename from Keyboard/KeyboardCls.cs rename to Devices/Keyboard/KeyboardCls.cs index 06df90d..d630427 100644 --- a/Keyboard/KeyboardCls.cs +++ b/Devices/Keyboard/KeyboardCls.cs @@ -7,7 +7,9 @@ using System.Windows.Forms; using SharpDX; using System.Text.RegularExpressions; -namespace SCJMapper_V2.Keyboard +using SCJMapper_V2.Common; + +namespace SCJMapper_V2.Devices.Keyboard { /// /// Handles one Keyboard device as DXInput device @@ -138,6 +140,23 @@ namespace SCJMapper_V2.Keyboard case ( int )Key.LeftControl: ctrlMod += "lctrl+"; break; case ( int )Key.RightControl: ctrlMod += "rctrl+"; break; + // function keys first - modifier ?? + case (int)Key.F1: key += "f1+"; break; + case (int)Key.F2: key += "f2+"; break; + case (int)Key.F3: key += "f3+"; break; + case (int)Key.F4: key += "f4+"; break; + case (int)Key.F5: key += "f5+"; break; + case (int)Key.F6: key += "f6+"; break; + case (int)Key.F7: key += "f7+"; break; + case (int)Key.F8: key += "f8+"; break; + case (int)Key.F9: key += "f9+"; break; + case (int)Key.F10: key += "f10+"; break; + case (int)Key.F11: key += "f11+"; break; + case (int)Key.F12: key += "f12+"; break; + case (int)Key.F13: key += "f13+"; break; + case (int)Key.F14: key += "f14+"; break; + case (int)Key.F15: key += "f15+"; break; + // all keys where the DX name does not match the SC name // Numpad case ( int )Key.NumberLock: key += "numlock+"; break; @@ -206,12 +225,14 @@ namespace SCJMapper_V2.Keyboard // all where the lowercase DX name matches the SC name default: - if ( ( ( int )k >= ( int )Key.Q ) && ( ( int )k <= ( int )Key.P ) ) key += k.ToString( ).ToLowerInvariant( ) + "+"; // ranges are based on the enum values... - else if ( ( ( int )k >= ( int )Key.A ) && ( ( int )k <= ( int )Key.L ) ) key += k.ToString( ).ToLowerInvariant( ) + "+"; // ranges are based on the enum values... - else if ( ( ( int )k >= ( int )Key.Z ) && ( ( int )k <= ( int )Key.M ) ) key += k.ToString( ).ToLowerInvariant( ) + "+"; // ranges are based on the enum values... + if ( ( ( int )k >= ( int )Key.Q ) && ( ( int )k <= ( int )Key.P ) ) + key += k.ToString( ).ToLowerInvariant( ) + "+"; // ranges are based on the enum values... + + else if ( ( ( int )k >= ( int )Key.A ) && ( ( int )k <= ( int )Key.L ) ) + key += k.ToString( ).ToLowerInvariant( ) + "+"; // ranges are based on the enum values... - else if ( ( ( int )k >= ( int )Key.F1 ) && ( ( int )k <= ( int )Key.F10 ) ) key += k.ToString( ).ToLowerInvariant( ) + "+"; // ranges are based on the enum values... - else if ( ( ( int )k >= ( int )Key.F11 ) && ( ( int )k <= ( int )Key.F15 ) ) key += k.ToString( ).ToLowerInvariant( ) + "+"; // ranges are based on the enum values... + else if ( ( ( int )k >= ( int )Key.Z ) && ( ( int )k <= ( int )Key.M ) ) + key += k.ToString( ).ToLowerInvariant( ) + "+"; // ranges are based on the enum values... else { } // no other ones handled break; diff --git a/Mouse/MouseCls.cs b/Devices/Mouse/MouseCls.cs similarity index 99% rename from Mouse/MouseCls.cs rename to Devices/Mouse/MouseCls.cs index 3b66431..15e94aa 100644 --- a/Mouse/MouseCls.cs +++ b/Devices/Mouse/MouseCls.cs @@ -8,7 +8,9 @@ using SharpDX; using System.Reflection; using System.Text.RegularExpressions; -namespace SCJMapper_V2.Mouse +using SCJMapper_V2.Common; + +namespace SCJMapper_V2.Devices.Mouse { /// /// Handles one Mouse device as DXInput device diff --git a/Options/DeviceOptionParameter.cs b/Devices/Options/DeviceOptionParameter.cs similarity index 98% rename from Options/DeviceOptionParameter.cs rename to Devices/Options/DeviceOptionParameter.cs index 010bad2..0c2119e 100644 --- a/Options/DeviceOptionParameter.cs +++ b/Devices/Options/DeviceOptionParameter.cs @@ -3,7 +3,7 @@ using System; -namespace SCJMapper_V2.Options +namespace SCJMapper_V2.Devices.Options { public class DeviceOptionParameter : ICloneable { @@ -99,7 +99,7 @@ namespace SCJMapper_V2.Options m_saturationSupported = false; m_saturation = "1.000"; m_saturationEnabled = false; - if ( Joystick.JoystickCls.IsDeviceClass( m_deviceClass ) ) { + if ( Devices.Joystick.JoystickCls.IsDeviceClass( m_deviceClass ) ) { m_saturationSupported = true; if ( !string.IsNullOrEmpty( sa ) ) { m_saturation = sa; diff --git a/Options/DeviceTuningParameter.cs b/Devices/Options/DeviceTuningParameter.cs similarity index 99% rename from Options/DeviceTuningParameter.cs rename to Devices/Options/DeviceTuningParameter.cs index 58519ed..4135eab 100644 --- a/Options/DeviceTuningParameter.cs +++ b/Devices/Options/DeviceTuningParameter.cs @@ -1,10 +1,11 @@ -using SCJMapper_V2.Gamepad; -using SCJMapper_V2.Joystick; -using System; +using System; using System.Collections.Generic; using System.Xml; -namespace SCJMapper_V2.Options +using SCJMapper_V2.Actions; +using SCJMapper_V2.Devices.Joystick; + +namespace SCJMapper_V2.Devices.Options { /// /// set of parameters to tune the Joystick diff --git a/Options/Deviceoptions.cs b/Devices/Options/Deviceoptions.cs similarity index 99% rename from Options/Deviceoptions.cs rename to Devices/Options/Deviceoptions.cs index e90e983..f7a0d2b 100644 --- a/Options/Deviceoptions.cs +++ b/Devices/Options/Deviceoptions.cs @@ -3,11 +3,11 @@ using System.Collections.Generic; using System.Xml; using System.IO; using System.Xml.Linq; -using SCJMapper_V2.Joystick; -using SCJMapper_V2.Gamepad; +using SCJMapper_V2.Devices.Joystick; +using SCJMapper_V2.Devices.Gamepad; using System.Linq; -namespace SCJMapper_V2.Options +namespace SCJMapper_V2.Devices.Options { /// /// Maintains an Deviceoptions - something like: diff --git a/Options/FormOptions.Designer.cs b/Devices/Options/FormOptions.Designer.cs similarity index 99% rename from Options/FormOptions.Designer.cs rename to Devices/Options/FormOptions.Designer.cs index 03f9165..cc7cbc7 100644 --- a/Options/FormOptions.Designer.cs +++ b/Devices/Options/FormOptions.Designer.cs @@ -1,4 +1,4 @@ -namespace SCJMapper_V2.Options +namespace SCJMapper_V2.Devices.Options { partial class FormOptions { diff --git a/Options/FormOptions.cs b/Devices/Options/FormOptions.cs similarity index 99% rename from Options/FormOptions.cs rename to Devices/Options/FormOptions.cs index 309ba28..cc3148f 100644 --- a/Options/FormOptions.cs +++ b/Devices/Options/FormOptions.cs @@ -1,5 +1,5 @@ -using SCJMapper_V2.Gamepad; -using SCJMapper_V2.Joystick; +using SCJMapper_V2.Devices.Gamepad; +using SCJMapper_V2.Devices.Joystick; using SCJMapper_V2.OGL; using System; using System.Collections.Generic; @@ -12,7 +12,9 @@ using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; -namespace SCJMapper_V2.Options +using SCJMapper_V2.Actions; + +namespace SCJMapper_V2.Devices.Options { public partial class FormOptions : Form { diff --git a/Options/FormOptions.resx b/Devices/Options/FormOptions.resx similarity index 100% rename from Options/FormOptions.resx rename to Devices/Options/FormOptions.resx diff --git a/Options/OptionTree.cs b/Devices/Options/OptionTree.cs similarity index 99% rename from Options/OptionTree.cs rename to Devices/Options/OptionTree.cs index 1670eb0..2c5139e 100644 --- a/Options/OptionTree.cs +++ b/Devices/Options/OptionTree.cs @@ -6,9 +6,8 @@ using System.Xml; using System.IO; using System.Xml.Linq; using System.Windows.Forms; -using SCJMapper_V2.Joystick; -namespace SCJMapper_V2.Options +namespace SCJMapper_V2.Devices.Options { /// /// Maintains an Options - something like: @@ -107,7 +106,7 @@ namespace SCJMapper_V2.Options m_tuning.Add( "turret_aim_yaw", new DeviceTuningParameter( "turret_aim_yaw", device ) ); // Gamepad specific - if ( Gamepad.GamepadCls.IsDeviceClass( device.DevClass ) ) { + if ( Devices.Gamepad.GamepadCls.IsDeviceClass( device.DevClass ) ) { m_tuning.Add( "fps_view_pitch", new DeviceTuningParameter( "fps_view_pitch", device ) ); m_tuning.Add( "fps_view_yaw", new DeviceTuningParameter( "fps_view_yaw", device ) ); diff --git a/Options/OptionsInvert.cs b/Devices/Options/OptionsInvert.cs similarity index 97% rename from Options/OptionsInvert.cs rename to Devices/Options/OptionsInvert.cs index 1a4717c..58eca9a 100644 --- a/Options/OptionsInvert.cs +++ b/Devices/Options/OptionsInvert.cs @@ -4,10 +4,10 @@ using System.Linq; using System.Text; using System.Xml; using System.Windows.Forms; -using SCJMapper_V2.Joystick; -using SCJMapper_V2.Gamepad; +using SCJMapper_V2.Devices.Joystick; +using SCJMapper_V2.Devices.Gamepad; -namespace SCJMapper_V2.Options +namespace SCJMapper_V2.Devices.Options { public class OptionsInvert { diff --git a/Options/Tuningoptions.cs b/Devices/Options/Tuningoptions.cs similarity index 99% rename from Options/Tuningoptions.cs rename to Devices/Options/Tuningoptions.cs index d719ac6..79c7d27 100644 --- a/Options/Tuningoptions.cs +++ b/Devices/Options/Tuningoptions.cs @@ -1,5 +1,5 @@ -using SCJMapper_V2.Gamepad; -using SCJMapper_V2.Joystick; +using SCJMapper_V2.Devices.Gamepad; +using SCJMapper_V2.Devices.Joystick; using System; using System.Collections.Generic; using System.IO; @@ -8,7 +8,7 @@ using System.Text; using System.Threading.Tasks; using System.Xml; -namespace SCJMapper_V2.Options +namespace SCJMapper_V2.Devices.Options { public class Tuningoptions : CloneableDictionary, ICloneable { diff --git a/FormMain.Designer.cs b/FormMain.Designer.cs index 13a247b..7e00a5d 100644 --- a/FormMain.Designer.cs +++ b/FormMain.Designer.cs @@ -15,8 +15,8 @@ { timer1.Stop( ); // Unacquire all DirectInput objects. - foreach ( Joystick.JoystickCls js in DeviceInst.JoystickListRef ) js.FinishDX( ); - DeviceInst.JoystickListRef.Clear( ); + foreach ( Devices.Joystick.JoystickCls js in Devices.DeviceInst.JoystickListRef ) js.FinishDX( ); + Devices.DeviceInst.JoystickListRef.Clear( ); if ( disposing && ( components != null ) ) components.Dispose( ); if ( disposing && ( m_AT != null ) ) m_AT.Dispose( ); @@ -91,7 +91,7 @@ this.tdiAddMod3 = new System.Windows.Forms.ToolStripMenuItem(); this.tc1 = new System.Windows.Forms.TabControl(); this.tabJS1 = new System.Windows.Forms.TabPage(); - this.UC_JoyPanel = new SCJMapper_V2.Joystick.UC_JoyPanel(); + this.UC_JoyPanel = new SCJMapper_V2.Devices.Joystick.UC_JoyPanel(); this.panel1 = new System.Windows.Forms.Panel(); this.btClip = new System.Windows.Forms.Button(); this.txRebind = new System.Windows.Forms.TextBox(); @@ -1478,7 +1478,7 @@ private System.Windows.Forms.ToolStripMenuItem tsiSaveAs; private System.Windows.Forms.SaveFileDialog SFD; private System.Windows.Forms.ImageList IL; - private Joystick.UC_JoyPanel UC_JoyPanel; + private Devices.Joystick.UC_JoyPanel UC_JoyPanel; private System.Windows.Forms.TableLayoutPanel tlpanel; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; diff --git a/FormMain.cs b/FormMain.cs index dc6b5b5..ab1c6e2 100644 --- a/FormMain.cs +++ b/FormMain.cs @@ -10,13 +10,16 @@ using System.Windows.Forms; using SharpDX.DirectInput; using System.IO; +using SCJMapper_V2.Common; +using SCJMapper_V2.Actions; using SCJMapper_V2.SC; using SCJMapper_V2.Table; -using SCJMapper_V2.Keyboard; -using SCJMapper_V2.Mouse; -using SCJMapper_V2.Gamepad; -using SCJMapper_V2.Joystick; -using SCJMapper_V2.Options; +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; namespace SCJMapper_V2 { @@ -414,7 +417,7 @@ namespace SCJMapper_V2 // provide the display items (init) m_AT.DefineShowOptions( cbxShowJoystick.Checked, cbxShowGamepad.Checked, cbxShowKeyboard.Checked, cbxShowMouse.Checked, cbxShowMappedOnly.Checked ); // Init with default profile filepath - m_AT.LoadProfileTree( SCDefaultProfile.DefaultProfileName, addDefaultBinding ); + m_AT.LoadProfileTree( addDefaultBinding ); lblProfileUsed.Text = SCDefaultProfile.UsedDefProfile; // SCA 2.2 show used profile // Activation Update @@ -853,7 +856,7 @@ namespace SCJMapper_V2 { AutoTabXML_Assignment( EATabXML.Tab_XML ); - rtb.Text = SCDefaultProfile.DefaultProfile( SCDefaultProfile.DefaultProfileName ); + rtb.Text = SCDefaultProfile.DefaultProfile( ); } private void btGrab_Click( object sender, EventArgs e ) diff --git a/FormSettings.cs b/FormSettings.cs index 4392156..b4b3934 100644 --- a/FormSettings.cs +++ b/FormSettings.cs @@ -7,6 +7,8 @@ using System.Linq; using System.Text; using System.Windows.Forms; +using SCJMapper_V2.Actions; + namespace SCJMapper_V2 { partial class FormSettings : Form diff --git a/LICENSE b/LICENSE index 26ce462..5254afe 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,8 @@ The MIT License (MIT) -Copyright (c) 2016 bm98 +For SCJMapper: + +Copyright (c) 2017 bm98 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +20,118 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. + +------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------- +Included components +------------------------------------------------------------------------------------- +BSD License + +For ZstdNet software + +Copyright (c) 2016-2017, SKB Kontur. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name SKB Kontur nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------------------------------------------------------------------------- +BSD License + +For Zstandard software + +Copyright (c) 2016-present, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------------------------------------------------------------------------- +Apache License, Version 2.0 + +For log4net http://logging.apache.org/log4net/license.html + +------------------------------------------------------------------------------------- +Copyright (c) 2010-2015 SharpDX - Alexandre Mutel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +------------------------------------------------------------------------------------- +The Open Toolkit library license + +Copyright (c) 2006 - 2014 Stefanos Apostolopoulos for +the Open Toolkit library. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in the +Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, subject +to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------------- diff --git a/OGL/FormJSCalCurve.cs b/OGL/FormJSCalCurve.cs index 7235562..a0fa9bb 100644 --- a/OGL/FormJSCalCurve.cs +++ b/OGL/FormJSCalCurve.cs @@ -8,17 +8,16 @@ using System.Text; using System.Windows.Forms; using OpenTK; -using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; -using OpenTK.Input; using System.IO; using System.Diagnostics; using System.Windows.Forms.DataVisualization.Charting; +using SCJMapper_V2.Actions; +using SCJMapper_V2.Devices.Joystick; +using SCJMapper_V2.Devices.Options; using SCJMapper_V2.OGL.TextureLoaders; -using SCJMapper_V2.Joystick; -using SCJMapper_V2.Options; namespace SCJMapper_V2.OGL { diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 2da81e7..248c591 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion( "2.32.0.66" )] -[assembly: AssemblyFileVersion( "2.32.0.66" )] +[assembly: AssemblyVersion( "2.33.0.67" )] +[assembly: AssemblyFileVersion( "2.33.0.67" )] diff --git a/README.md b/README.md index e010b72..aeeb839 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ SCJMapper-V2
============

-SC Joystick Mapper (.Net 4; using sharpDX/OpenTK wrapper)
-this should work with Win8.x Win 10 out of the box
+SC Joystick Mapper (.Net 4.5.2; using sharpDX/OpenTK wrapper)
+this should work with Win7, Win8.x Win 10 out of the box

+Built using Windows Forms - Issues with display scaling may be encountered - set display scaling to 100%.
using sharpDX 2.6.3 .net wrapper (http://sharpdx.org/)

@@ -15,8 +16,9 @@ using OpenTK.1.1.1589.5942 .net wrapper (http://www.opentk.com/)
NET40\OpenTK.dll
NET40\OpenTK.GLControl.dll

-NET20\Ionic.Zip.Reduced.dll
-net40-full\log4net.dll
+using ZstdNet 1.3.1 (https://www.nuget.org/packages/ZstdNet/)
+
+using log4net.dll

Built with VisualStudio 2017 Community free version

@@ -33,6 +35,9 @@ This file is licensed under the Creative Commons Attribution 2.0 Generic license At Toyomi fishermen's wharf, Tokyo.
Author: Author Masato OHTA

+SC_Area18, _GrimHex, _DyingStar, _BrokenMoon, _Kareah +by Rellim (SC handle) +
OutThere1 and 3 made with Spacescape:
http://alexcpeterson.com/spacescape

diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000..ca7667b --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1,289 @@ +SC Joystick Mapper V 2.33 - Build 67 BETA +(c) Cassini, StandardToaster - 23-Dec-2017 + +Contains 12 files + graphics: + +SCJMapper.exe The program (V2.33) +SCJMapper.exe.config Program config (V2.33) - MUST be in the same folder as the Exe file +SharpDX.DirectInput.dll Managed DirectInput Assembly - MUST be in the same folder as the Exe file +SharpDX.dll Managed DirectX Assembly - MUST be in the same folder as the Exe file +OpenTK.dll Managed OpenGL Assembly - MUST be in the same folder as the Exe file +OpenTK.GLControl.dll Managed OpenGL Assembly - MUST be in the same folder as the Exe file +ZstdNet.dll Managed Zip Assembly (v2.33) - MUST be in the same folder as the Exe file +x64/libzstd.dll Native dll for ZstdNet (v2.33) - MUST be in the same folder as the Exe file +x86/libzstd.dll Native dll for ZstdNet (v2.33) - MUST be in the same folder as the Exe file +log4net.dll Managed Logging Assembly - MUST be in the same folder as the Exe file +log4net.config.OFF Config file for logging - To use it - rename as log4net.config and run the program + then look for trace.log in the same folder +SCJMapper_QGuide V2.30beta.pdf Quick Guide +ReadMe.txt This file + +graphics folder Skybox Images (V2.32) - graphics folder MUST be in the same folder as the Exe file + +NOTE V 2.33: + search order for defaultProfile.xml to build the action tree is: + 1. directory where SCJMapper Exe is located + 2. directory of \LIVE\USER + 3. extract from \LIVE\Data.p4k + 4. extract from SCJMapper exe file (derived from 3.0 build 695052) + + --> in order to get always the most current one use 3. (and therefore remove the ones in 1. and 2.) + --> The one used is shown below the actionTree (Profile: ....) + +Read the Guide first RTFM ;-) +Put all files into one folder and hit SCJMapper.exe to run it + +For Updates and information visit: +https://github.com/SCToolsfactory/SCJMapper-V2 + +Scanned for viruses before packing... +cassini@burri-web.org + +Changelog: +V 2.33 - BETA Build 67 +- update for SC 3.0.0 Alpha public +- fix - finding SC game folder - may work automatically for 3.0 Alpha else define it in Settings +- add - get the defaultProfile.xml from LIVE\data.p4k file if possible (real game assets) +- improvement - caching def profile once it is read from disk +- removed - old SC path and folder locators (SCJM does not longer work with pre 3.0 game) +- removed - reference to Iconic.Zip DLL (replaced with Zstd) +- update - defaultProfile.xml as last resort from PTU 3.0-695052 (Dec 18, build) +V 2.32 - BETA Build 66 +- add - path to defaultProfile can be in USER directory of SC +- add - some skyboxes from game captures (thanks to Rellim) +- removed - PTU folders in Settings - no longer used in PTU 3.0 +- fix - finding SC game folder - may work automatically for PTU 3.0 else define it in Settings +- update - defaultProfile from PTU 3.0-689345 (Dec 15, build) +V 2.30 - BETA Build 64 +- add - Tab to show all mappings for the current input (Tabbed with XML other Dump items) +- add - Setting (enabled, disabled -> default) to automatically switch the new tabs - either Input or Dump +- add - addbind of Mouse input is possible for Keyboard actions - seems to work somehow in the game... +- improvement - mouse mappings in kbd entries in defaultProfile are collected as mouse now and allowed to map +- improvement - removed some unneeded tree scans - to speed things up +- fix - issue with user activations modes while dumping the mapping list +- fix - issue with loading a map with gamepad mappings and the gamepad is not connected +- fixes and refacturing while encountered... +- update - doc SCJMapper_QGuide V2.30beta.pdf +V 2.29 - BETA Build 63 +- add - Calibrate gamepad thumb axes (press ABXY buttons all together and wait 2 sec - should zero all 4 axes) +- fix #56 - exception when entering Tuning +- fix - wrong gamepad action codes (xi1_xi_command instead of xi1_command) +- fix - some more issues with gamepads +- fix - options did not properly update when assigning them to another cmd or clearing the entry +- improvement - Options Dialog selection and deselection of items improved +- improvement - Tuning Dialog selection and deselection of items improved +- improvement - Dump Log: added some more interesting captures from the game log file +- improvement - Win7 / Win10 hidden text/controls with High DPI scaling issues partly resolved +- update - doc SCJMapper_QGuide V2.29beta.pdf +V 2.28 - BETA Build 62 +- add #48 - Tune Strafe controls +- add - "Options ..." dialog to edit all device and control options +- add - 2 more three 3D scenes for Tuning +- improvement #49 - Mapping area: Current mapping is shown +- improvement - Gamepad support improved, Tab is now always the most left one if gamepads are enabled +- improvement - Dump Log: added some more interesting captures from the game log file +- improvement - Tuning is now closer to CIG implementation, remove Sensitivity, add Saturation instead +- fix #51 - accepting multiple actionmaps in default profile (collects only the first one found) +- fix - bug re- joystick hats (affected No 2..4) +- fixes and refacturing while encountered... +- update - fallback default profile from SC 2.6.3 alpha +- update - doc SCJMapper_QGuide V2.28beta.pdf +V 2.27 - BETA Build 61 +- add - Collapse/Expand in context menu in Mapping tree +- improvement - actionmaps are taken from the defaultProfile and will no longer need a program update +- improvement - rename Blend to Disable +- fix - an issue in Seetings for actionmap ignore handling +- update - fallback default profile from SC 2.6.0 alpha +- update - doc SCJMapper_QGuide V2.27beta.pdf +V 2.26 - BETA Build 60 +- add - new actionmaps from SC 2.5.0 alpha to choose from in Settings +- update - fallback default profile from SC 2.5.0 alpha +V 2.25 - BETA Build 59 +- fix - an issue in parsing options from imported maps +- add - an option to show the actiontree as CSV list with more/less details (change in Settings) +- improvement - In table view add possibility to Blend All visible unmapped entries +V 2.24 - BETA Build 58 +- fix - some trouble in SC path finding +V 2.23 - BETA Build 57 +- update - Using .Net 4.5.2 Now (seems to handle some scaling issues WinForm apps) +- update - Try to find the SC path also as StarCitizen\Live (instead of Public) was mentioned for SC2.2.2 onwards ??? +- fix - addbind UNDEF removed when assigned +- improvement - Issue a infobox if the Client folder cannot be found + (please submit the complete folder structure of your installation as bug report ...) +- add - a table display for mappings +- some internal stuff (namespaces etc) +- update - doc SCJMapper_QGuide V2.23beta.pdf +V 2.22 - BETA Build 56 +- fix - try again to fix Win10 scaling issues for some PCs (hidden assignment area) +- improvement - actions with a profile modifier attached will show underlined in the action tree +- improvement - less offensive gamepad color mark ... +- add - a button to dump the used defaultProfile in the right area +- some internal stuff +- update - doc SCJMapper_QGuide V2.22beta.pdf +V 2.21 - BETA Build 55 +- fix #40 added Tab entry in Ctrl. context menu +- fix - try to fix Win10 scaling issues (hidden assignment area) +- fix - profile tree color indication also applied when re-reading defaultProfile +- improvement - enumerates up to 12 devices now (though not tested as I don't have 12 ..) +- add - use of SCA 2.2 provided defaultProfile (new location and format) +- add - indication of the used defaultProfile +- add - built in defaultProfile updated to SCA 2.2 +V 2.19 - BETA Build 52 +- fix #37 improved defaultProfile Parsing +- fix #38 locale issue - changed App number formatting to US +- fix #39 changed equal to equals string for kbd entry +- add - default actionmap to choose from (it is on the ignore list) +V 2.18 - BETA Build 51 +- fix - layout works now for Win10 +- fix - uses game defaultProfile again +- fix - keyboard command for all Ctrl keys fixed +- fix - keyboard command formatting +- improvement - timeout ~4 sec for kbd modifiers in Joystick Mode (Esc no longer needed) +- improvement - ActivationMode handling finished +- improvement - user ActivationMode change indication in mapping tree +- improvement - Blending adds multiTap=1 to overwrite double taps +- improvement - Dump List: added ActivationModes; device checkBox applied to list +- update - doc SCJMapper_QGuide V2.18beta.pdf +V 2.17 - BETA Build 50 +- update - Updated for SC Alpha 2.0/2.1PTU using new actionmap syntax (no longer use device attribute) +- update - Complete new QuickReference Guide +- update - Supports actionmaps with +- add - ActivationMode - Use Context Menu in ActionTree (or read the manual) +- add - PTU file usage in Settings +- add - Prepared JS Modifiers (but SC cannot right now - so it is disabled) +- add - full mouse settings +- improvement - some GUI improvements +- improvement - reworked blending +- removed - global JS or GP blend options in Settings +- removed - ignoreversion from Settings +- NOTE: - Dump Log does not work right now as CIG does no longer list detected controllers in the log file +- NOTE: - Right now a number of binds behave erratically e.g. addbind does not work at all + so be aware that your map is not necessarily wrong but the game may just have a bug there +V 2.16 - BETA Build 49 +- update - Updated for AC Alpha 1.3 defaultProfile does no longer have js1_ or xi_ marks form commands +- NOTE: - Dump Log does not work right now as CIG does no longer list detected controllers in the log file +V 2.15 - BETA Build 48 +- update - Updated for AC Alpha 1.1.6 new files locations to find files and mappings +- NOTE: - Dump Log does not work right now as CIG does no longer list detected controllers in the log file +V 2.14 - BETA Build 47 +- update - added new defaultProfile (CIG allows some more joystick mappings) +V 2.13 - BETA Build 46 +- update - added new defaultProfile and actionmaps from AC 1.1.1 +- add - keyboard modifier for joystick (e.g. rctrl+js1_xy) - Press ESC to clear modifiers +- fix - device checkboxes are now applied after Reassign +- fix - invert checkbox handling (removed flight invert - use the one in Tuning) +- fix - Add UICustomization Header and Devices List in any case +V 2.12 - BETA Build 45 +- improvement - SCJM maintains mappings in USER rather than data folder (AC 1.03) +- improvement - UICustomization Header for joystick updated (label is the filename minus "layout_") (AC 1.03) +V 2.11 - BETA Build 44 +- fix - reading of deadzone value (if not a number should not break anymore) +- fix - writing the proper deadzone XML if first used +- fix - reading addbind commands from existing mappings will appear now in the tree +- improvement - better handling of the default mapping name from config file +- improvement - mapping name added to XML mapping (first line comment extended with mapping name) +V 2.10 - Build 43 - Production +- new feature - added Action Tree context menu for Assign, Clear and Blend +- fix - issue for Js Reassignment if more than one was not yet assigned +- improvement - Right click in Action Tree selects the items (no need to select and then right click anymore) +V 2.10 - BETA Build 41 +- fix - issue with null ptr assignment in Device Tuning (review and fix of AC1.0 changes) +- fix - disabled first joystick tab when gamepad is second or later +- improvement - added tooltips for device tabs showing Name and GUID +- improvement - added full 4 number version for beta builds +V 2.10 - BETA Build 40 +- rework for AC 1.0 +- new feature - add DumpLog to get the AC detected Controller assignments from logfile +- new feature - add Invert checkboxes for supported option items +- new feature - context menu in treeview allows to add/delete action sub items (support addbind mapping in XML) +- update - cannot longer assign cross device mappings (AC 1.0 related - use addbind above) +- update - new options naming and structure (not compatible with pre 2.10 - delete them in the file and then reload) +- update - Profile Version to 1 +V 2.8 - BETA Build 37 +- new feature - add checkboxes to show Joystick, Gamepad, Kbd and Mapped Only +- fix - Blended ones don't reload with proper visual +V 2.8 - BETA Build 36 +- new feature - add invert for single mappings +- improvement - initialization and assignment of Joystick devices +V 2.8 - BETA Build 34 +- new feature - add keyboard input +- new feature - add gamepad input as xboxpad +- new feature - add gamepad for tuning +- new feature - blend single entries with +- fix - tuning copy to all axis now applies immediately +- improvement - cleanup of some inconsistencies +V 2.7 - Build 33 +- fix - if an axis is not mapped the prog will not longer crash (was null ptr exception) +- doc update 2.7 +V 2.7 - BETA +- new feature - Joystick Tuning +V 2.6 +- fix - taking actionmaps from config file now works +- improvement - added actionmap vehicle_driver +V 2.5 +- new feature - support and maintain option tags +- improvement - support and maintain version and ignoreversion attribute / can force ignoreversion="1" +- improvement - makes backup copy before each save (in my documents e.g. layout_my_xyz.xml.backup) +- Update of the Guide for V2.5 +V 2.4 +- improvement - add new actionmaps for AC 0.9 (flycam, spaceship_turret) +- improvement - supports now assignment of js1 .. js8 - SC may not support all though... +- Update of the Guide for V2.4 +V 2.3 +- new feature - allow reassignment of the jsN group +- improvement - uniquely identified devices with the same name (use GUID) +- improvement - shows jsN assignment in Joystick tab +- improvement - detection of the SC install path extended to one more Registry entry +- fix - blend unmapped works properly now +- fix - manual entry of SC directory works now +- Update of the Guide for V2.3 +V 2.2 +- new feature - option to ignore actionmaps in Settings +- improvement - add actionmaps for multiplayer, singleplayer, player +- improvement - GUI layout of Joystick tabs for more than 4 devices +- Update of the Guide for V2.2 +V 2.1 +- program is maintained at "https://github.com/SCToolsfactory/SCJMapper-V2/releases" +- new feature - option to blend unmapped actions +- improvement - ignore buttons in Settings +- improvement - override the built in detection of the SC folder in Settings +- added - trace log for resolving crash and other issues +- Update of the Guide for V2.1 +V 2.0 +- program is maintained at "https://github.com/bm98/SCJMapper-V2/releases" +- new feature - get defaultProfile.xml from game assets +- new feature - get actionsmaps from game assets +- new feature - reset to defaults +- new feature - load and save own maps to gamefolders (makes backup in My Documents\SCJMapper) +- new feature - filter the action tree +- new feature - drag and drop an mapping file into the XML window +- new feature - make throttle assignment for any axis +- improved joystick detection (jitter avoidance, limit can be set in Program config file) +- improved button detection (blend buttons that are always on) +- improved sizeable window +- improved settings persist between sessions +- Update of the Guide for V2.0 +- removed defaultProfile.xml from distribution (REMOVE IT FROM YOUR FOLDER - else it will be taken as action list) +V 1.4PRE +- using a new Managed DirectX assembly and built with .Net4 (Hope this works for Win8.1) +- added Joystick properties and Axis Names from the Joystick driver +V 1.3 +- new feature - read the original defaultProfile.xml from SC to derive the actions (must be in the EXE folder) +- added support for up to 8 devices +- added multibinding i.e. bind the same action to multiple buttons, one for kbd, one for xbox etc. if the profile supports it +- added Dump List - a readable list of the commands (can be saved as txt file - using Save as) +- fixed "Find 1st" +- Update of the Guide +- removed MappingVars file from distribution (REMOVE IT FROM YOUR FOLDER - else it will be taken as action list) +V 1.2 +- added support for rebinding xboxpad and ps3pad +- added Find 1st for a Control +- fixed Hat direction not maintained as last Control used +- some GUI refinements +- Update of the Guide (incl MappingVar.csv format) +MappingVar file +- added commands that where missing +- changed from keyboard to xboxpad rebinding where possible to leave kbd intact +V 1.1 +- fixed issue with less than 3 joysticks attached +V 1.0 initial diff --git a/SC/ActivationModes.cs b/SC/ActivationModes.cs index 56c2775..21bdb01 100644 --- a/SC/ActivationModes.cs +++ b/SC/ActivationModes.cs @@ -249,7 +249,7 @@ namespace SCJMapper_V2.SC /// Returns the list of ActivationMode names ///
/// A list of names - public List Names + public IList Names { get { diff --git a/SC/DProfileReader.cs b/SC/DProfileReader.cs index 174a85e..30d5b50 100644 --- a/SC/DProfileReader.cs +++ b/SC/DProfileReader.cs @@ -4,6 +4,8 @@ using System.Text; using System.Xml; using System.IO; +using SCJMapper_V2.Actions; + namespace SCJMapper_V2.SC { /// @@ -131,7 +133,8 @@ namespace SCJMapper_V2.SC if ( attr.ContainsKey( "ActivationMode" ) ) { actModeName = attr["ActivationMode"]; multiTap = ActivationModes.Instance.MultiTapFor( actModeName ).ToString( ); // given by the already collected items - } else { + } + else { // name remains default - we handle multiTaps only here multiTap = "1"; // default if not changed in the action to may be 2 or so.. if ( attr.ContainsKey( "multiTap" ) ) { @@ -148,9 +151,10 @@ namespace SCJMapper_V2.SC ac.defBinding = attr["joystick"]; ac.defActivationMode = actMode; if ( ac.defBinding == " " ) { - ac.defBinding = Joystick.JoystickCls.BlendedInput; + ac.defBinding = Devices.Joystick.JoystickCls.BlendedInput; m_currentMap.Add( ac ); // finally add it to the current map if it was bound - } else if ( !string.IsNullOrEmpty( ac.defBinding ) ) { + } + else if ( !string.IsNullOrEmpty( ac.defBinding ) ) { ac.defBinding = "js1_" + ac.defBinding; m_currentMap.Add( ac ); // finally add it to the current map if it was bound } @@ -163,9 +167,10 @@ namespace SCJMapper_V2.SC ac.defBinding = attr["keyboard"]; ac.defActivationMode = actMode; if ( ac.defBinding == " " ) { - ac.defBinding = Keyboard.KeyboardCls.BlendedInput; + ac.defBinding = Devices.Keyboard.KeyboardCls.BlendedInput; m_currentMap.Add( ac ); // finally add it to the current map if it was bound - } else if ( !string.IsNullOrEmpty( ac.defBinding ) ) { + } + else if ( !string.IsNullOrEmpty( ac.defBinding ) ) { ac.defBinding = "kb1_" + ac.defBinding; m_currentMap.Add( ac ); // finally add it to the current map if it was bound } @@ -178,9 +183,10 @@ namespace SCJMapper_V2.SC ac.defBinding = attr["mouse"]; ac.defActivationMode = actMode; if ( ac.defBinding == " " ) { - ac.defBinding = Mouse.MouseCls.BlendedInput; + ac.defBinding = Devices.Mouse.MouseCls.BlendedInput; m_currentMap.Add( ac ); // finally add it to the current map if it was bound - } else if ( !string.IsNullOrEmpty( ac.defBinding ) ) { + } + else if ( !string.IsNullOrEmpty( ac.defBinding ) ) { ac.defBinding = "mo1_" + ac.defBinding; m_currentMap.Add( ac ); // finally add it to the current map if it was bound } @@ -193,9 +199,10 @@ namespace SCJMapper_V2.SC ac.defBinding = attr["xboxpad"]; ac.defActivationMode = actMode; if ( ac.defBinding == " " ) { - ac.defBinding = Gamepad.GamepadCls.BlendedInput; + ac.defBinding = Devices.Gamepad.GamepadCls.BlendedInput; m_currentMap.Add( ac ); // finally add it to the current map if it was bound - } else if ( !string.IsNullOrEmpty( ac.defBinding ) ) { + } + else if ( !string.IsNullOrEmpty( ac.defBinding ) ) { ac.defBinding = "xi1_" + ac.defBinding; m_currentMap.Add( ac ); // finally add it to the current map if it was bound } @@ -324,10 +331,12 @@ namespace SCJMapper_V2.SC // the element name is a control if ( xr.NodeType == XmlNodeType.EndElement ) { done = ( xr.Name == m_nodeNameStack.Peek( ) ); // EXIT if the end element matches the entry - } else if ( xr.IsEmptyElement ) { + } + else if ( xr.IsEmptyElement ) { // an attribute only element CollectActions( attr ); - } else { + } + else { // one with subelements again m_nodeNameStack.Push( xr.Name ); // recursive .. push element name to terminate later (this is i.e. keyboard) ReadActionSub( xr, actionName, xr.Name ); @@ -370,13 +379,15 @@ namespace SCJMapper_V2.SC m_currentMap.name = mapName; 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 - }else { + } + else { log.DebugFormat( "ReadElement: IGNORED duplicate map with name: {0}", mapName ); } m_state = EState.inActionMap; // now we are in and processing the map } } - } else if ( m_state == EState.inActionMap ) { + } + else if ( m_state == EState.inActionMap ) { // processing a valid action map - collect actions if ( eName.ToLower( ) == "action" ) { // this is an action.. - collect it @@ -415,10 +426,12 @@ namespace SCJMapper_V2.SC if ( xr.IsEmptyElement ) { retVal = retVal && ReadEmptyElement( xr ); m_nodeNameStack.Pop( ); // empty ones end inline - } else { + } + else { retVal = retVal && ReadElement( xr ); } - } else if ( xr.NodeType == XmlNodeType.EndElement ) { + } + else if ( xr.NodeType == XmlNodeType.EndElement ) { //Console.Write( "\n", xr.Name ); string exitElement = m_nodeNameStack.Pop( ); if ( m_state == EState.inActionMap ) @@ -432,14 +445,20 @@ namespace SCJMapper_V2.SC else return false; - } catch ( Exception ex ) { + } + catch ( Exception ex ) { // get any exceptions from reading log.Error( "DProfileReader.ReadXML - unexpected", ex ); return false; } } - + /* + // + // + ... + // + */ private bool ReadActivationModes( XmlReader xr ) { log.Debug( "DProfileReader.ReadActivationModes - Entry" ); @@ -458,7 +477,45 @@ namespace SCJMapper_V2.SC } while ( xr.Read( ) ); return true; - } catch ( Exception ex ) { + } + catch ( Exception ex ) { + // get any exceptions from reading + log.Error( "DProfileReader.ReadXML - unexpected", ex ); + return false; + } + } + + + // Read modifiers + + // + // + // + // + // + // + // + // + + // + // + // + + // + // + // + + // + + private bool ReadModifiers( XmlReader xr ) + { + log.Debug( "DProfileReader.ReadModifiers - Entry" ); + + try { + xr.ReadToFollowing( "modifiers" ); + return Modifiers.Instance.FromXML( xr.ReadOuterXml(), true ); + } + catch ( Exception ex ) { // get any exceptions from reading log.Error( "DProfileReader.ReadXML - unexpected", ex ); return false; @@ -494,6 +551,9 @@ namespace SCJMapper_V2.SC reader.Read( ); ValidContent &= ReadActivationModes( reader ); + Modifiers.Instance.Clear( ); + ValidContent &= ReadModifiers( reader ); + m_nodeNameStack.Push( "profile" ); // we are already in the XML now ValidContent &= ReadXML( reader ); diff --git a/SC/Modifiers.cs b/SC/Modifiers.cs new file mode 100644 index 0000000..828ad4d --- /dev/null +++ b/SC/Modifiers.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace SCJMapper_V2.SC +{ + public class Modifier + { + public string Name = ""; + public bool DefaultProfile = false; + + public Modifier( string name, bool defaultProfile = false ) + { + Name = name; + DefaultProfile = defaultProfile; + } + + public string ToXML() + { + string r = string.Format( "\t\t\n", Name ); + return r; + } + + public static bool operator ==( Modifier a, Modifier b ) + { + // If both are null, or both are same instance, return true. + if ( ReferenceEquals( a, b ) ) { + return true; + } + + // If one is null, but not both, return false. + if ( ( (object)a == null ) || ( (object)b == null ) ) { + return false; + } + + // Return true if the fields match: + return a.Equals( b ); + } + + public static bool operator !=( Modifier a, Modifier b ) + { + return !( a == b ); + } + + public override bool Equals( System.Object obj ) + { + // If parameter is null return false. + if ( obj == null ) { + return false; + } + + // If parameter cannot be cast to Point return false. + Modifier p = obj as Modifier; + if ( (System.Object)p == null ) { + return false; + } + + // Return true if the fields match: + return ( this.Equals( p ) ); + } + + /// + /// Returns true if they are the same + /// + /// ActivationMode to compare with + /// True if both are the same, else false + public bool Equals( Modifier p ) + { + // If parameter is null return false: + if ( (object)p == null ) { + return false; + } + + // Return true if the fields match: + return ( Name == p.Name ); + } + + + public override int GetHashCode() + { + return Name.GetHashCode( ) ^ this.GetHashCode( ); + } + } + + + /// + /// Contains the ActivationMode from SC + /// can be: + /// "Default" using the defActivationMode setting of the profile + /// any of the List of profileDefined ones + /// + public sealed class Modifiers : List + { + private static readonly Modifiers instance = new Modifiers( ); + + + public static Modifiers Instance + { + get { + return instance; + } + } + + /// + /// cTor: Empty - hidden + /// + private Modifiers() { } + + /// + /// Returns the list of ActivationMode names + /// + /// A list of names + public IList Names + { + get { + List retVal = new List( ); + foreach ( Modifier am in this ) retVal.Add( am.Name ); + return retVal; + } + } + + /// + /// Returns the number of users added modifiers + /// + public int UserCount + { + get { + int cnt = 0; + foreach ( Modifier m in this ) { + if ( !m.DefaultProfile ) + cnt++; + } + return cnt; + } + } + + /// + /// Read the modifier node from profile or ActionMaps + /// + /// + /// + /// + public bool FromXML( string xml, bool defProfile = false ) + { + XmlReaderSettings settings = new XmlReaderSettings( ); + settings.ConformanceLevel = ConformanceLevel.Fragment; + settings.IgnoreWhitespace = true; + settings.IgnoreComments = true; + XmlReader reader = XmlReader.Create( new StringReader( xml ), settings ); + + try { + reader.ReadToFollowing( "modifiers" ); + reader.ReadToDescendant( "mod" ); + do { + if ( reader.NodeType == XmlNodeType.EndElement ) { + reader.Read( ); + break; // finished + } + string name = reader["input"]; + if ( !string.IsNullOrEmpty( name ) ) { + var m = new Modifier( name, defProfile ); + if ( !Contains( m ) ) + Add( m ); + } + } while ( reader.Read( ) ); + + return true; + } + catch ( Exception ex ) { + // get any exceptions from reading + return false; + } + + } + + + /// + /// Returns the XML string of Non Profile items + /// + /// An XML string + public string ToXML() + { + if ( UserCount <= 0 ) return ""; + + string r = string.Format( "\t\n" ); + foreach ( Modifier m in this ) { + if ( !m.DefaultProfile ) + r += m.ToXML( ); + } + r += string.Format( "\t\n\n" ); + return r; + } + + } +} diff --git a/SC/SCDefaultProfile.cs b/SC/SCDefaultProfile.cs index 82e149f..f3c3405 100644 --- a/SC/SCDefaultProfile.cs +++ b/SC/SCDefaultProfile.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using Ionic.Zip; using System.IO; using SCJMapper_V2.CryXMLlib; @@ -18,17 +17,19 @@ namespace SCJMapper_V2.SC { private static readonly log4net.ILog log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod( ).DeclaringType ); + private static string m_defProfileCached = ""; // cache... + /// /// Ref to the used defaultProfile to inform the user about it. /// - public static String UsedDefProfile = "n.a."; + public static string UsedDefProfile = "n.a."; /// /// Returns a list of files found that match 'defaultProfile*.xml' /// 20151220BM: return only the single defaultProfile name /// /// A list of filenames - can be empty - static public String DefaultProfileName + static public string DefaultProfileName { get { return "defaultProfile.xml"; } } @@ -37,140 +38,95 @@ namespace SCJMapper_V2.SC /// Returns the sought default profile as string from various locations /// SC Alpha 2.2: Have to find the new one in E:\G\StarCitizen\StarCitizen\Public\Data\DataXML.pak (contains the binary XML now) /// - /// The filename of the profile to be extracted /// A string containing the file contents - static public String DefaultProfile( String defaultProfileName ) + static public string DefaultProfile() { log.Debug( "DefaultProfile - Entry" ); - String retVal = ""; + string retVal = m_defProfileCached; + if ( !string.IsNullOrEmpty( retVal ) ) return retVal; // Return cached defaultProfile - // first choice a defaultProfile.xml in the app dir distributed with the application ??? to be deleted ??? - if ( File.Exists( defaultProfileName ) ) { // 20170404 - use the given name, not the one from SCPATH... - using ( StreamReader sr = new StreamReader( defaultProfileName ) ) { + // first choice a defaultProfile.xml in the app dir distributed with the application ??? to be deleted ??? + if ( File.Exists( DefaultProfileName ) ) { // 20170404 - use the given name, not the one from SCPATH... + using ( StreamReader sr = new StreamReader( DefaultProfileName ) ) { retVal = sr.ReadToEnd( ); UsedDefProfile = "AppDirectory defaultProfile"; log.InfoFormat( "- Use {0}", UsedDefProfile ); - + m_defProfileCached = retVal; return retVal; // EXIT } } - // second try to get the SC defaultProfile from the DataXML.pak - string patchProfile = Path.Combine( SCPath.SCClientUSERPath, defaultProfileName ); + // second try to get the SC defaultProfile ..\USER\defaultProfile.xml + string patchProfile = Path.Combine( SCPath.SCClientUSERPath, DefaultProfileName ); if ( File.Exists( patchProfile ) ) { // 20171126 PTU patch location in ..\USER\defaultProfile.xml using ( StreamReader sr = new StreamReader( patchProfile ) ) { retVal = sr.ReadToEnd( ); UsedDefProfile = "USER Directory defaultProfile.xml"; log.InfoFormat( "- Use {0}", UsedDefProfile ); - + m_defProfileCached = retVal; return retVal; // EXIT } } // PTU 3.0 those cannot longer work - but let them in for a while - // second try to get the SC defaultProfile from the DataXML.pak - retVal = ExtractDefaultBinProfile( defaultProfileName ); - if ( !String.IsNullOrEmpty( retVal ) ) { - UsedDefProfile = "DataXML defaultProfile"; - log.InfoFormat( "- Use {0}", UsedDefProfile ); - return retVal; // EXIT - } - // third try to get the SC defaultProfile from the GameData.pak - retVal = ExtractDefaultProfile( defaultProfileName ); - if ( !String.IsNullOrEmpty( retVal ) ) { + // third try to get the SC defaultProfile from the Data.p4k + retVal = ExtractDefaultBinProfileP4k( DefaultProfileName ); + if ( !string.IsNullOrEmpty( retVal ) ) { UsedDefProfile = "GamePack defaultProfile"; log.InfoFormat( "- Use {0}", UsedDefProfile ); + m_defProfileCached = retVal; return retVal; // EXIT } // last resort is the built in one - retVal = SCJMapper_V2.Properties.Resources.defaultProfile; + retVal = Properties.Resources.defaultProfile; UsedDefProfile = "App Resource defaultProfile"; log.InfoFormat( "- Use {0}", UsedDefProfile ); + m_defProfileCached = retVal; return retVal; // EXIT } /// /// Zip Extracts the file to a string - /// SC Alpha 2.2: Have to find the new one in E:\G\StarCitizen\StarCitizen\Public\Data\DataXML.pak (contains the binary XML now) + /// SC Alpha 2.2: Have to find the new one in E:\G\StarCitizen\StarCitizen\LIVE\Data.p4k (contains the binary XML now) /// - static private String ExtractDefaultBinProfile( String defaultProfileName ) + static private string ExtractDefaultBinProfileP4k( string defaultProfileName ) { - log.Debug( "ExtractDefaultBinProfile - Entry" ); - - String retVal = ""; - if ( File.Exists( SCPath.SCDataXML_pak ) ) { - using ( ZipFile zip = ZipFile.Read( SCPath.SCDataXML_pak ) ) { - zip.CaseSensitiveRetrieval = false; - try { - ICollection gdpak = zip.SelectEntries( "name = " + "'" + defaultProfileName + "'", SCPath.DefaultProfilePath_rel ); - if ( gdpak != null ) { - try { - MemoryStream mst = new MemoryStream( ); - gdpak.FirstOrDefault( ).Extract( mst ); - // use the binary XML reader - CryXmlNodeRef ROOT = null; - CryXmlBinReader.EResult readResult = CryXmlBinReader.EResult.Error; - CryXmlBinReader cbr = new CryXmlBinReader( ); - - ROOT = cbr.LoadFromBuffer( mst.ToArray( ), out readResult ); - if ( readResult == CryXmlBinReader.EResult.Success ) { - XmlTree tree = new XmlTree( ); - tree.BuildXML( ROOT ); - retVal = tree.XML_string; - } - else { - log.ErrorFormat( " Error in CryXmlBinReader: {0}", cbr.GetErrorDescription() ); - retVal = ""; // clear any remanents - } - } - catch { - retVal = ""; // clear any remanents - } + log.Debug( "ExtractDefaultBinProfileP4k - Entry" ); + + string retVal = ""; + if ( File.Exists( SCPath.SCData_p4k ) ) { + try { + var PD = new p4kFile.p4kDirectory( ); + p4kFile.p4kFile p4K = PD.ScanDirectoryFor( SCPath.SCData_p4k, defaultProfileName ); + if ( p4K != null ) { + byte[] fContent = PD.GetFile( SCPath.SCData_p4k, p4K ); + + // use the binary XML reader + CryXmlNodeRef ROOT = null; + CryXmlBinReader.EResult readResult = CryXmlBinReader.EResult.Error; + CryXmlBinReader cbr = new CryXmlBinReader( ); + ROOT = cbr.LoadFromBuffer( fContent, out readResult ); + if ( readResult == CryXmlBinReader.EResult.Success ) { + XmlTree tree = new XmlTree( ); + tree.BuildXML( ROOT ); + retVal = tree.XML_string; + } + else { + log.ErrorFormat( " Error in CryXmlBinReader: {0}", cbr.GetErrorDescription( ) ); + retVal = ""; // clear any remanents } - } - catch ( Exception ex ) { - log.Error( " Unexpected ", ex ); - } - } - } - return retVal; - } - /// - /// Zip Extracts the file to a string - /// - static private String ExtractDefaultProfile( String defaultProfileName ) - { - log.Debug( "ExtractDefaultProfile - Entry" ); - - String retVal = ""; - if ( File.Exists( SCPath.SCGameData_pak ) ) { - using ( ZipFile zip = ZipFile.Read( SCPath.SCGameData_pak ) ) { - zip.CaseSensitiveRetrieval = false; - try { - ICollection gdpak = zip.SelectEntries( "name = " + "'" + defaultProfileName + "'", SCPath.DefaultProfilePath_rel ); - if ( gdpak != null ) { - try { - MemoryStream mst = new MemoryStream( ); - gdpak.FirstOrDefault( ).Extract( mst ); - UTF8Encoding unc = new UTF8Encoding( ); - retVal = unc.GetString( mst.ToArray( ) ); - } - catch { - retVal = ""; // clear any remanents - } - } - } - catch ( Exception ex ) { - log.Error( " Unexpected", ex ); } - } + catch ( Exception ex ) { + log.Error( " Unexpected ", ex ); + } + } return retVal; } diff --git a/SC/SCLogExtract.cs b/SC/SCLogExtract.cs index 5f4ef4c..d418285 100644 --- a/SC/SCLogExtract.cs +++ b/SC/SCLogExtract.cs @@ -19,6 +19,7 @@ namespace SCJMapper_V2.SC if ( l.StartsWith( "log started" ) ) return String.Format("\t{0}\n", inLine); if ( l.StartsWith( "executable:" ) ) return String.Format( "\t{0}\n", inLine ); if ( l.StartsWith( "productversion" ) ) return String.Format( "\t{0}\n", inLine ); + if ( l.StartsWith( "host cpu" ) ) return String.Format( "\t{0}\n", inLine ); if ( l.StartsWith( "windows:" ) ) return String.Format( "\t{0}\n", inLine ); diff --git a/SC/SCPath.cs b/SC/SCPath.cs index ef2e7c0..6f376fb 100644 --- a/SC/SCPath.cs +++ b/SC/SCPath.cs @@ -18,95 +18,24 @@ namespace SCJMapper_V2.SC private static bool hasInformed = false; // prevent msgbox chains.. /// - /// Try to locate the launcher under "App Paths" + /// Try to locate the launcher from Alpha 3.0.0 public - e.g. E:\G\StarCitizen\RSI Launcher /// - static private String SCLauncherFile1 + static private String SCLauncherDir6 { get { - log.Debug( "SCLauncherFile1 - Entry" ); - String scLauncher = (String)Registry.GetValue( @"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\App Paths\StarCitizen Launcher.exe", "", null ); + log.Debug( "SCLauncherDir6 - Entry" ); + String scLauncher = (String)Registry.GetValue( @"HKEY_LOCAL_MACHINE\SOFTWARE\81bfc699-f883-50c7-b674-2483b6baae23", "InstallLocation", null ); if ( scLauncher != null ) { - log.Info( "SCLauncherFile1 - Found HKLM - AppPath - Launcher.exe" ); - if ( File.Exists( scLauncher ) ) { - return scLauncher; - } - else { - log.WarnFormat( "SCLauncherFile1 - file does not exist: {0}", scLauncher ); - return ""; - } - } - log.Warn( "SCLauncherFile1 - did not found HKLM - AppPath - Launcher.exe" ); - return ""; - } - } - - /// - /// Try to locate the launcher under "Uninstall" - /// - static private String SCLauncherFile2 - { - get { - log.Debug( "SCLauncherFile2 - Entry" ); - String scLauncher = (String)Registry.GetValue( @"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\StarCitizen", "DisplayIcon", null ); - if ( scLauncher != null ) { - log.Info( "SCLauncherFile2 - Found HKLM - Uninstall - StarCitizen" ); - if ( File.Exists( scLauncher ) ) { - return scLauncher; - } - else { - log.WarnFormat( "SCLauncherFile2 - file does not exist: {0}", scLauncher ); - return ""; - } - } - log.Warn( "SCLauncherFile2 - did not found HKLM - Uninstall - StarCitizen" ); - return ""; - } - } - - - /// - /// Try to locate the launcher under "Uninstall" - /// - static private String SCLauncherFile3 - { - get { - log.Debug( "SCLauncherFile3 - Entry" ); - String scLauncher = (String)Registry.GetValue( @"HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Cloud Imperium Games\StarCitizen Launcher.exe", "", null ); - if ( scLauncher != null ) { - log.Info( "SCLauncherFile3 - Found HKLM - CIG - Launcher.exe" ); - if ( File.Exists( scLauncher ) ) { - return scLauncher; - } - else { - log.WarnFormat( "SCLauncherFile3 - file does not exist: {0}", scLauncher ); - return ""; - } - } - log.Warn( "SCLauncherFile3 - did not found HKLM - CIG - Launcher.exe" ); - return ""; - } - } - - - /// - /// Try to locate the launcher from Alpha 1.1.6 - e.g. E:\G\StarCitizen\CIGLauncher.exe - /// - static private String SCLauncherFile4 - { - get { - log.Debug( "SCLauncherFile4 - Entry" ); - String scLauncher = (String)Registry.GetValue( @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths\CIGLauncher.exe", "", null ); - if ( scLauncher != null ) { - log.Info( "SCLauncherFile4 - Found HKCU - CIGLauncher.exe" ); - if ( File.Exists( scLauncher ) ) { + log.Info( "SCLauncherDir6 - Found HKLM -InstallLocation" ); + if ( Directory.Exists( scLauncher ) ) { return scLauncher; } else { - log.WarnFormat( "SCLauncherFile4 - file does not exist: {0}", scLauncher ); + log.WarnFormat( "SCLauncherDir6 - directory does not exist: {0}", scLauncher ); return ""; } } - log.Warn( "SCLauncherFile4 - did not found HKCU - CIGLauncher.exe" ); + log.Warn( "SCLauncherDir6 - did not found HKLM - InstallLocation" ); return ""; } } @@ -120,7 +49,7 @@ namespace SCJMapper_V2.SC log.Debug( "SCLauncherDir5 - Entry" ); String scLauncher = (String)Registry.GetValue( @"HKEY_LOCAL_MACHINE\SOFTWARE\94a6df8a-d3f9-558d-bb04-097c192530b9", "InstallLocation", null ); if ( scLauncher != null ) { - log.Info( "SCLauncherDir5 - Found HKLM -InstallLocation" ); + log.Info( "SCLauncherDir5 - Found HKLM -InstallLocation (PTU)" ); if ( Directory.Exists( scLauncher ) ) { return scLauncher; } @@ -139,7 +68,7 @@ namespace SCJMapper_V2.SC /// - /// Returns the base SC install path from something like "E:\G\StarCitizen\Launcher\StarCitizenLauncher.exe" + /// Returns the base SC install path from something like "E:\G\StarCitizen" /// static private String SCBasePath { @@ -148,8 +77,8 @@ namespace SCJMapper_V2.SC appSettings.Reload( ); // local instance - reload as it might be changed outside String scp = ""; + // User setting has Prio if ( appSettings.UserSCPathUsed ) { - // User has priority scp = appSettings.UserSCPath; log.InfoFormat( "SCBasePath - user defined folder given: {0}", scp ); #if DEBUG @@ -177,65 +106,24 @@ namespace SCJMapper_V2.SC else { // start the registry search - sequence 5..1 to get the newest method first - scp = SCLauncherDir5; + scp = SCLauncherDir6; // 3.0 Public Launcher #if DEBUG //*************************************** //scp = ""; // TEST not found (COMMENT OUT FOR PRODUCTIVE BUILD) //*************************************** #endif if ( !string.IsNullOrEmpty( scp ) ) { - // AC 1.1.6 path OK - this one needs no adjustments anymore but removing the filename scp = Path.GetDirectoryName( scp ); // "E:\G\StarCitizen" return scp; } - scp = SCLauncherFile4; + scp = SCLauncherDir5; // 3.0 PTU Launcher #if DEBUG //*************************************** //scp = ""; // TEST not found (COMMENT OUT FOR PRODUCTIVE BUILD) //*************************************** #endif if ( !string.IsNullOrEmpty( scp ) ) { - // AC 1.1.6 path OK - this one needs no adjustments anymore but removing the filename - scp = Path.GetDirectoryName( scp ); // "E:\G\StarCitizen" - return scp; - } - - scp = SCLauncherFile3; -#if DEBUG - //*************************************** - //scp = ""; // TEST not found (COMMENT OUT FOR PRODUCTIVE BUILD) - //*************************************** -#endif - if ( !string.IsNullOrEmpty( scp ) ) { - // found the launcher.exe file - path adjust for the old subdir (may be remove path find 1..3 later) - scp = Path.GetDirectoryName( scp ); // "E:\G\StarCitizen\Launcher" - scp = Path.GetDirectoryName( scp ); // "E:\G\StarCitizen" - return scp; - } - - scp = SCLauncherFile2; -#if DEBUG - //*************************************** - //scp = ""; // TEST not found (COMMENT OUT FOR PRODUCTIVE BUILD) - //*************************************** -#endif - if ( !string.IsNullOrEmpty( scp ) ) { - // found the launcher.exe file - path adjust for the old subdir (may be remove path find 1..3 later) - scp = Path.GetDirectoryName( scp ); // "E:\G\StarCitizen\Launcher" - scp = Path.GetDirectoryName( scp ); // "E:\G\StarCitizen" - return scp; - } - - scp = SCLauncherFile1; -#if DEBUG - //*************************************** - //scp = ""; // TEST not found (COMMENT OUT FOR PRODUCTIVE BUILD) - //*************************************** -#endif - if ( !string.IsNullOrEmpty( scp ) ) { - // found the launcher.exe file - path adjust for the old subdir (may be remove path find 1..3 later) - scp = Path.GetDirectoryName( scp ); // "E:\G\StarCitizen\Launcher" scp = Path.GetDirectoryName( scp ); // "E:\G\StarCitizen" return scp; } @@ -274,9 +162,7 @@ namespace SCJMapper_V2.SC /// /// Returns the SC Client path - /// AC 1.1.6: E:\G\StarCitizen\StarCitizen\Public - /// SC 2x: alternatively use PTU path E:\G\StarCitizen\StarCitizen\Test - /// SC 2.2.2: alternatively search path in E:\G\StarCitizen\StarCitizen\Live (don't know but this was mentioned in CIGs relnotes lately) + /// SC 3.0.0: search path like E:\G\StarCitizen\StarCitizen\LIVE /// static public String SCClientPath { @@ -294,23 +180,18 @@ namespace SCJMapper_V2.SC // scp = Path.Combine( scp, "StarCitizen" ); string scpX = ""; - // regular game folder - scpX = Path.Combine( scp, "Public" ); - if ( Directory.Exists( scpX ) ) return scpX; - - // SC 2.2.2+ did not found it so try Live now + // SC 3.0 try LIVE scpX = Path.Combine( scp, "LIVE" ); if ( Directory.Exists( scpX ) ) return scpX; // Issue a warning here to let the user know issue = string.Format( "Cannot find the SC Client Path !!\n\n" + "Tried to look for:\n" + - "{0}\\Public or \n" + "{0}\\LIVE \n" + "The program cannot load or save in GameFolders\n\n" + "Please submit a bug report, adding your complete SC game folder structure", scp ); - log.WarnFormat( "SCClientPath - StarCitizen\\Public, StarCitizen\\Live subfolder does not exist: {0}", scp ); + log.WarnFormat( "SCClientPath - StarCitizen\\Live subfolder does not exist: {0}", scp ); // Issue a warning here to let the user know if ( !hasInformed ) System.Windows.Forms.MessageBox.Show( issue, "Cannot find SC Client Path !!", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Exclamation ); @@ -323,7 +204,7 @@ namespace SCJMapper_V2.SC /// /// Returns the SC ClientData path - /// AC 1.1.6: E:\G\StarCitizen\StarCitizen\Public\Data + /// AC 3.0: E:\G\StarCitizen\StarCitizen\LIVE\Data /// static public String SCClientDataPath { @@ -340,7 +221,7 @@ namespace SCJMapper_V2.SC #endif if ( Directory.Exists( scp ) ) return scp; - log.WarnFormat( "SCClientDataPath - StarCitizen\\Public\\Data subfolder does not exist: {0}", scp ); + log.WarnFormat( "SCClientDataPath - StarCitizen\\LIVE\\Data subfolder does not exist: {0}", scp ); return ""; } } @@ -348,7 +229,7 @@ namespace SCJMapper_V2.SC /// /// Returns the SC ClientData path - /// AC 1.1.6: E:\G\StarCitizen\StarCitizen\Public\USER + /// AC 3.0: E:\G\StarCitizen\StarCitizen\LIVE\USER /// static public String SCClientUSERPath { @@ -365,7 +246,7 @@ namespace SCJMapper_V2.SC #endif if ( Directory.Exists( scp ) ) return scp; - log.WarnFormat( "SCClientUSERPath - StarCitizen\\Public\\USER subfolder does not exist: {0}", scp ); + log.WarnFormat( "SCClientUSERPath - StarCitizen\\LIVE\\USER subfolder does not exist: {0}", scp ); return ""; } } @@ -388,7 +269,7 @@ namespace SCJMapper_V2.SC #endif if ( Directory.Exists( scp ) ) return scp; - log.WarnFormat( "SCClientLogsPath - StarCitizen\\Public subfolder does not exist: {0}", scp ); + log.WarnFormat( "SCClientLogsPath - StarCitizen\\LIVE subfolder does not exist: {0}", scp ); return ""; } } @@ -414,49 +295,24 @@ namespace SCJMapper_V2.SC #endif if ( Directory.Exists( scp ) ) return scp; - log.WarnFormat( "SCClientMappingPath - StarCitizen\\Public\\USER\\Controls\\Mappings subfolder does not exist: {0}", scp ); - return ""; - } - } - - - /// - /// Returns the SC GameData.pak file path - /// AC 1.1.6: E:\G\StarCitizen\StarCitizen\Public\Data\GameData.pak - /// - static public String SCGameData_pak - { - get { - log.Debug( "SCGameData_pak - Entry" ); - String scp = SCClientDataPath; - if ( String.IsNullOrEmpty( scp ) ) return ""; - // - scp = Path.Combine( scp, "GameData.pak" ); -#if DEBUG - //*************************************** - // scp += "X"; // TEST not found (COMMENT OUT FOR PRODUCTIVE BUILD) - //*************************************** -#endif - if ( File.Exists( scp ) ) return scp; - - log.WarnFormat( "SCGameData_pak - StarCitizen\\Public\\Data\\GameData.pak file does not exist: {0}", scp ); + log.WarnFormat( "SCClientMappingPath - StarCitizen\\LIVE\\USER\\Controls\\Mappings subfolder does not exist: {0}", scp ); return ""; } } /// - /// Returns the SC DataXML.pak file path - /// SC Alpha 2.2: E:\G\StarCitizen\StarCitizen\Public\Data\DataXML.pak (contains the binary XML now) + /// Returns the SC Data.p4k file path + /// SC Alpha 3.0: E:\G\StarCitizen\StarCitizen\LIVE\Data.p4k (contains the binary XML now) /// - static public String SCDataXML_pak + static public String SCData_p4k { get { - log.Debug( "SCDataXML_pak - Entry" ); - String scp = SCClientDataPath; + log.Debug( "SCDataXML_p4k - Entry" ); + String scp = SCClientPath; if ( String.IsNullOrEmpty( scp ) ) return ""; // - scp = Path.Combine( scp, "DataXML.pak" ); + scp = Path.Combine( scp, "Data.p4k" ); #if DEBUG //*************************************** // scp += "X"; // TEST not found (COMMENT OUT FOR PRODUCTIVE BUILD) @@ -464,7 +320,7 @@ namespace SCJMapper_V2.SC #endif if ( File.Exists( scp ) ) return scp; - log.WarnFormat( "SCDataXML_pak - StarCitizen\\Public\\Data\\DataXML.pak file does not exist: {0}", scp ); + log.WarnFormat( "SCData_p4k - StarCitizen\\Public\\Data\\Data.p4k file does not exist: {0}", scp ); return ""; } } diff --git a/SC/p4kFile/p4kDirectory.cs b/SC/p4kFile/p4kDirectory.cs new file mode 100644 index 0000000..922b5a8 --- /dev/null +++ b/SC/p4kFile/p4kDirectory.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +namespace SCJMapper_V2.p4kFile +{ + + + /// + /// Limited Directory scanner for p4k files + /// + public class p4kDirectory + { + // 4.3.6 Overall.ZIP file format: + //[local file header 1] + //[encryption header 1] + //[file data 1] + //[data descriptor 1] + // . + // . + // . + //[local file header n] + //[encryption header n] + //[file data n] + //[data descriptor n] + + //[archive decryption header] + //[archive extra data record] + + //[central directory header 1] + // . + // . + // . + //[central directory header n] + //[zip64 end of central directory record] + //[zip64 end of central directory locator] + //[end of central directory record] + + + /// + /// Retrieve the file given by the descriptor (from our list) + /// and return the content as string + /// + /// A file descriptor from this list + /// The content of the file or an empty string + public byte[] GetFile( string p4kFilename, p4kFile file ) + { + if ( !File.Exists( p4kFilename ) ) return new byte[] { }; + + using ( p4kRecReader reader = new p4kRecReader( p4kFilename ) ) { + return file.GetFile( reader ); + } + } + + // scans file directory entries + // finds the directory structs and processes from there + + private p4kEndOfCentralDirRecord m_endOfCentralDirRecord = null; + private p4kZ64EndOfCentralDirLocator m_z64EndOfCentralDirLocator = null; + private p4kZ64EndOfCentralDirRecord m_z64EndOfCentralDirRecord = null; + + /// + /// Scans directory entries and extract a file (string.EndsWith is used) + /// + /// The p4k file + /// The filename to look for + /// A BackgroundWorker object + public p4kFile ScanDirectoryFor( string p4kFilename, string filename ) + { + if ( !File.Exists( p4kFilename ) ) return null; + + using ( p4kRecReader reader = new p4kRecReader( p4kFilename ) ) { + // work from the end of the file + reader.GotoLastPage( ); + m_endOfCentralDirRecord = new p4kEndOfCentralDirRecord( reader ); + + // position first + reader.Seek( m_endOfCentralDirRecord.RecordOffset - p4kRecReader.PageSize ); + m_z64EndOfCentralDirLocator = new p4kZ64EndOfCentralDirLocator( reader ); + + // for the next the position should be found already - seek it + reader.Seek( m_z64EndOfCentralDirLocator.Z64EndOfCentralDir ); + m_z64EndOfCentralDirRecord = new p4kZ64EndOfCentralDirRecord( reader ); + // now we should have the start of the directory entries... + + + // position first + reader.Seek( m_z64EndOfCentralDirRecord.Z64StartOfCentralDir ); + // loop all file - as per dir reporting + for ( long i = 0; i < m_z64EndOfCentralDirRecord.NumberOfEntries; i++ ) { + p4kDirectoryEntry de = new p4kDirectoryEntry( reader ); + if ( !string.IsNullOrEmpty( filename ) && de.Filename.ToLower( ).EndsWith( filename.ToLower( ) ) ) { + var p = new p4kFile( de ); // FOUND + reader.TheReader.Close( ); + return p; // bail out if found + } + } + } + return null; + } + + + } +} diff --git a/SC/p4kFile/p4kDirectoryEntry.cs b/SC/p4kFile/p4kDirectoryEntry.cs new file mode 100644 index 0000000..c314e89 --- /dev/null +++ b/SC/p4kFile/p4kDirectoryEntry.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace SCJMapper_V2.p4kFile +{ + /// + /// Represents a Directory entry in the p4k file + /// The file seems to be based on a Zip64 file structure + /// + internal class p4kDirectoryEntry + { + + /// + /// ctor: Create class from data returned by the Reader; starts reading at current stream position + /// + /// A binary data reader for this type of data - positioned already + public p4kDirectoryEntry( p4kRecReader reader ) + { + // sanity check only + System.Diagnostics.Trace.Assert( Marshal.SizeOf( typeof( MyRecord ) ) == RecordLength, + "Record size does not match!(" + Marshal.SizeOf( typeof( MyRecord ) ).ToString( ) + ")" ); + System.Diagnostics.Trace.Assert( Marshal.SizeOf( typeof( MyZ64ExtraRecord ) ) == Z64ExtraRecordLength, + "Extra Record size does not match!(" + Marshal.SizeOf( typeof( MyZ64ExtraRecord ) ).ToString( ) + ")" ); + + if ( reader.IsOpen( ) ) { + try { + // get the next Directory record + long cPos = p4kSignatures.FindSignatureInPage( reader, p4kSignatures.CentralDirRecord ); + if ( cPos >= 0 ) { + m_recordOffset = cPos; + reader.Seek( cPos ); + m_item = p4kRecReader.ByteToType( reader.TheReader ); + m_itemValid = true; // implicite from Find.. + } + + // get some file attributes + if ( m_itemValid ) { + if ( m_item.FilenameLength > 0 ) { + ReadFilename( reader ); + } + if ( m_item.ExtraFieldLength > 0 ) { + ReadExtradata( reader ); // Likely Zip64 extensions + } + m_fileDateTime = p4kFileTStamp.FromDos( m_item.LastModDate, m_item.LastModTime ); // get the file time given in the container + + // check if standard fields or extension is used for size information + if ( m_item.CompressedSize < 0xffffffff ) { + m_fileSizeComp = m_item.CompressedSize; + m_fileSizeUnComp = m_item.UncompressedSize; + } + else { + m_fileSizeComp = (long)m_z64Item.CompressedSize; + m_fileSizeUnComp = (long)m_z64Item.UncompressedSize; + } + } + } + catch (Exception e) { + m_itemValid = false; + m_recordOffset = -1; + } + finally { + if ( !m_itemValid ) { + throw new OperationCanceledException( string.Format( "EOF - cannot find CentralDirRec in this page" ) ); + } + } + } + } + + /// + /// Get the filename from the data + /// + /// The open and positioned reader + private void ReadFilename( p4kRecReader reader ) + { + byte[] fileNameBytes = new byte[m_item.FilenameLength]; + fileNameBytes = reader.ReadBytes( m_item.FilenameLength ); + m_filename = Encoding.ASCII.GetString( fileNameBytes ); + } + + /// + /// Read the extra data + /// + /// + private void ReadExtradata( p4kRecReader reader ) + { + // first the Zip64 extra record + m_z64Item = p4kRecReader.ByteToType( reader.TheReader ); + // then the rest of the extra record (is another item with tag 0x0666 and the rest lenght (ignored) + m_extraBytes = new byte[m_item.ExtraFieldLength - Z64ExtraRecordLength]; + m_extraBytes = reader.ReadBytes( m_extraBytes.Length ); + m_extraBytes = null; // dump it ... + // now we would be able to read the file content + } + + /// + /// Return the file related to this entry + /// + /// An open p4k File reader + /// The content of the file or an empty string + public byte[] GetFile( p4kRecReader reader ) + { + if ( !m_itemValid ) return new byte[] { }; // ERROR cannot.. + + reader.Seek( FileHeaderOffset ); + p4kFileHeader p4Kfh = new p4kFileHeader( reader ); + return p4Kfh.GetFile( reader ); + } + + + public bool IsValid { get => m_itemValid; } + public long RecordOffset { get => m_recordOffset; } + public string Filename { get => m_filename; } + public long FileSizeComp { get => m_fileSizeComp; } + public long FileSizeUnComp { get => m_fileSizeUnComp; } + public DateTime FileModifyDate { get => m_fileDateTime; } + public long FileHeaderOffset + { + get { + if ( m_itemValid ) + return (long)m_z64Item.LocalHeaderOffset; + else + return -1; + } + } + + private MyRecord m_item; + private bool m_itemValid = false; + private MyZ64ExtraRecord m_z64Item; + private byte[] m_extraBytes = null; + private long m_recordOffset = -1; // offset from start (current position) + // Entry attributes + private string m_filename = ""; + private DateTime m_fileDateTime = new DateTime( 1970,1,1 ); + private long m_fileSizeComp = 0; + private long m_fileSizeUnComp = 0; + + // 4.3.12 Central directory structure: + private const int RecordLength = 46; + [StructLayout( LayoutKind.Sequential, Pack = 1 )] // , Size = RecordLength + private struct MyRecord + { + [MarshalAs( UnmanagedType.ByValArray, SizeConst = 4 )] + public byte[] ID; // central file header signature 4 bytes(0x02014b50) + [MarshalAs( UnmanagedType.U2 )] + public UInt16 VersionMadeBy; // version made by 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 ExtractVersion; // version made by 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 BitFlags; // general purpose bit flag 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 CompressionMethod; // compression method 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 LastModTime; // last mod file time 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 LastModDate; // last mod file date 2 bytes + [MarshalAs( UnmanagedType.U4 )] + public UInt32 CRC32; // crc-32 4 bytes + [MarshalAs( UnmanagedType.U4 )] + public UInt32 CompressedSize; // compressed size 4 bytes + [MarshalAs( UnmanagedType.U4 )] + public UInt32 UncompressedSize; // uncompressed size 4 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 FilenameLength; // file name length 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 ExtraFieldLength; // extra field length 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 FilecommentLength; // file comment length 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 DiskNumberStart; // disk number start 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 IntFileAttr; // internal file attributes 2 bytes + [MarshalAs( UnmanagedType.U4 )] + public UInt32 ExtFileAttr; // external file attributes 4 bytes + [MarshalAs( UnmanagedType.U4 )] + public UInt32 RelOffsetHeader; // relative offset of local header 4 bytes + // file name( variable size ) + // extra field( variable size ) + // file comment( variable size ) + } + + + private const int Z64ExtraRecordLength = 32; + [StructLayout( LayoutKind.Sequential, Pack = 1 )] // , Size = RecordLength + private struct MyZ64ExtraRecord + { + [MarshalAs( UnmanagedType.U2 )] + public UInt16 ID; // (Zip64 ExtraHeader Signature) + [MarshalAs( UnmanagedType.U2 )] + public UInt16 Size; // Size 2 bytes Size of this "extra" block + [MarshalAs( UnmanagedType.U8 )] + public UInt64 UncompressedSize; // Original Size 8 bytes Original uncompressed file size + [MarshalAs( UnmanagedType.U8 )] + public UInt64 CompressedSize; // Compressed Size 8 bytes Size of compressed data + [MarshalAs( UnmanagedType.U8 )] + public UInt64 LocalHeaderOffset; // Relative Header Offset 8 bytes Offset of local header record + [MarshalAs( UnmanagedType.U4 )] + public UInt32 DiskStart; // Disk Start Number 4 bytes Number of the disk on which this file starts + } + + + } +} diff --git a/SC/p4kFile/p4kEndOfCentralDirRecord.cs b/SC/p4kFile/p4kEndOfCentralDirRecord.cs new file mode 100644 index 0000000..483f2d2 --- /dev/null +++ b/SC/p4kFile/p4kEndOfCentralDirRecord.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace SCJMapper_V2.p4kFile +{ + /// + /// Represents an EndOfCentralDirRecord entry in the p4k file + /// The file seems to be based on a Zip64 file structure + /// + internal class p4kEndOfCentralDirRecord + { + + /// + /// ctor: Create class from data returned by the Reader + /// + /// A binary data reader for this type of data - positioned at last page + public p4kEndOfCentralDirRecord( p4kRecReader reader ) + { + // sanity check only + System.Diagnostics.Trace.Assert( Marshal.SizeOf( typeof( MyRecord ) ) == RecordLength, + "Record size does not match!(" + Marshal.SizeOf( typeof( MyRecord ) ).ToString( ) + ")" ); + + if ( reader.IsOpen( ) ) { + try { + long cPos = p4kSignatures.FindSignatureInPage( reader, p4kSignatures.EndOfCentralDirRecord ); + if ( cPos >= 0 ) { + m_recordOffset = cPos; + reader.Seek( cPos ); + m_item = p4kRecReader.ByteToType( reader.TheReader ); + m_itemValid = true; + } + } + catch { + m_itemValid = false; + m_recordOffset = -1; + } + finally { + if ( !m_itemValid ) { + throw new OperationCanceledException( string.Format( "EOF - cannot find EndOfCentralDirRecord" ) ); + } + } + } + } + + public bool IsValid { get => m_itemValid; } + public long RecordOffset { get => m_recordOffset; } + + + private MyRecord m_item; + private bool m_itemValid = false; + private long m_recordOffset = -1; // offset from start (current position) + + //4.3.16 End of central directory record: + private const int RecordLength = 22; + [StructLayout( LayoutKind.Sequential, Pack = 1 )] // , Size = RecordLength + private struct MyRecord + { + [MarshalAs( UnmanagedType.ByValArray, SizeConst = 4 )] + public byte[] ID; // end of central dir signature 4 bytes(0x06054b50) + [MarshalAs( UnmanagedType.U2 )] + public UInt16 DiskNumber; // number of this disk 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 DiskNumbersFromStart; // number of the disk with the start of the central directory 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 NumEntriesOnDisk; // total number of entries in the central directory on this disk 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 TotalNumEntries; // total number of entries in the central directory 2 bytes + [MarshalAs( UnmanagedType.U4 )] + public UInt32 SizeOfCDir; // size of the central directory 4 bytes + [MarshalAs( UnmanagedType.U4 )] + public UInt32 OffsetOfCDir; // offset of start of central directory with respect to the starting disk number 4 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 CommentLen; // .ZIP file comment length 2 bytes + // .ZIP file comment( variable size ) + } + + + + } +} diff --git a/SC/p4kFile/p4kFile.cs b/SC/p4kFile/p4kFile.cs new file mode 100644 index 0000000..b4676c1 --- /dev/null +++ b/SC/p4kFile/p4kFile.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SCJMapper_V2.p4kFile +{ + /// + /// General File descriptor - knows about locations and can extract files + /// + public class p4kFile + { + // File Properties + private string m_filename = ""; + public string Filename { get => m_filename; set => m_filename = value; } + + private long m_fileHeaderOffset = -1; // file location of the fileheader in the p4k file + public long FileHeaderPosition { get => m_fileHeaderOffset; } + + private long m_compressedSize = -1; + public long CompressedSize { get => m_compressedSize; } + + private long m_uncompressedSize = -1; + public long FileSize { get => m_uncompressedSize; } + + private DateTime m_fileDateTime = new DateTime( 1970, 1, 1 ); + public DateTime FileModifyDate { get => m_fileDateTime; } + + private string m_fileDateTimeS = new DateTime( 1970, 1, 1 ).ToString("s"); // pre fabricated is faster to sort than always creating the string... + public string FileModifyDateS { get => m_fileDateTimeS; } + + + + + + /// + /// cTor: from fileHeader + /// + /// A File Header + internal p4kFile( p4kFileHeader fileHeader ) + { + Filename = fileHeader.Filename; + m_compressedSize = fileHeader.FileSizeComp; + m_uncompressedSize = fileHeader.FileSizeUnComp; + m_fileDateTime = fileHeader.FileModifyDate; + m_fileDateTimeS = m_fileDateTime.ToString( "s" ); + m_fileHeaderOffset = fileHeader.RecordOffset; + } + + + /// + /// cTor: from Directory Entry + /// + /// A Directory Entry + internal p4kFile( p4kDirectoryEntry dirEntry ) + { + Filename = dirEntry.Filename; + m_compressedSize = dirEntry.FileSizeComp; + m_uncompressedSize = dirEntry.FileSizeUnComp; + m_fileDateTime = dirEntry.FileModifyDate; + m_fileDateTimeS = m_fileDateTime.ToString( "s" ); + m_fileHeaderOffset = dirEntry.FileHeaderOffset; + } + + public p4kFile() + { + } + + + /// + /// Return the file related to this entry + /// + /// An open p4k File reader + /// The content of the file or an empty string + internal byte[] GetFile( p4kRecReader reader ) + { + if ( ( m_fileHeaderOffset >= 0 ) && reader.IsOpen( ) ) { + // seek file header + reader.Seek( m_fileHeaderOffset ); + byte[] fContent = { }; + // re-create the header + using ( p4kFileHeader p4KFileHeader = new p4kFileHeader( reader ) ) { + // dump the file + fContent = p4KFileHeader.GetFile( reader ); + } + return fContent; + } + return new byte[] { }; + } + + } +} diff --git a/SC/p4kFile/p4kFile.csproj b/SC/p4kFile/p4kFile.csproj new file mode 100644 index 0000000..b450e3b --- /dev/null +++ b/SC/p4kFile/p4kFile.csproj @@ -0,0 +1,61 @@ + + + + + Debug + AnyCPU + {AAA80EE7-DE9C-4CE6-A6C9-5EAE48273FAE} + Library + Properties + p4kFile + p4kFile + v4.5.2 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + ..\packages\ZstdNet.1.3.1\lib\net45\ZstdNet.dll + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SC/p4kFile/p4kFileHeader.cs b/SC/p4kFile/p4kFileHeader.cs new file mode 100644 index 0000000..71eb6b5 --- /dev/null +++ b/SC/p4kFile/p4kFileHeader.cs @@ -0,0 +1,324 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using ZstdNet; + +namespace SCJMapper_V2.p4kFile +{ + /// + /// Represents a Fileheader entry in the p4k File + /// seems to be a Zip64 based file and therefore using those headers + /// + internal class p4kFileHeader : IDisposable + { + + //4.3.7 Local file header: + + // local file header signature 4 bytes(0x04034b50) + // version needed to extract 2 bytes + // general purpose bit flag 2 bytes + // compression method 2 bytes + // last mod file time 2 bytes + // last mod file date 2 bytes + // crc-32 4 bytes + // compressed size 4 bytes + // uncompressed size 4 bytes + // file name length 2 bytes + // extra field length 2 bytes + + // file name( variable size ) + // extra field( variable size ) + + //4.3.8 File data + + // Immediately following the local header for a file + // SHOULD be placed the compressed or stored data for the file. + // If the file is encrypted, the encryption header for the file + // SHOULD be placed after the local header and before the file + // data. The series of[local file header][encryption header] + // [file data][data descriptor] repeats for each file in the + // .ZIP archive. + + // Zero-byte files, directories, and other file types that + // contain no content MUST not include file data. + + // 4.5.3 -Zip64 Extended Information Extra Field(0x0001): + + // The following is the layout of the zip64 extended + // information "extra" block.If one of the size or + // offset fields in the Local or Central directory + // record is too small to hold the required data, + // a Zip64 extended information record is created. + // The order of the fields in the zip64 extended + // information record is fixed, but the fields MUST + // only appear if the corresponding Local or Central + // directory record field is set to 0xFFFF or 0xFFFFFFFF. + + // Note: all fields stored in Intel low - byte / high - byte order. + + // Value Size Description + // ---- - --------------- + //(ZIP64)0x0001 2 bytes Tag for this "extra" block type + // Size 2 bytes Size of this "extra" block + // Original + // Size 8 bytes Original uncompressed file size + // Compressed + // Size 8 bytes Size of compressed data + // Relative Header + // Offset 8 bytes Offset of local header record + // Disk Start + // Number 4 bytes Number of the disk on which + // this file starts + + // This entry in the Local header MUST include BOTH original + // and compressed file size fields.If encrypting the + // central directory and bit 13 of the general purpose bit + // flag is set indicating masking, the value stored in the + // Local Header for the original file size will be zero. + + /// + /// ctor: Create class from data returned by the Reader + /// + /// A binary data reader for this type of data + public p4kFileHeader( p4kRecReader reader ) + { + // sanity check only + System.Diagnostics.Trace.Assert( Marshal.SizeOf( typeof( MyRecord ) ) == RecordLength, + "Record size does not match!(" + Marshal.SizeOf( typeof( MyRecord ) ).ToString( ) + ")" ); + System.Diagnostics.Trace.Assert( Marshal.SizeOf( typeof( MyZ64ExtraRecord ) ) == Z64ExtraRecordLength, + "Extra Record size does not match!(" + Marshal.SizeOf( typeof( MyZ64ExtraRecord ) ).ToString( ) + ")" ); + + if ( reader.IsOpen( ) ) { + try { + long cPos = reader.Position; + do { + // Fileheaders are Page aligned - scan to find one + reader.AdvancePage( ); // to next page + cPos = reader.Position; + string cPosS = cPos.ToString( "X" ); + m_recordOffset = cPos; + m_item = p4kRecReader.ByteToType( reader.TheReader ); + m_itemValid = m_item.ID.SequenceEqual( p4kSignatures.LocalFileHeaderCry ); + } while ( ( cPos < reader.Length ) && !m_itemValid ); + + // get some file attributes + if ( m_itemValid ) { + if ( m_item.FilenameLength > 0 ) { + ReadFilename( reader ); + } + if ( m_item.ExtraFieldLength > 0 ) { + ReadExtradata( reader ); // Likely Zip64 extensions + } + m_fileDateTime = p4kFileTStamp.FromDos( m_item.LastModDate, m_item.LastModTime ); + + // check if standard fields or extension is used for size information + if ( m_item.CompressedSize < 0xffffffff ) { + m_fileSizeComp = m_item.CompressedSize; + m_fileSizeUnComp = m_item.UncompressedSize; + } + else { + m_fileSizeComp = (long)m_z64Item.CompressedSize; + m_fileSizeUnComp = (long)m_z64Item.UncompressedSize; + } + // now we would be able to read the file content + // but we skip it for now to process the next header + m_fileOffset = reader.TheReader.BaseStream.Position; // save position of this item + reader.TheReader.BaseStream.Seek( m_fileSizeComp, SeekOrigin.Current ); + } + else { + // actually invalid but good manner .. + m_recordOffset = -1; + m_fileOffset = -1; + m_fileSizeComp = 0; + m_fileSizeUnComp = 0; + } + + } + catch { + m_itemValid = false; + } + finally { + if ( !m_itemValid ) { + if ( m_item.ID.SequenceEqual( p4kSignatures.CentralDirRecord ) ) { + // read beyond the file entries + throw new OperationCanceledException( string.Format( "EOF - found Central Directory header {0}", m_item.ID.ToString( ) ) ); + } + else { + // other error + throw new NotSupportedException( string.Format( "Cannot process fileheader ID {0}", m_item.ID.ToString( ) ) ); + } + } + } + } + } + + + /// + /// Return the file related to this entry + /// + /// An open p4k File reader + /// The content of the file or an empty string + public byte[] GetFile( p4kRecReader reader ) + { + if ( !m_itemValid ) return new byte[] { }; // ERROR cannot.. + + reader.Seek( m_fileOffset ); + // ?? big files may have trouble here - may be we need to read and write chunks for this + // but for now we only want to get XMLs out and that is OK with byte alloc on the heap + byte[] fileBytes = new byte[m_fileSizeComp]; + fileBytes = reader.ReadBytes( fileBytes.Length ); + + byte[] decompFile = null; + if ( m_item.CompressionMethod == 0x64 ) { + // this indicates p4k ZStd compression + using ( var decompressor = new Decompressor( ) ) { + try { + decompFile = decompressor.Unwrap( fileBytes ); + return decompFile; + } + catch ( ZstdException e ) { + Console.WriteLine( "ZStd - Cannot decode file: " + m_filename ); + Console.WriteLine( "Error: " + e.Message ); + //Console.ReadLine(); + return new byte[] { }; + } + } + } + else { + // plain write - might be wrong if another compression was applied.. + decompFile = fileBytes; + return decompFile; + } + } + + + public bool IsValid { get => m_itemValid; } + public string Filename { get => m_filename; } + public long RecordOffset { get => m_recordOffset; } + public long FileOffset { get => m_fileOffset; } + public long FileSizeComp { get => m_fileSizeComp; } + public long FileSizeUnComp { get => m_fileSizeUnComp; } + public DateTime FileModifyDate { get => m_fileDateTime; } + + + + private void ReadFilename( p4kRecReader reader ) + { + byte[] fileNameBytes = new byte[m_item.FilenameLength]; + fileNameBytes = reader.ReadBytes( m_item.FilenameLength ); + m_filename = Encoding.ASCII.GetString( fileNameBytes ); + } + + private void ReadExtradata( p4kRecReader reader ) + { + // first the Zip64 extra record + m_z64Item = p4kRecReader.ByteToType( reader.TheReader ); + // then the rest of the extra record (is another item with tag 0x0666 and the rest lenght (ignored) + m_extraBytes = new byte[m_item.ExtraFieldLength - Z64ExtraRecordLength]; + m_extraBytes = reader.ReadBytes( m_extraBytes.Length ); + m_extraBytes = null; // dump it ... + // now we would be able to read the file content + } + + + private MyRecord m_item; + private bool m_itemValid = false; + private MyZ64ExtraRecord m_z64Item; + private byte[] m_extraBytes = null; + private long m_recordOffset = -1; // offset from start (current position) + private long m_fileOffset = 0; // offset from start (current position) + // Entry attributes + private string m_filename = ""; + private DateTime m_fileDateTime = new DateTime( 1970, 1, 1 ); + private long m_fileSizeComp = 0; + private long m_fileSizeUnComp = 0; + + + + private const int RecordLength = 30; + [StructLayout( LayoutKind.Sequential, Pack = 1 )] // , Size = RecordLength + private struct MyRecord + { + [MarshalAs( UnmanagedType.ByValArray, SizeConst = 4 )] + public byte[] ID; // local file header signature 4 bytes(0x14034b50) NOTE p4k here uses 0x14 and not 0x04 + [MarshalAs( UnmanagedType.U2 )] + public UInt16 ExtractVersion; // version made by 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 BitFlags; // general purpose bit flag 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 CompressionMethod; // compression method 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 LastModTime; // last mod file time 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 LastModDate; // last mod file date 2 bytes + [MarshalAs( UnmanagedType.U4 )] + public UInt32 CRC32; // crc-32 4 bytes + [MarshalAs( UnmanagedType.U4 )] + public UInt32 CompressedSize; // compressed size 4 bytes + [MarshalAs( UnmanagedType.U4 )] + public UInt32 UncompressedSize; // uncompressed size 4 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 FilenameLength; // file name length 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 ExtraFieldLength; // extra field length 2 bytes + } + + private const int Z64ExtraRecordLength = 32; + [StructLayout( LayoutKind.Sequential, Pack = 1 )] // , Size = RecordLength + private struct MyZ64ExtraRecord + { + [MarshalAs( UnmanagedType.U2 )] + public UInt16 ID; // (Zip64 ExtraHeader Signature) + [MarshalAs( UnmanagedType.U2 )] + public UInt16 Size; // Size 2 bytes Size of this "extra" block + [MarshalAs( UnmanagedType.U8 )] + public UInt64 UncompressedSize; // Original Size 8 bytes Original uncompressed file size + [MarshalAs( UnmanagedType.U8 )] + public UInt64 CompressedSize; // Compressed Size 8 bytes Size of compressed data + [MarshalAs( UnmanagedType.U8 )] + public UInt64 LocalHeaderOffset; // Relative Header Offset 8 bytes Offset of local header record + [MarshalAs( UnmanagedType.U4 )] + public UInt32 DiskStart; // Disk Start Number 4 bytes Number of the disk on which this file starts + } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose( bool disposing ) + { + if ( !disposedValue ) { + if ( disposing ) { + // TODO: dispose managed state (managed objects). + m_itemValid = false; + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + m_extraBytes = null; + + disposedValue = true; + } + } + + // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. + // ~p4kFileHeader() { + // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Dispose(false); + // } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose( true ); + // TODO: uncomment the following line if the finalizer is overridden above. + // GC.SuppressFinalize(this); + } + #endregion + + } +} diff --git a/SC/p4kFile/p4kFileTStamp.cs b/SC/p4kFile/p4kFileTStamp.cs new file mode 100644 index 0000000..7863037 --- /dev/null +++ b/SC/p4kFile/p4kFileTStamp.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SCJMapper_V2.p4kFile +{ + class p4kFileTStamp + { + + /// + /// Converts from Zip (DOS) File date time to .Net DatetTime format + /// + /// A DOS file date integer + /// A DOS file time integer + /// The DateTime conversion of the input + public static DateTime FromDos( UInt16 date, UInt16 time ) + { + // DOS date : hBit-- YYYYYYY MMMM TTTTT (year + 1980) + // DOS time : hBit-- hhhhh mmmmmm xxxxx (x*2 = sec) + + int year = ( ( date >> 9 ) & 0x7f ) + 1980; + int month = ( date >> 5 ) & 0x0f; + int day = date & 0x01f; + int hour = ( time >> 11 ) & 0x1f; + int min = ( time >> 5 ) & 0x3f; + int sec = ( time & 0x01f ) * 2; + try { + var ret = new DateTime( year, month, day, hour, min, sec ); + return ret; + } + catch ( Exception e ) { + return new DateTime( 1970, 1, 1 ); + } + } + + } +} diff --git a/SC/p4kFile/p4kRecReader.cs b/SC/p4kFile/p4kRecReader.cs new file mode 100644 index 0000000..7c8e830 --- /dev/null +++ b/SC/p4kFile/p4kRecReader.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace SCJMapper_V2.p4kFile +{ + /// + /// Implements a binary reader for p4k files + /// + class p4kRecReader : IDisposable + { + public const long PageSize = 0x1000; + + private FileStream m_filestr = null; + private BinaryReader m_reader = null; + + private DateTime m_fileCreatedT; // hold the file creation time + + + /// + /// ctor: + /// + /// The filename + public p4kRecReader( String filename ) + { + Open( filename ); + m_fileCreatedT = File.GetCreationTimeUtc( m_filestr.Name ); + } + + /// + /// Open the file + /// + /// The filename + /// True when successfull + private bool Open( String filename ) + { + if ( File.Exists( filename ) ) { + m_filestr = File.OpenRead( filename ); + m_reader = new BinaryReader( m_filestr ); + return true; + } + + return false; + } + + + /// + /// Returns the Reader + /// + public BinaryReader TheReader + { + get { return m_reader; } + } + + + /// + /// Allocates and reads bytes of the size of one record + /// and returns the allocated bytes are structure - allowing structured access to binary data + /// Note: there is no error checking whatsoever here - so better make sure everything is OK + /// + /// The record type to read + /// A binary reader + /// The read record + public static T ByteToType( BinaryReader reader ) + { + byte[] bytes = reader.ReadBytes( Marshal.SizeOf( typeof( T ) ) ); + + GCHandle handle = GCHandle.Alloc( bytes, GCHandleType.Pinned ); + T theStructure = (T)Marshal.PtrToStructure( handle.AddrOfPinnedObject( ), typeof( T ) ); + handle.Free( ); + + return theStructure; + } + + /// + /// Returns the file status + /// + /// True if the file is open and the reader can read + public bool IsOpen() + { + if ( m_filestr != null ) return m_filestr.CanRead; + else return false; + } + + /// + /// Returns the current Position of the underlying stream + /// + public long Position { get => m_filestr.Position; } + + /// + /// Returns the Length of the underlying stream + /// + public long Length { get => m_filestr.Length; } + + /// + /// Returns a number of bytes as array (from the underlying reader) + /// + /// Number of bytes to read + /// A byte array + public byte[] ReadBytes(int count) + { + return m_reader.ReadBytes( count ); + } + + + /// + /// Seeks from to pos from beginning + /// + /// A position in the filestream + /// The current starting position before seek + public long Seek( long pos ) + { + long thisPos = m_filestr.Position; + m_filestr.Seek( pos, SeekOrigin.Begin ); + return thisPos; + } + + /// + /// As the file is 4k page oriented - this advances to the next page + /// + public void AdvancePage() + { + long current = TheReader.BaseStream.Position; + long remainder = current % PageSize; + if ( remainder > 0 ) { + long seek = PageSize - remainder; + TheReader.BaseStream.Seek( seek, SeekOrigin.Current ); + } + } + + /// + /// Places the stream at the last Page of the file + /// + /// Position of the stream + public long GotoLastPage() + { + TheReader.BaseStream.Seek( -PageSize, SeekOrigin.End ); + return TheReader.BaseStream.Position; + } + + /// + /// Returns one Page of the file @ current pos + /// + /// PageSize bytes at the current pos of the stream + public byte[] GetPage() + { + return TheReader.ReadBytes( (int)PageSize ); + } + + /// + /// Returns the file creation timestamp + /// + public DateTime FileDateTime + { + get { return m_fileCreatedT; } + set {; } + } + + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + protected virtual void Dispose( bool disposing ) + { + if ( !disposedValue ) { + if ( disposing ) { + // TODO: dispose managed state (managed objects). + if ( m_reader != null ) { + m_reader.Close( ); + m_reader.Dispose( ); + m_reader = null; + } + if ( m_filestr != null ) { + m_filestr.Close( ); + m_filestr.Dispose( ); + m_filestr = null; + } + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + + disposedValue = true; + } + } + + // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. + // ~p4kRecReader() { + // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Dispose(false); + // } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose( true ); + // TODO: uncomment the following line if the finalizer is overridden above. + // GC.SuppressFinalize(this); + } + #endregion + + } +} diff --git a/SC/p4kFile/p4kSignatures.cs b/SC/p4kFile/p4kSignatures.cs new file mode 100644 index 0000000..38368bf --- /dev/null +++ b/SC/p4kFile/p4kSignatures.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SCJMapper_V2.p4kFile +{ + internal class p4kSignatures + { + + // From PKWare APPNOTE.TXT + //4.3.6 Overall.ZIP file format: + + // [local file header 1] LocalFileHeader OR LocalFileHeaderCry for this p4k version + // [encryption header 1] + // [file data 1] + // [data descriptor 1] + // . + // . + // . + // [local file header n] + // [encryption header n] + // [file data n] + // [data descriptor n] + // [archive decryption header] CentralDirRec + // [archive extra data record] ExtraDataRecord + // [central directory header 1] CentralDirRec + // . + // . + // . + // [central directory header n] CentralDirRec + // [zip64 end of central directory record] Z64CentralDirRecEnd + // [zip64 end of central directory locator] Z64CentralDirLocEnd + // [end of central directory record] CentralDirRecEnd + + + public static readonly byte[] LocalFileHeader = { 0x50, 0x4B, 0x03, 0x04 }; // (0x04034b50) 4.3.7 Local file header: + public static readonly byte[] LocalFileHeaderCry = { 0x50, 0x4B, 0x03, 0x14 }; // as found in the p4k files + public static readonly byte[] ExtraDataRecord = { 0x50, 0x4B, 0x06, 0x08 }; // (0x08064b50) 4.3.11 Archive extra data record: + public static readonly byte[] CentralDirRecord = { 0x50, 0x4B, 0x01, 0x02 }; // (0x02014b50) 4.3.12 Central directory structure: + public static readonly byte[] DigitalSignature = { 0x50, 0x4B, 0x05, 0x05 }; // (0x05054b50) 4.3.13 Digital signature: + public static readonly byte[] Z64EndOfCentralDirRec = { 0x50, 0x4B, 0x06, 0x06 }; // (0x06064b50) 4.3.14 Zip64 end of central directory record + public static readonly byte[] Z64EndOfCentralDirLocator = { 0x50, 0x4B, 0x06, 0x07 }; // (0x07064b50) 4.3.15 Zip64 end of central directory locator + public static readonly byte[] EndOfCentralDirRecord = { 0x50, 0x4B, 0x05, 0x06 }; // (0x06054b50) 4.3.16 End of central directory record: + + /// + /// Returns the position of the Signature within the stream + /// Searches one page from current location + /// + /// A positioned reader + /// The position within the stream or -1 if not found + public static long FindSignatureInPage( p4kRecReader reader, byte[] signature ) + { + long pos = reader.Position; + byte[] lPage = reader.GetPage( ); + for ( int i = 0; i < lPage.Length - 4; i++ ) { + if ( lPage.Skip( i ).Take( 4 ).SequenceEqual( signature ) ) { + // now this should be the start of the item + return pos + i; + } + } + return -1; // not found... + } + + /// + /// Returns the position of the Signature within the stream + /// Searches one page from current location starting at the end of the page + /// + /// A positioned reader + /// The position within the stream or -1 if not found + public static long FindSignatureInPageBackwards( p4kRecReader reader, byte[] signature ) + { + long pos = reader.Position; + byte[] lPage = reader.GetPage( ); + for ( int i = lPage.Length - 4; i > 0; i-- ) { + if ( lPage.Skip( i ).Take( 4 ).SequenceEqual( signature ) ) { + // now this should be the start of the item [end of central directory record] + return pos + i; + } + } + return -1; // not found... + } + + + + } +} diff --git a/SC/p4kFile/p4kZ64EndOfCentralDirLocator.cs b/SC/p4kFile/p4kZ64EndOfCentralDirLocator.cs new file mode 100644 index 0000000..25b5a54 --- /dev/null +++ b/SC/p4kFile/p4kZ64EndOfCentralDirLocator.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace SCJMapper_V2.p4kFile +{ + /// Represents an Z64EndOfCentralDirLocator entry in the p4k file + /// The file seems to be based on a Zip64 file structure + internal class p4kZ64EndOfCentralDirLocator + { + /// + /// ctor: Create class from data returned by the Reader + /// + /// A binary data reader for this type of data already positioned + public p4kZ64EndOfCentralDirLocator( p4kRecReader reader ) + { + // sanity check only + System.Diagnostics.Trace.Assert( Marshal.SizeOf( typeof( MyRecord ) ) == RecordLength, + "Record size does not match!(" + Marshal.SizeOf( typeof( MyRecord ) ).ToString( ) + ")" ); + + if ( reader.IsOpen( ) ) { + try { + long cPos = p4kSignatures.FindSignatureInPageBackwards( reader, p4kSignatures.Z64EndOfCentralDirLocator ); + if ( cPos >= 0 ) { + m_recordOffset = cPos; + reader.Seek( cPos ); + m_item = p4kRecReader.ByteToType( reader.TheReader ); + m_itemValid = true; + } + } + catch { + m_itemValid = false; + m_recordOffset = -1; + } + finally { + if ( !m_itemValid ) { + throw new OperationCanceledException( string.Format( "EOF - cannot find Z64EndOfCentralDirLocator" ) ); + } + } + } + } + + public bool IsValid { get => m_itemValid; } + public long RecordOffset { get => m_recordOffset; } + public long Z64EndOfCentralDir + { + get { + if ( m_itemValid ) + return (long)m_item.OffsetOfz64EofCDir; + else + return -1; + } + } + + private MyRecord m_item; + private bool m_itemValid = false; + private long m_recordOffset = -1; // offset from start (current position) + + + //4.3.15 Zip64 end of central directory locator + private const int RecordLength = 20; + [StructLayout( LayoutKind.Sequential, Pack = 1 )] // , Size = RecordLength + private struct MyRecord + { + [MarshalAs( UnmanagedType.ByValArray, SizeConst = 4 )] + public byte[] ID; // zip64 end of central dir locator signature 4 bytes(0x07064b50) + [MarshalAs( UnmanagedType.U4 )] + public UInt32 DiskNumber; // number of the disk with the start of the zip64 end of central directory 4 bytes + [MarshalAs( UnmanagedType.U8 )] + public UInt64 OffsetOfz64EofCDir; // relative offset of the zip64 end of central directory record 8 bytes (pts to Z64CentralDirRecEnd) + [MarshalAs( UnmanagedType.U4 )] + public UInt32 TotalNumEntries; // total number of disks 4 bytes + } + + + + } +} diff --git a/SC/p4kFile/p4kZ64EndOfCentralDirRecord.cs b/SC/p4kFile/p4kZ64EndOfCentralDirRecord.cs new file mode 100644 index 0000000..aa3faa9 --- /dev/null +++ b/SC/p4kFile/p4kZ64EndOfCentralDirRecord.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace SCJMapper_V2.p4kFile +{ + /// Represents an Z64EndOfCentralDirRecord entry in the p4k file + /// The file seems to be based on a Zip64 file structure + internal class p4kZ64EndOfCentralDirRecord + { + /// + /// ctor: Create class from data returned by the Reader + /// + /// A binary data reader for this type of data - positioned already + public p4kZ64EndOfCentralDirRecord( p4kRecReader reader ) + { + // sanity check only + System.Diagnostics.Trace.Assert( Marshal.SizeOf( typeof( MyRecord ) ) == RecordLength, + "Record size does not match!(" + Marshal.SizeOf( typeof( MyRecord ) ).ToString( ) + ")" ); + + if ( reader.IsOpen( ) ) { + try { + long cPos = p4kSignatures.FindSignatureInPage( reader, p4kSignatures.Z64EndOfCentralDirRec ); + if ( cPos >= 0 ) { + m_recordOffset = cPos; + reader.Seek( cPos ); + m_item = p4kRecReader.ByteToType( reader.TheReader ); + m_itemValid = true; + } + } + catch { + m_itemValid = false; + m_recordOffset = -1; + } + finally { + if ( !m_itemValid ) { + throw new OperationCanceledException( string.Format( "EOF - cannot find Z64EndOfCentralDirRecord" ) ); + } + } + } + } + + public bool IsValid { get => m_itemValid; } + public long RecordOffset { get => m_recordOffset; } + public long Z64StartOfCentralDir + { + get { + if ( m_itemValid ) + return (long)m_item.OffsetOfZ64CDir; + else + return -1; + } + } + public long NumberOfEntries + { + get { + if ( m_itemValid ) + return (long)m_item.NumEntriesTotal; + else + return -1; + } + } + + private MyRecord m_item; + private bool m_itemValid = false; + private long m_recordOffset = -1; // offset from start (current position) + + + //4.3.14 Zip64 end of central directory record + private const int RecordLength = 56; + [StructLayout( LayoutKind.Sequential, Pack = 1 )] // , Size = RecordLength + private struct MyRecord + { + [MarshalAs( UnmanagedType.ByValArray, SizeConst = 4 )] + public byte[] ID; // zip64 end of central dir signature 4 bytes(0x06064b50) + [MarshalAs( UnmanagedType.U8 )] + public UInt64 SizeOfZ64CDir; // size of zip64 end of central directory record 8 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 VersionMadeBy; // version made by 2 bytes + [MarshalAs( UnmanagedType.U2 )] + public UInt16 ExtractVersion; // version made by 2 bytes + [MarshalAs( UnmanagedType.U4 )] + public UInt32 DiskNumber; // number of this disk 4 bytes + [MarshalAs( UnmanagedType.U4 )] + public UInt32 DiskNumbersFromStart; // number of the disk with the start of the central directory 4 bytes + [MarshalAs( UnmanagedType.U8 )] + public UInt64 NumEntriesOnDisk; // total number of entries in the central directory on this disk 8 bytes + [MarshalAs( UnmanagedType.U8 )] + public UInt64 NumEntriesTotal; // total number of entries in the central directory 8 bytes + [MarshalAs( UnmanagedType.U8 )] + public UInt64 SizeOfCDir; // size of the central directory 8 bytes + [MarshalAs( UnmanagedType.U8 )] + public UInt64 OffsetOfZ64CDir; // offset of start of central directory with respect to the starting disk number 8 bytes + // zip64 extensible data sector (variable size) + } + + + + } +} diff --git a/SCJMapper-V2.csproj b/SCJMapper-V2.csproj index fbb7fa8..80479ed 100644 --- a/SCJMapper-V2.csproj +++ b/SCJMapper-V2.csproj @@ -14,7 +14,8 @@ 512 - 3422add1 + + false publish\ true @@ -26,8 +27,8 @@ false false true - 66 - 2.32.0.%2a + 67 + 2.33.0.%2a false true @@ -85,9 +86,6 @@ app.manifest - - packages\DotNetZip.Reduced.1.9.1.8\lib\net20\Ionic.Zip.Reduced.dll - packages\log4net.2.0.3\lib\net40-full\log4net.dll @@ -121,26 +119,40 @@ + + packages\ZstdNet.1.3.1\lib\net45\ZstdNet.dll + - - - - + + + + - - - - - + + + + + Form - + FormOptions.cs - + + + + + + + + + + + + @@ -158,16 +170,16 @@ FormTable.cs - - - - - - - + + + + + + + - + @@ -176,17 +188,17 @@ FormJSCalCurve.cs - + Form FormMain.cs - + Form - + FormReassign.cs @@ -195,11 +207,11 @@ FormSettings.cs - - - - - + + + + + @@ -210,17 +222,17 @@ - - - + + + UserControl - + UC_JoyPanel.cs - + - + @@ -229,20 +241,20 @@ - + - - + + UserControl - + UC_GpadPanel.cs FormMain.cs Designer - + FormOptions.cs @@ -251,13 +263,13 @@ FormJSCalCurve.cs - + FormReassign.cs FormSettings.cs - + UC_JoyPanel.cs @@ -270,7 +282,7 @@ Resources.resx True - + UC_GpadPanel.cs @@ -286,6 +298,8 @@ Always + + DS_ActionMaps.xsd @@ -333,6 +347,7 @@ + @@ -385,7 +400,9 @@ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + in order to get always the most current one use 3. (and therefore remove the ones in 1. and 2.) + --> The one used is shown below the actionTree (Profile: ....) Read the Guide first RTFM ;-) Put all files into one folder and hit SCJMapper.exe to run it @@ -29,8 +40,17 @@ Scanned for viruses before packing... cassini@burri-web.org Changelog: +V 2.33 - BETA Build 67 +- update for SC 3.0.0 Alpha public +- fix - finding SC game folder - may work automatically for 3.0 Alpha else define it in Settings +- add - get the defaultProfile.xml from LIVE\data.p4k file if possible (real game assets) +- improvement - caching def profile once it is read from disk +- removed - old SC path and folder locators (SCJM does not longer work with pre 3.0 game) +- removed - reference to Iconic.Zip DLL (replaced with Zstd) +- update - defaultProfile.xml as last resort from PTU 3.0-695052 (Dec 18, build) V 2.32 - BETA Build 66 - add - path to defaultProfile can be in USER directory of SC +- add - some skyboxes from game captures (thanks to Rellim) - removed - PTU folders in Settings - no longer used in PTU 3.0 - fix - finding SC game folder - may work automatically for PTU 3.0 else define it in Settings - update - defaultProfile from PTU 3.0-689345 (Dec 15, build) @@ -44,7 +64,6 @@ V 2.30 - BETA Build 64 - fix - issue with loading a map with gamepad mappings and the gamepad is not connected - fixes and refacturing while encountered... - update - doc SCJMapper_QGuide V2.30beta.pdf -Changelog: V 2.29 - BETA Build 63 - add - Calibrate gamepad thumb axes (press ABXY buttons all together and wait 2 sec - should zero all 4 axes) - fix #56 - exception when entering Tuning diff --git a/graphics/Readme.txt b/graphics/Readme.txt index c1e24ea..5e73ba4 100644 --- a/graphics/Readme.txt +++ b/graphics/Readme.txt @@ -1,5 +1,9 @@ -Canyon, Highway, Shiodome, BigSight, LA Heliport: +Image Credits: + +SC_Area18, _GrimHex, _DyingStar, _BrokenMoon, _Kareah +by Rellim (SC handle) +Canyon, Highway, Shiodome, BigSight, LA Heliport: http://www.hdrlabs.com/sibl/archive.html sIBL Archive - Free HDRI sets for smart Image-Based Lighting diff --git a/packages.config b/packages.config index 7647a64..305ad65 100644 --- a/packages.config +++ b/packages.config @@ -1,9 +1,9 @@  - + \ No newline at end of file diff --git a/packages/DotNetZip.Reduced.1.9.1.8/DotNetZip.Reduced.1.9.1.8.nupkg b/packages/DotNetZip.Reduced.1.9.1.8/DotNetZip.Reduced.1.9.1.8.nupkg deleted file mode 100644 index 5dbbf5f..0000000 Binary files a/packages/DotNetZip.Reduced.1.9.1.8/DotNetZip.Reduced.1.9.1.8.nupkg and /dev/null differ diff --git a/packages/DotNetZip.Reduced.1.9.1.8/lib/net20/Ionic.Zip.Reduced.dll b/packages/DotNetZip.Reduced.1.9.1.8/lib/net20/Ionic.Zip.Reduced.dll deleted file mode 100644 index 9622cc5..0000000 Binary files a/packages/DotNetZip.Reduced.1.9.1.8/lib/net20/Ionic.Zip.Reduced.dll and /dev/null differ diff --git a/packages/ZstdNet.1.3.1/ZstdNet.1.3.1.nupkg b/packages/ZstdNet.1.3.1/ZstdNet.1.3.1.nupkg new file mode 100644 index 0000000..1abef34 Binary files /dev/null and b/packages/ZstdNet.1.3.1/ZstdNet.1.3.1.nupkg differ diff --git a/packages/ZstdNet.1.3.1/lib/LICENSE b/packages/ZstdNet.1.3.1/lib/LICENSE new file mode 100644 index 0000000..611bba2 --- /dev/null +++ b/packages/ZstdNet.1.3.1/lib/LICENSE @@ -0,0 +1,30 @@ +BSD License + +For ZstdNet software + +Copyright (c) 2016-2017, SKB Kontur. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name SKB Kontur nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/ZstdNet.1.3.1/lib/net45/ZstdNet.dll b/packages/ZstdNet.1.3.1/lib/net45/ZstdNet.dll new file mode 100644 index 0000000..b998948 Binary files /dev/null and b/packages/ZstdNet.1.3.1/lib/net45/ZstdNet.dll differ diff --git a/packages/ZstdNet.1.3.1/lib/netstandard2.0/ZstdNet.dll b/packages/ZstdNet.1.3.1/lib/netstandard2.0/ZstdNet.dll new file mode 100644 index 0000000..ecc1597 Binary files /dev/null and b/packages/ZstdNet.1.3.1/lib/netstandard2.0/ZstdNet.dll differ