WPF version

rabi_display
Copy Liu 8 years ago
parent 37ee73d0bf
commit 8be92a35be
No known key found for this signature in database
GPG Key ID: 63A453DBF23409AF
  1. 1
      .gitignore
  2. 9
      rabi_splitter_WPF/App.xaml
  3. 16
      rabi_splitter_WPF/App.xaml.cs
  4. 266
      rabi_splitter_WPF/MainContext.cs
  5. 60
      rabi_splitter_WPF/MainWindow.xaml
  6. 457
      rabi_splitter_WPF/MainWindow.xaml.cs
  7. 97
      rabi_splitter_WPF/MemoryHelper.cs
  8. 1048
      rabi_splitter_WPF/Properties/Annotations.cs
  9. 55
      rabi_splitter_WPF/Properties/AssemblyInfo.cs
  10. 71
      rabi_splitter_WPF/Properties/Resources.Designer.cs
  11. 117
      rabi_splitter_WPF/Properties/Resources.resx
  12. 30
      rabi_splitter_WPF/Properties/Settings.Designer.cs
  13. 7
      rabi_splitter_WPF/Properties/Settings.settings
  14. 168
      rabi_splitter_WPF/StaticData.cs
  15. BIN
      rabi_splitter_WPF/irisu.ico
  16. 102
      rabi_splitter_WPF/rabi_splitter_WPF.csproj
  17. 10
      rabiribi_splitter.sln

1
.gitignore vendored

@ -250,3 +250,4 @@ paket-files/
# JetBrains Rider
.idea/
*.sln.iml
*.DotSettings

@ -0,0 +1,9 @@
<Application x:Class="rabi_splitter_WPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:rabi_splitter_WPF"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
namespace rabi_splitter_WPF
{
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{
}
}

@ -0,0 +1,266 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using rabi_splitter_WPF.Annotations;
namespace rabi_splitter_WPF
{
public class BossData:INotifyPropertyChanged
{
public int BossIdx { get; set; }
public int BossID { get; set; }
public int BossHP { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class DebugContext : INotifyPropertyChanged
{
private bool _bossEvent;
private string _debugLog;
public ObservableCollection<BossData> BossList = new ObservableCollection<BossData>();
public bool BossEvent
{
get { return _bossEvent; }
set
{
if (value == _bossEvent) return;
_bossEvent = value;
OnPropertyChanged(nameof(BossEvent));
}
}
public string DebugLog
{
get { return _debugLog; }
set
{
if (value == _debugLog) return;
_debugLog = value;
OnPropertyChanged(nameof(DebugLog));
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class MainContext : INotifyPropertyChanged
{
private bool _musicStart;
private bool _musicEnd;
private bool _computer;
private bool _miruDe;
private bool _sideCh;
private bool _aliusI;
private bool _tm2;
private bool _irisu1;
private bool _debugArea;
private int _serverPort;
private string _gameVer;
private string _gameMusic;
private bool _igt;
public bool MusicStart
{
get { return _musicStart; }
set
{
if (value == _musicStart) return;
_musicStart = value;
OnPropertyChanged(nameof(MusicStart));
}
}
public bool MusicEnd
{
get { return _musicEnd; }
set
{
if (value == _musicEnd) return;
_musicEnd = value;
OnPropertyChanged(nameof(MusicEnd));
}
}
public bool Computer
{
get { return _computer; }
set
{
if (value == _computer) return;
_computer = value;
OnPropertyChanged(nameof(Computer));
}
}
public bool MiruDe
{
get { return _miruDe; }
set
{
if (value == _miruDe) return;
_miruDe = value;
OnPropertyChanged(nameof(MiruDe));
}
}
public bool SideCh
{
get { return _sideCh; }
set
{
if (value == _sideCh) return;
_sideCh = value;
OnPropertyChanged(nameof(SideCh));
}
}
public bool AliusI
{
get { return _aliusI; }
set
{
if (value == _aliusI) return;
_aliusI = value;
OnPropertyChanged(nameof(AliusI));
}
}
public bool Tm2
{
get { return _tm2; }
set
{
if (value == _tm2) return;
_tm2 = value;
OnPropertyChanged(nameof(Tm2));
}
}
public bool Irisu1
{
get { return _irisu1; }
set
{
if (value == _irisu1) return;
_irisu1 = value;
OnPropertyChanged(nameof(Irisu1));
}
}
public bool DebugArea
{
get { return _debugArea; }
set
{
if (value == _debugArea) return;
_debugArea = value;
OnPropertyChanged(nameof(DebugArea));
}
}
public int ServerPort
{
get { return _serverPort; }
set
{
if (value == _serverPort) return;
_serverPort = value;
OnPropertyChanged(nameof(ServerPort));
}
}
public string GameVer
{
get { return _gameVer; }
set
{
if (value == _gameVer) return;
_gameVer = value;
OnPropertyChanged(nameof(GameVer));
}
}
public string GameMusic
{
get { return _gameMusic; }
set
{
if (value == _gameMusic) return;
_gameMusic = value;
OnPropertyChanged(nameof(GameMusic));
}
}
public bool Igt
{
get { return _igt; }
set
{
if (value == _igt) return;
_igt = value;
OnPropertyChanged(nameof(Igt));
}
}
public string oldtitle;
public int veridx;
public int lastmoney;
public int lastmapid;
public int lastmusicid;
public bool bossbattle;
public List<int> lastbosslist;
public int lastnoah3hp;
public int lastTM;
public DateTime LastTMAddTime;
public MainContext()
{
this.MusicEnd = true;
this.MusicStart = false;
this.Computer = true;
this.MiruDe = true;
this.SideCh = true;
this.AliusI = true;
this.Tm2 = true;
this.Irisu1 = true;
this.DebugArea = false;
this.ServerPort = 16834;
this.Igt = true;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

@ -0,0 +1,60 @@
<Window x:Class="rabi_splitter_WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:rabi_splitter_WPF"
mc:Ignorable="d"
Title="Irisu is watching you" d:DataContext="{d:DesignData local:MainContext}" SizeToContent="WidthAndHeight">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<DockPanel>
<StackPanel Orientation="Horizontal" Margin="5" DockPanel.Dock="Top">
<StackPanel Margin="0,0,100,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,0,0,3">
<TextBlock TextWrapping="Wrap" Text="LiveSplit Server Port" VerticalAlignment="Center" FontSize="16" Margin="0,0,10,0"/>
<TextBox TextWrapping="Wrap" Text="{Binding ServerPort}" VerticalAlignment="Center" HorizontalAlignment="Center" VerticalContentAlignment="Center" FontSize="16" Padding="6,0"/>
<Button x:Name="BtnConnect" Content="Connect" VerticalAlignment="Center" FontSize="16" Width="80" Click="BtnConnect_Click"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,0,0,3">
<TextBlock TextWrapping="Wrap" Text="Rabi-Ribi Status:" FontSize="16"/>
<TextBlock TextWrapping="Wrap" Text="{Binding GameVer}" FontSize="16"/>
</StackPanel>
<TextBlock TextWrapping="Wrap" Text="{Binding GameMusic}" HorizontalAlignment="Left" FontSize="15" Margin="0,0,0,4"/>
<CheckBox Content="Split when BOSS music STARTS" HorizontalAlignment="Left" FontSize="15" Margin="0,0,0,4" IsChecked="{Binding MusicStart, Mode=TwoWay}"/>
<CheckBox Content="Split when BOSS music ENDS" HorizontalAlignment="Left" FontSize="15" Margin="0,0,0,4" IsChecked="{Binding MusicEnd, Mode=TwoWay}"/>
<CheckBox Content="Split when the computer is found" HorizontalAlignment="Left" FontSize="15" Margin="0,0,0,4" IsChecked="{Binding Computer,Mode=TwoWay}"/>
<CheckBox Content="Split when Miru despawns" HorizontalAlignment="Left" FontSize="15" Margin="0,0,0,4" IsChecked="{Binding MiruDe,Mode=TwoWay}"/>
<CheckBox Content="Ignore the Side Chapter" HorizontalAlignment="Left" FontSize="15" Margin="0,0,0,4" IsChecked="{Binding SideCh,Mode=TwoWay}"/>
<CheckBox Content="Ignore the next &quot;SUDDEN DEATH&quot; (Ignore Alius I)" HorizontalAlignment="Left" FontSize="15" Margin="0,0,0,4" IsChecked="{Binding AliusI,Mode=TwoWay}" IsEnabled="False"/>
<CheckBox Content="Split when Town Member +2 or Nixie despawns" HorizontalAlignment="Left" FontSize="15" Margin="0,0,0,4" IsChecked="{Binding Tm2,Mode=TwoWay}"/>
<CheckBox Content="Ignore Irisu Phase 1" HorizontalAlignment="Left" FontSize="15" Margin="0,0,0,4" IsChecked="{Binding Irisu1,Mode=TwoWay}"/>
<CheckBox Content="Track In-Game Time" HorizontalAlignment="Left" FontSize="15" Margin="0,0,0,4" IsChecked="{Binding Igt,Mode=TwoWay}"/>
<CheckBox Content="Show debug area" HorizontalAlignment="Left" FontSize="15" Margin="0,0,0,4" IsChecked="{Binding DebugArea,Mode=TwoWay}"/>
</StackPanel>
<StackPanel Visibility="{Binding DebugArea, Converter={StaticResource BooleanToVisibilityConverter}, Mode=TwoWay}" >
<DockPanel x:Name="DebugPanel" LastChildFill="True" d:DataContext="{d:DesignData local:DebugContext}">
<TextBlock TextWrapping="Wrap" Text="debug area" DockPanel.Dock="Top"/>
<CheckBox IsEnabled="False" Content="(DEBUG)BOSS event tracked" FontSize="15" Margin="0,0,0,4" VerticalAlignment="Top" HorizontalAlignment="Left" DockPanel.Dock="Top" IsChecked="{Binding BossEvent,Mode=TwoWay}"/>
<DataGrid ItemsSource="{Binding BossList}" CanUserAddRows="False" IsReadOnly="True" AutoGenerateColumns="False" SelectionUnit="FullRow" DockPanel.Dock="Top"
d:DataContext="{d:DesignData local:BossData}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding BossIdx}" ClipboardContentBinding="{Binding BossIdx}" Header="Index"/>
<DataGridTextColumn Binding="{Binding BossID}" ClipboardContentBinding="{Binding BossID}" Header="Entity ID"/>
<DataGridTextColumn Binding="{Binding BossHP}" ClipboardContentBinding="{Binding BossHP}" Header="HP"/>
</DataGrid.Columns>
</DataGrid>
<TextBox TextWrapping="Wrap" Text="{Binding DebugLog,Mode=TwoWay}" DockPanel.Dock="Bottom" MinHeight="203"/>
</DockPanel>
</StackPanel>
</StackPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
<TextBlock Text="v0.3" Margin="0,0,30,0" ></TextBlock>
<TextBlock Text="GitHub" MouseUp="TextBlock_MouseUp" Cursor="Hand" Foreground="Blue" TextDecorations="Underline" ></TextBlock>
</StackPanel>
</DockPanel>
</Window>

@ -0,0 +1,457 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace rabi_splitter_WPF
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
private MainContext mainContext;
private DebugContext debugContext;
private static TcpClient tcpclient;
private static NetworkStream networkStream;
private readonly Regex titleReg = new Regex(@"ver.*?(\d+\.?\d+.*)$");
private readonly Thread memoryThread;
private void ReadMemory()
{
var processlist = Process.GetProcessesByName("rabiribi");
if (processlist.Length > 0)
{
Process process = processlist[0];
if (process.MainWindowTitle != mainContext.oldtitle)
{
var result = titleReg.Match(process.MainWindowTitle);
string rabiver;
if (result.Success)
{
rabiver = result.Groups[1].Value;
mainContext.veridx = Array.IndexOf(StaticData.VerNames, rabiver);
if (mainContext.veridx < 0)
{
mainContext.GameVer = rabiver + " Running (not support)";
return;
}
}
else
{
mainContext.veridx = -1;
mainContext.GameVer = "Running (Unknown version)";
return;
}
mainContext.GameVer = rabiver + " Running";
mainContext.oldtitle = process.MainWindowTitle;
}
if (mainContext.veridx < 0) return;
#region read igt
int igt = MemoryHelper.GetMemoryValue<int>(process, StaticData.IGTAddr[mainContext.veridx]);
if (igt > 0 && mainContext.Igt)
{
sendigt((float)igt / 60);
}
#endregion
#region CheckMoney
if (mainContext.Computer)
{
var newmoney = MemoryHelper.GetMemoryValue<int>(process, StaticData.MoneyAddress[mainContext.veridx]);
if (newmoney - mainContext.lastmoney == 17500)
{
sendsplit();
DebugLog("get 17500 en, split");
}
mainContext.lastmoney = newmoney;
}
#endregion
int mapid = MemoryHelper.GetMemoryValue<int>(process, StaticData.MapAddress[mainContext.veridx]);
if (mainContext.lastmapid != mapid)
{
DebugLog("newmap: " + mapid + ":" + StaticData.MapNames[mapid]);
mainContext.lastmapid = mapid;
}
#region checkTM
#endregion
#region Music
int musicaddr = StaticData.MusicAddr[mainContext.veridx];
int musicid = MemoryHelper.GetMemoryValue<int>(process, musicaddr);
if (musicid > 0 && musicid < StaticData.MusicNames.Length)
{
if (mainContext.lastmusicid != musicid)
{
DebugLog("new music:" + musicid + ":" + StaticData.MusicNames[musicid]);
mainContext.GameMusic = StaticData.MusicNames[musicid];
var bossmusicflag = StaticData.BossMusics.Contains(musicid);
if (bossmusicflag)
{
if (mainContext.bossbattle)
{
//直接换boss曲
if (mainContext.MusicStart || mainContext.MusicEnd)
{
sendsplit();
DebugLog("new boss music, split");
}
debugContext.BossEvent = true;
mainContext.lastmusicid = musicid;
return;
}
}
if (!mainContext.bossbattle)
{
if (musicid == 54 && mainContext.AliusI)
{
mainContext.bossbattle = false;
debugContext.BossEvent = false;
mainContext.AliusI = false;
DebugLog("Alius music, ignore once");
}
if (musicid == 42 && mapid == 1 && mainContext.Irisu1)
{
mainContext.bossbattle = false;
debugContext.BossEvent = false;
DebugLog("Irisu P1, ignore");
}
else
{
if (bossmusicflag)
{
if (mapid == 5 && musicid == 44 && mainContext.SideCh)
{
mainContext.bossbattle = false;
debugContext.BossEvent = false;
DebugLog("sidechapter, ignore");
}
else
{
mainContext.bossbattle = true;
debugContext.BossEvent = true;
mainContext.lastbosslist = new List<int>();
mainContext.lastnoah3hp = -1;
if (mainContext.MusicStart)
{
sendsplit();
DebugLog("music start, split");
}
}
}
}
}
else
{
if (!bossmusicflag) //boss music end!
{
mainContext.bossbattle = false;
if (mainContext.MusicEnd)
{
sendsplit();
DebugLog("music end, split");
}
}
}
mainContext.lastmusicid = musicid;
}
}
else
{
mainContext.GameMusic = "N/A";
}
#endregion Music
#region SpecialBOSS
if (mainContext.bossbattle)
{
if (mainContext.MiruDe || false)//todo noah3 option
{
int Noah3HP = -1;
if (mapid >= 0 && mapid < StaticData.MapNames.Length)
{
int ptr = MemoryHelper.GetMemoryValue<int>(process, StaticData.EnenyPtrAddr[mainContext.veridx]);
List<int> bosses = new List<int>();
for (var i = 0; i < 50; i++)
{
ptr = ptr + StaticData.EnenyEntitySize[mainContext.veridx];
var emyid = MemoryHelper.GetMemoryValue<int>(process,
ptr + StaticData.EnenyEnitiyIDOffset[mainContext.veridx], false);
if (StaticData.BossNames.ContainsKey(emyid))
{
bosses.Add(emyid);
if (emyid == 1053)
{
Noah3HP = MemoryHelper.GetMemoryValue<int>(process,
ptr + StaticData.EnenyEnitiyHPOffset[mainContext.veridx], false);
}
}
}
if (mainContext.MiruDe)
{
foreach (var boss in mainContext.lastbosslist)
{
if (boss == 1043)
{
if (!bosses.Contains(boss)) //despawn
{
sendsplit();
DebugLog("miru despawn, split");
mainContext.bossbattle = false;
}
}
}
}
// if (cbBoss3.Checked)
// {
// if (bosses.Contains(1053) && Noah3HP < lastnoah3hp && Noah3HP == 1)
// {
// sendsplit();
// DebugLog("noah3 hp 1, split");
// bossbattle = false;
// }
// }
if (mainContext.Tm2 && musicid == 8)
{
bool f = true;
foreach (var boss in mainContext.lastbosslist)
{
if (boss == 1024)
{
if (!bosses.Contains(boss)) //despawn
{
sendsplit();
DebugLog("nixie despawn, split");
mainContext.bossbattle = false;
f = false;
break;
}
}
}
int newTM = MemoryHelper.GetMemoryValue<int>(process, StaticData.TownMemberAddr[mainContext.veridx]);
if (newTM - mainContext.lastTM == 1 && f) //for after 1.71 , 1.71 isn't TM+2 at once when skip Nixie, it's TM+1 twice
{
if (DateTime.Now - mainContext.LastTMAddTime < TimeSpan.FromSeconds(1))
{
var d = DateTime.Now - mainContext.LastTMAddTime;
mainContext.bossbattle = false;
sendsplit();
DebugLog("TM+2 in " + d.TotalMilliseconds + " ms, split");
}
mainContext.LastTMAddTime = DateTime.Now;
}
else if (newTM - mainContext.lastTM == 2 && f)//for 1.65-1.70
{
mainContext.bossbattle = false;
sendsplit();
DebugLog("TM+2, split");
}
mainContext.lastTM = newTM;
}
mainContext.lastbosslist = bosses;
mainContext.lastnoah3hp = Noah3HP;
}
}
}
#endregion SpecialBOSS
if (mainContext.DebugArea)
{
int ptr = MemoryHelper.GetMemoryValue<int>(process, StaticData.EnenyPtrAddr[mainContext.veridx]);
// List<int> bosses = new List<int>();
// List<int> HPS = new List<int>();
debugContext.BossList.Clear();
// ptr += StaticData.EnenyEntitySize[mainContext.veridx] * 3;
for (var i = 0; i < 50; i++)
{
ptr += StaticData.EnenyEntitySize[mainContext.veridx];
debugContext.BossList.Add(new BossData()
{
BossIdx = i,
BossID = MemoryHelper.GetMemoryValue<int>(process,
ptr + StaticData.EnenyEnitiyIDOffset[mainContext.veridx], false),
BossHP = MemoryHelper.GetMemoryValue<int>(process,
ptr + StaticData.EnenyEnitiyHPOffset[mainContext.veridx], false)
});
// this.Invoke(new Action(() =>
// {
// t1.Text = string.Join("\n", bosses);
// t2.Text = string.Join("\n", HPS);
// }));
}
}
debugContext.BossEvent = mainContext.bossbattle;
}
else
{
mainContext.oldtitle = "";
mainContext.GameVer = "Not Found";
mainContext.GameMusic = "N/A";
}
}
private void DebugLog(string log)
{
this.debugContext.DebugLog += log + "\n";
}
private void sendsplit()
{
if (tcpclient != null && tcpclient.Connected)
{
try
{
var b = Encoding.UTF8.GetBytes("split\r\n");
networkStream.Write(b, 0, b.Length);
}
catch (Exception)
{
disconnect();
}
}
}
private void sendigt(float time)
{
if (tcpclient != null && tcpclient.Connected)
{
try
{
var b = Encoding.UTF8.GetBytes($"setgametime {time}\r\n");
networkStream.Write(b, 0, b.Length);
}
catch (Exception)
{
disconnect();
}
}
}
void disconnect()
{
tcpclient = null;
this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
BtnConnect.IsEnabled = true;
}));
}
public MainWindow()
{
InitializeComponent();
mainContext=new MainContext();
debugContext=new DebugContext();
this.DataContext = mainContext;
DebugPanel.DataContext = debugContext;
memoryThread = new Thread(() =>
{
while (true)
{
ReadMemory();
Thread.Sleep(1000 / 60);
}
});
memoryThread.IsBackground = true;
memoryThread.Start();
}
private void BtnConnect_Click(object sender, RoutedEventArgs e)
{
if (tcpclient != null && tcpclient.Connected) return;
try
{
tcpclient = new TcpClient("127.0.0.1", Convert.ToInt32(mainContext.ServerPort));
networkStream = tcpclient.GetStream();
this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
BtnConnect.IsEnabled = false;
}));
}
catch (Exception)
{
tcpclient = null;
networkStream = null;
MessageBox.Show(this, "Connect Failed");
}
}
private void TextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
Process.Start("https://github.com/copyliu/rabiribi_splitter");
}
}
}

@ -0,0 +1,97 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace rabi_splitter_WPF
{
public static class MemoryHelper
{
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(int hProcess,
int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
private const int PROCESS_WM_READ = 0x0010;
public static T GetMemoryValue<T>(Process process, int addr,bool baseaddr=true)
{
int datasize;
switch (Type.GetTypeCode(typeof(T)))
{
case TypeCode.Boolean:
case TypeCode.Byte:
case TypeCode.Char:
case TypeCode.SByte:
datasize = 1;
break;
case TypeCode.Int16:
case TypeCode.UInt16:
datasize = 2;
break;
case TypeCode.Int32:
case TypeCode.UInt32:
datasize = 4;
break;
case TypeCode.Int64:
case TypeCode.UInt64:
datasize = 8;
break;
default:
throw new Exception("not supported");
}
byte[] buffer = new byte[datasize];
int bytesRead = 0;
IntPtr processHandle = OpenProcess(PROCESS_WM_READ, false, process.Id);
if (baseaddr)
{ ReadProcessMemory((int) processHandle, process.MainModule.BaseAddress.ToInt32() + addr, buffer,
datasize, ref bytesRead);}
else
{
ReadProcessMemory((int)processHandle, addr, buffer,
datasize, ref bytesRead);
}
switch (Type.GetTypeCode(typeof(T)))
{
case TypeCode.Boolean:
return (T)Convert.ChangeType( buffer[0] == 1,typeof(T));
case TypeCode.Byte:
case TypeCode.SByte:
return (T)Convert.ChangeType(buffer[0] , typeof(T));
case TypeCode.Char:
return (T)Convert.ChangeType((char)buffer[0], typeof(T));
case TypeCode.Int16:
return (T)Convert.ChangeType(BitConverter.ToInt16(buffer,0), typeof(T));
case TypeCode.UInt16:
return (T)Convert.ChangeType(BitConverter.ToUInt16(buffer, 0), typeof(T));
case TypeCode.Int32:
return (T)Convert.ChangeType(BitConverter.ToInt32(buffer, 0), typeof(T));
case TypeCode.UInt32:
return (T)Convert.ChangeType(BitConverter.ToUInt32(buffer, 0), typeof(T));
case TypeCode.Int64:
return (T)Convert.ChangeType(BitConverter.ToInt64(buffer, 0), typeof(T));
case TypeCode.UInt64:
return (T)Convert.ChangeType(BitConverter.ToUInt64(buffer, 0), typeof(T));
default:
throw new Exception("not supported");
}
}
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,55 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("rabi_splitter_WPF")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("rabi_splitter_WPF")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
//若要开始生成可本地化的应用程序,请设置
//.csproj 文件中的 <UICulture>CultureYouAreCodingWith</UICulture>
//例如,如果您在源文件中使用的是美国英语,
//使用的是美国英语,请将 <UICulture> 设置为 en-US。 然后取消
//对以下 NeutralResourceLanguage 特性的注释。 更新
//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //主题特定资源词典所处位置
//(未在页面中找到资源时使用,
//或应用程序资源字典中找到时使用)
ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置
//(未在页面中找到资源时使用,
//、应用程序或任何主题专用资源字典中找到时使用)
)]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
// 方法是按如下所示使用“*”: :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

@ -0,0 +1,71 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本: 4.0.30319.42000
//
// 对此文件的更改可能导致不正确的行为,如果
// 重新生成代码,则所做更改将丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace rabi_splitter_WPF.Properties
{
/// <summary>
/// 强类型资源类,用于查找本地化字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或删除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
}
/// <summary>
/// 返回此类使用的缓存 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("rabi_splitter_WPF.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 覆盖当前线程的 CurrentUICulture 属性
/// 使用此强类型的资源类的资源查找。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace rabi_splitter_WPF.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

@ -0,0 +1,168 @@
using System.Collections.Generic;
namespace rabi_splitter_WPF
{
public static class StaticData
{
public static Dictionary<int, string> BossNames = new Dictionary<int, string>()
{
{1009, "Cocoa"},
{1011, "Rumi"},
{1012, "Ashuri"},
{1013, "Rita"},
{1014, "Ribbon"},
{1015, "Cocoa"},
{1018, "Cicini"},
{1020, "Saya"},
{1021, "Syaro"},
{1022, "Pandora"},
{1023, "Nieve"},
{1024, "Nixie"},
{1025, "Aruraune"},
{1030, "Seana"},
{1031, "Lilith"},
{1032, "Vanilla"},
{1033, "Chocolate"},
{1035, "Illusion Alius"},
{1036, "Pink Kotri"},
{1037, "Noah 1"},
{1038, "Irisu"},
{1039, "Miriam"},
{1043, "Miru"},
{1053, "Noah 3"},
{1054, "Keke Bunny"},
};
public static string[] MapNames = new string[]
{
"Southern Woodland",
"Western Coast",
"Island Core",
"Northern Tundra",
"Eastern Highlands",
"Rabi Rabi Town",
"Plurkwood",
"Subterranean Area",
"Warp Destination",
"System Interior",
};
public static int[][] MapBoss = new int[][]
{
new[] {1011, 1009, 1025, 1014, 1018},
new[] {1036, 1038, 1031, 1022, 1012},
new[] {1032, 1036, 1030, 1033},
new[] {1024, 1023, 1013, 1030},
new[] {1012, 1020,},
new int[0],
new[] {1054},
new[] {1036, 1039},
new[] {1037, 1053, 1035, 1043},
new[] {1021},
};
public static string[] MusicNames = new[]
{
"-NO MUSIC-",
"ADVENTURE STARTS HERE",
"SPECTRAL CAVE",
"FORGOTTEN CAVE",
"UNDERWATER AMBIENT",
"LIBRARY AMBIENT",
"FORGOTTEN CAVE II",
"STARTING FOREST NIGHT",
"BOUNCE BOUNCE",
"RABI RABI BEACH",
"PANDORA'S PALACE",
"RABI RABI RAVINE",
"HOME SWEET HOME",
"RABI RABI PARK",
"INSIDE UPRPRC",
"SKY ISLAND TOWN",
"WINTER WONDERLAND",
"CYBERSPACE.EXE",
"EVERNIGHT PEAK",
"EXOTIC LABORATORY",
"GOLDEN RIVERBANK",
"FLOATING GRAVEYARD",
"SYSTEM INTERIOR II",
"AURORA PALACE",
"SPEICHER GALERIE",
"DEEP UNDER THE SEA",
"SKY-HIGH BRIDGE",
"WARP DESTINATION",
"VOLCANIC CANERNS",
"PLURKWOOD",
"ANOTHER D",
"ICY SUMMIT",
"PREPARE EVENT",
"MIDBOSS BATTLE",
"MIDSTREAM JAM",
"MIRIAM'S SHOP",
"BUNNY PANIC!!!",
"THE TRUTH NEVER SPOKEN",
"BRAWL BREAKS VER.2",
"BRAWL BREAKS",
"SANDBAG MINI GAME",
"STAFF ROLL",
"RFN - III",
"NO REMORSE",
"GET ON WITH IT",
"THEME OF RABI-RIBI 8BIT",
"THEME OF RABI-RIBI",
"FULL ON COMBAT",
"HI-TECH DUEL",
"UNFAMILIAR PLACE",
"UNFAMILIAR PLACE AGAIN",
"KITTY ATTACK",
"M.R.",
"MAIN MENU",
"SUDDEN DEATH",
"RABI RABI RAVINE VER.2",
"WASTE",
"ARTBOOK INTRO",
"RABI-RIBI PIANO TITLE",
"MISCHIEVOUS MASQUERADE",
};
public static int[] BossMusics = new[]
{
44,
38,
47,
34,
51,
43,
52,
37,
39,
42,
48,
8,
54
};
public static int[] MapAddress = { 0xA3353C, 0xA57020, 0xA5E0AC, 0xA600AC };
public static int[] EnenyPtrAddr = { 0x00940EE0, 0x00964A1C, 0x0096BA3C, 0x0096DA3C };
public static int[] EnenyEnitiyHPOffset = { 0x4c8, 0x4d0,0x4d8, 0x4d8 };
public static int[] EnenyEnitiyIDOffset = { 0x4e4,0x4ec, 0x4F4, 0x4F4 };
public static int[] EnenyEntitySize = {0x6F4, 0x6FC, 0x704, 0x704 };
public static int[] MaxEntityEntry = { 50,50,50 ,50};
public static int[] MoneyAddress = { 0xD3823C, 0xD5B9FC, 0xD63D2C, 0xD654CC };
public static string[] VerNames = {"1.65", "1.70","1.71","1.75"};
public static int[] MusicAddr =
{
0xA46294,
0xA69D98,
0xA70E28,
0x4FAB60
};
public static int[] TownMemberAddr = {0xD38934, 0xD5C0F4, 0xD63BC4, 0xD65BC4 };
public static int[] IGTAddr = { 0xD388E0, 0xD5C0A0, 0xD63B70, 0xD65B70 };
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{7BEE24D8-8C97-41A3-9F90-6C60D450C3C3}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>rabi_splitter_WPF</RootNamespace>
<AssemblyName>rabi_splitter</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>irisu.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="StaticData.cs" />
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="MainContext.cs" />
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="MemoryHelper.cs" />
<Compile Include="Properties\Annotations.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<Resource Include="irisu.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

@ -1,10 +1,12 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio 15
VisualStudioVersion = 15.0.26206.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "rabiribi_splitter", "rabiribi_splitter\rabiribi_splitter.csproj", "{1933C91E-7300-42A1-A0C5-2D54B95C5BBB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "rabi_splitter_WPF", "rabi_splitter_WPF\rabi_splitter_WPF.csproj", "{7BEE24D8-8C97-41A3-9F90-6C60D450C3C3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -15,6 +17,10 @@ Global
{1933C91E-7300-42A1-A0C5-2D54B95C5BBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1933C91E-7300-42A1-A0C5-2D54B95C5BBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1933C91E-7300-42A1-A0C5-2D54B95C5BBB}.Release|Any CPU.Build.0 = Release|Any CPU
{7BEE24D8-8C97-41A3-9F90-6C60D450C3C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7BEE24D8-8C97-41A3-9F90-6C60D450C3C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7BEE24D8-8C97-41A3-9F90-6C60D450C3C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7BEE24D8-8C97-41A3-9F90-6C60D450C3C3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

Loading…
Cancel
Save