First working version of Rhubarb Lip Sync for Spine (C#, Windows Forms)
This commit is contained in:
parent
7e7acb8068
commit
df783f9afa
|
@ -0,0 +1,63 @@
|
|||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
|
@ -0,0 +1,3 @@
|
|||
bin/
|
||||
obj/
|
||||
packages/
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "rhubarb-for-spine", "rhubarb-for-spine\rhubarb-for-spine.csproj", "{C5ED6F8A-6141-4BAE-BE24-77DEE23E495F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C5ED6F8A-6141-4BAE-BE24-77DEE23E495F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C5ED6F8A-6141-4BAE-BE24-77DEE23E495F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C5ED6F8A-6141-4BAE-BE24-77DEE23E495F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C5ED6F8A-6141-4BAE-BE24-77DEE23E495F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,147 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
public class AnimationFileModel : ModelBase {
|
||||
private readonly string animationFilePath;
|
||||
private readonly SemaphoreSlim semaphore;
|
||||
private readonly JObject json;
|
||||
|
||||
private string _mouthSlot;
|
||||
private MouthNaming _mouthNaming;
|
||||
private IReadOnlyCollection<MouthShape> _mouthShapes;
|
||||
private string _mouthShapesDisplayString;
|
||||
private BindingList<AudioFileModel> _audioFileModels;
|
||||
|
||||
public AnimationFileModel(string animationFilePath, SemaphoreSlim semaphore) {
|
||||
this.animationFilePath = animationFilePath;
|
||||
this.semaphore = semaphore;
|
||||
json = SpineJson.ReadJson(animationFilePath);
|
||||
SpineJson.ValidateJson(json, AnimationFileDirectory);
|
||||
|
||||
Slots = SpineJson.GetSlots(json);
|
||||
MouthSlot = SpineJson.GuessMouthSlot(Slots);
|
||||
var audioFileModels = SpineJson.GetAudioEvents(json)
|
||||
.Select(CreateAudioFileModel)
|
||||
.ToList();
|
||||
AudioFileModels = new BindingList<AudioFileModel>(audioFileModels);
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<string> Slots { get; }
|
||||
|
||||
public string MouthSlot {
|
||||
get { return _mouthSlot; }
|
||||
set {
|
||||
_mouthSlot = value;
|
||||
MouthNaming = GetMouthNaming();
|
||||
MouthShapes = GetMouthShapes();
|
||||
OnPropertyChanged(nameof(MouthSlot));
|
||||
}
|
||||
}
|
||||
|
||||
public MouthNaming MouthNaming {
|
||||
get { return _mouthNaming; }
|
||||
private set {
|
||||
_mouthNaming = value;
|
||||
OnPropertyChanged(nameof(MouthNaming));
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<MouthShape> MouthShapes {
|
||||
get { return _mouthShapes; }
|
||||
private set {
|
||||
_mouthShapes = value;
|
||||
MouthShapesDisplayString = GetMouthShapesDisplayString();
|
||||
OnPropertyChanged(nameof(MouthShapes));
|
||||
}
|
||||
}
|
||||
|
||||
public string MouthShapesDisplayString {
|
||||
get { return _mouthShapesDisplayString; }
|
||||
set {
|
||||
_mouthShapesDisplayString = value;
|
||||
SetError(nameof(MouthShapesDisplayString), GetMouthShapesDisplayStringError());
|
||||
OnPropertyChanged(nameof(MouthShapesDisplayString));
|
||||
}
|
||||
}
|
||||
|
||||
public BindingList<AudioFileModel> AudioFileModels {
|
||||
get { return _audioFileModels; }
|
||||
set {
|
||||
_audioFileModels = value;
|
||||
if (!_audioFileModels.Any()) {
|
||||
SetError(nameof(AudioFileModels), "The JSON file doesn't contain any audio events.");
|
||||
}
|
||||
OnPropertyChanged(nameof(AudioFileModels));
|
||||
}
|
||||
}
|
||||
|
||||
private string AnimationFileDirectory => Path.GetDirectoryName(animationFilePath);
|
||||
|
||||
private MouthNaming GetMouthNaming() {
|
||||
IReadOnlyCollection<string> mouthNames = SpineJson.GetSlotAttachmentNames(json, _mouthSlot);
|
||||
return MouthNaming.Guess(mouthNames);
|
||||
}
|
||||
|
||||
private List<MouthShape> GetMouthShapes() {
|
||||
IReadOnlyCollection<string> mouthNames = SpineJson.GetSlotAttachmentNames(json, _mouthSlot);
|
||||
return rhubarb_for_spine.MouthShapes.All
|
||||
.Where(shape => mouthNames.Contains(MouthNaming.GetName(shape)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private string GetMouthShapesDisplayString() {
|
||||
return MouthShapes
|
||||
.Select(shape => shape.ToString())
|
||||
.Join(", ");
|
||||
}
|
||||
|
||||
private string GetMouthShapesDisplayStringError() {
|
||||
var basicMouthShapes = rhubarb_for_spine.MouthShapes.Basic;
|
||||
var missingBasicShapes = basicMouthShapes
|
||||
.Where(shape => !MouthShapes.Contains(shape))
|
||||
.ToList();
|
||||
if (!missingBasicShapes.Any()) return null;
|
||||
|
||||
var result = new StringBuilder();
|
||||
string missingString = string.Join(", ", missingBasicShapes);
|
||||
result.AppendLine(missingBasicShapes.Count > 1
|
||||
? $"Mouth shapes {missingString} are missing."
|
||||
: $"Mouth shape {missingString} is missing.");
|
||||
MouthShape first = basicMouthShapes.First();
|
||||
MouthShape last = basicMouthShapes.Last();
|
||||
result.Append($"At least the basic mouth shapes {first}-{last} need corresponding image attachments.");
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private AudioFileModel CreateAudioFileModel(SpineJson.AudioEvent audioEvent) {
|
||||
string audioDirectory = SpineJson.GetAudioDirectory(json, AnimationFileDirectory);
|
||||
string filePath = Path.Combine(audioDirectory, audioEvent.RelativeAudioFilePath);
|
||||
bool animatedPreviously = SpineJson.HasAnimation(json, GetAnimationName(audioEvent.Name));
|
||||
var extendedMouthShapes = MouthShapes.Where(shape => !rhubarb_for_spine.MouthShapes.IsBasic(shape));
|
||||
return new AudioFileModel(
|
||||
audioEvent.Name, filePath, audioEvent.RelativeAudioFilePath, audioEvent.Dialog,
|
||||
new HashSet<MouthShape>(extendedMouthShapes), animatedPreviously,
|
||||
semaphore, cues => AcceptAnimationResult(cues, audioEvent));
|
||||
}
|
||||
|
||||
private string GetAnimationName(string audioEventName) {
|
||||
return $"say_{audioEventName}";
|
||||
}
|
||||
|
||||
private void AcceptAnimationResult(
|
||||
IReadOnlyCollection<MouthCue> mouthCues, SpineJson.AudioEvent audioEvent
|
||||
) {
|
||||
string animationName = GetAnimationName(audioEvent.Name);
|
||||
SpineJson.CreateOrUpdateAnimation(
|
||||
json, mouthCues, audioEvent.Name, animationName, MouthSlot, MouthNaming);
|
||||
File.WriteAllText(animationFilePath, json.ToString(Formatting.Indented));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
public class AudioFileModel : ModelBase {
|
||||
private readonly string filePath;
|
||||
private readonly ISet<MouthShape> extendedMouthShapes;
|
||||
private readonly SemaphoreSlim semaphore;
|
||||
private readonly Action<IReadOnlyCollection<MouthCue>> reportResult;
|
||||
private bool animatedPreviously;
|
||||
private CancellationTokenSource cancellationTokenSource;
|
||||
|
||||
private AudioFileStatus _status;
|
||||
private string _actionLabel;
|
||||
private double? _animationProgress;
|
||||
|
||||
public AudioFileModel(
|
||||
string name,
|
||||
string filePath,
|
||||
string displayFilePath,
|
||||
string dialog,
|
||||
ISet<MouthShape> extendedMouthShapes,
|
||||
bool animatedPreviously,
|
||||
SemaphoreSlim semaphore,
|
||||
Action<IReadOnlyCollection<MouthCue>> reportResult
|
||||
) {
|
||||
this.reportResult = reportResult;
|
||||
Name = name;
|
||||
this.filePath = filePath;
|
||||
this.extendedMouthShapes = extendedMouthShapes;
|
||||
this.animatedPreviously = animatedPreviously;
|
||||
this.semaphore = semaphore;
|
||||
DisplayFilePath = displayFilePath;
|
||||
Dialog = dialog;
|
||||
Status = animatedPreviously ? AudioFileStatus.Done : AudioFileStatus.NotAnimated;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public string DisplayFilePath { get; }
|
||||
public string Dialog { get; }
|
||||
|
||||
public double? AnimationProgress {
|
||||
get { return _animationProgress; }
|
||||
private set {
|
||||
_animationProgress = value;
|
||||
OnPropertyChanged(nameof(AnimationProgress));
|
||||
|
||||
// Hack, so that a binding to Status will refresh
|
||||
OnPropertyChanged(nameof(Status));
|
||||
}
|
||||
}
|
||||
|
||||
public AudioFileStatus Status {
|
||||
get { return _status; }
|
||||
private set {
|
||||
_status = value;
|
||||
ActionLabel = _status == AudioFileStatus.Scheduled || _status == AudioFileStatus.Animating
|
||||
? "Cancel"
|
||||
: "Animate";
|
||||
OnPropertyChanged(nameof(Status));
|
||||
}
|
||||
}
|
||||
|
||||
public string ActionLabel {
|
||||
get { return _actionLabel; }
|
||||
private set {
|
||||
_actionLabel = value;
|
||||
OnPropertyChanged(nameof(ActionLabel));
|
||||
}
|
||||
}
|
||||
|
||||
public void PerformAction() {
|
||||
switch (Status) {
|
||||
case AudioFileStatus.NotAnimated:
|
||||
case AudioFileStatus.Done:
|
||||
StartAnimation();
|
||||
break;
|
||||
case AudioFileStatus.Scheduled:
|
||||
case AudioFileStatus.Animating:
|
||||
CancelAnimation();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
private async void StartAnimation() {
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
Status = AudioFileStatus.Scheduled;
|
||||
try {
|
||||
await semaphore.WaitAsync(cancellationTokenSource.Token);
|
||||
Status = AudioFileStatus.Animating;
|
||||
try {
|
||||
var progress = new Progress(value => AnimationProgress = value);
|
||||
IReadOnlyCollection<MouthCue> mouthCues = await RhubarbCli.AnimateAsync(
|
||||
filePath, Dialog, extendedMouthShapes, progress, cancellationTokenSource.Token);
|
||||
animatedPreviously = true;
|
||||
Status = AudioFileStatus.Done;
|
||||
reportResult(mouthCues);
|
||||
} finally {
|
||||
AnimationProgress = null;
|
||||
semaphore.Release();
|
||||
cancellationTokenSource = null;
|
||||
}
|
||||
} catch (OperationCanceledException) {
|
||||
Status = animatedPreviously ? AudioFileStatus.Done : AudioFileStatus.NotAnimated;
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelAnimation() {
|
||||
cancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
private class Progress : IProgress<double> {
|
||||
private readonly Action<double> report;
|
||||
private double value;
|
||||
|
||||
public Progress(Action<double> report) {
|
||||
this.report = report;
|
||||
}
|
||||
|
||||
public void Report(double progress) {
|
||||
value = progress;
|
||||
report(progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum AudioFileStatus {
|
||||
NotAnimated,
|
||||
Scheduled,
|
||||
Animating,
|
||||
Done
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
/// <summary>
|
||||
/// A modification of the standard <see cref="ComboBox"/> in which a data binding
|
||||
/// on the SelectedItem property with the update mode set to DataSourceUpdateMode.OnPropertyChanged
|
||||
/// actually updates when a selection is made in the combobox.
|
||||
/// Code taken from https://stackoverflow.com/a/8392100/52041
|
||||
/// </summary>
|
||||
public class BindableComboBox : ComboBox {
|
||||
/// <inheritdoc />
|
||||
protected override void OnSelectionChangeCommitted(EventArgs e) {
|
||||
base.OnSelectionChangeCommitted(e);
|
||||
|
||||
var bindings = DataBindings
|
||||
.Cast<Binding>()
|
||||
.Where(binding => binding.PropertyName == nameof(SelectedItem)
|
||||
&& binding.DataSourceUpdateMode == DataSourceUpdateMode.OnPropertyChanged);
|
||||
foreach (Binding binding in bindings) {
|
||||
// Force the binding to update from the new SelectedItem
|
||||
binding.WriteValue();
|
||||
|
||||
// Force the Textbox to update from the binding
|
||||
binding.ReadValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,366 @@
|
|||
using System;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
partial class MainForm {
|
||||
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
protected override void OnLoad(EventArgs e) {
|
||||
base.OnLoad(e);
|
||||
|
||||
// Some bindings cannot be added in InitializeComponent; see https://stackoverflow.com/a/47167781/52041.
|
||||
mouthSlotComboBox.DataBindings.Add(new System.Windows.Forms.Binding("DataSource", animationFileBindingSource, "Slots", true));
|
||||
mouthSlotComboBox.DataBindings.Add(new System.Windows.Forms.Binding("SelectedItem", animationFileBindingSource, "MouthSlot", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
audioEventsDataGridView.AutoGenerateColumns = false;
|
||||
audioEventsDataGridView.DataBindings.Add(new System.Windows.Forms.Binding("DataSource", animationFileBindingSource, "AudioFileModels", true));
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
|
||||
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.filePathLabel = new System.Windows.Forms.Label();
|
||||
this.panel1 = new System.Windows.Forms.Panel();
|
||||
this.filePathTextBox = new System.Windows.Forms.TextBox();
|
||||
this.filePathBrowseButton = new System.Windows.Forms.Button();
|
||||
this.mouthSlotLabel = new System.Windows.Forms.Label();
|
||||
this.animationFileBindingSource = new System.Windows.Forms.BindingSource(this.components);
|
||||
this.mouthShapesLabel = new System.Windows.Forms.Label();
|
||||
this.audioEventsLabel = new System.Windows.Forms.Label();
|
||||
this.audioEventsDataGridView = new System.Windows.Forms.DataGridView();
|
||||
this.mouthShapesResultLabel = new System.Windows.Forms.Label();
|
||||
this.mouthNamingLabel = new System.Windows.Forms.Label();
|
||||
this.mouthNamingResultLabel = new System.Windows.Forms.Label();
|
||||
this.mainErrorProvider = new System.Windows.Forms.ErrorProvider(this.components);
|
||||
this.animationFileErrorProvider = new System.Windows.Forms.ErrorProvider(this.components);
|
||||
this.mainBindingSource = new System.Windows.Forms.BindingSource(this.components);
|
||||
this.mouthSlotComboBox = new rhubarb_for_spine.BindableComboBox();
|
||||
this.eventColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.audioFileColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.dialogColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.statusColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||
this.actionColumn = new System.Windows.Forms.DataGridViewButtonColumn();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.panel1.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.animationFileBindingSource)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.audioEventsDataGridView)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.mainErrorProvider)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.animationFileErrorProvider)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.mainBindingSource)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.AutoSize = true;
|
||||
this.tableLayoutPanel1.ColumnCount = 2;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.filePathLabel, 0, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.panel1, 1, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.mouthSlotLabel, 0, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.mouthSlotComboBox, 1, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.mouthShapesLabel, 0, 3);
|
||||
this.tableLayoutPanel1.Controls.Add(this.audioEventsLabel, 0, 4);
|
||||
this.tableLayoutPanel1.Controls.Add(this.audioEventsDataGridView, 1, 4);
|
||||
this.tableLayoutPanel1.Controls.Add(this.mouthShapesResultLabel, 1, 3);
|
||||
this.tableLayoutPanel1.Controls.Add(this.mouthNamingLabel, 0, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.mouthNamingResultLabel, 1, 2);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(5, 5);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(3);
|
||||
this.tableLayoutPanel1.RowCount = 5;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(903, 612);
|
||||
this.tableLayoutPanel1.TabIndex = 0;
|
||||
//
|
||||
// filePathLabel
|
||||
//
|
||||
this.filePathLabel.AutoSize = true;
|
||||
this.filePathLabel.Location = new System.Drawing.Point(6, 3);
|
||||
this.filePathLabel.Name = "filePathLabel";
|
||||
this.filePathLabel.Padding = new System.Windows.Forms.Padding(0, 6, 0, 0);
|
||||
this.filePathLabel.Size = new System.Drawing.Size(81, 19);
|
||||
this.filePathLabel.TabIndex = 0;
|
||||
this.filePathLabel.Text = "Spine JSON file";
|
||||
//
|
||||
// panel1
|
||||
//
|
||||
this.panel1.AutoSize = true;
|
||||
this.panel1.Controls.Add(this.filePathTextBox);
|
||||
this.panel1.Controls.Add(this.filePathBrowseButton);
|
||||
this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
|
||||
this.panel1.Location = new System.Drawing.Point(93, 6);
|
||||
this.panel1.Name = "panel1";
|
||||
this.panel1.Size = new System.Drawing.Size(804, 23);
|
||||
this.panel1.TabIndex = 1;
|
||||
//
|
||||
// filePathTextBox
|
||||
//
|
||||
this.filePathTextBox.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.mainBindingSource, "FilePath", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
|
||||
this.mainErrorProvider.SetIconPadding(this.filePathTextBox, -20);
|
||||
this.filePathTextBox.Location = new System.Drawing.Point(0, 0);
|
||||
this.filePathTextBox.Name = "filePathTextBox";
|
||||
this.filePathTextBox.Size = new System.Drawing.Size(769, 20);
|
||||
this.filePathTextBox.TabIndex = 2;
|
||||
//
|
||||
// filePathBrowseButton
|
||||
//
|
||||
this.filePathBrowseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.filePathBrowseButton.Location = new System.Drawing.Point(775, 0);
|
||||
this.filePathBrowseButton.Name = "filePathBrowseButton";
|
||||
this.filePathBrowseButton.Size = new System.Drawing.Size(30, 20);
|
||||
this.filePathBrowseButton.TabIndex = 3;
|
||||
this.filePathBrowseButton.Text = "...";
|
||||
this.filePathBrowseButton.UseVisualStyleBackColor = true;
|
||||
this.filePathBrowseButton.Click += new System.EventHandler(this.filePathBrowseButton_Click);
|
||||
//
|
||||
// mouthSlotLabel
|
||||
//
|
||||
this.mouthSlotLabel.AutoSize = true;
|
||||
this.mouthSlotLabel.Location = new System.Drawing.Point(6, 32);
|
||||
this.mouthSlotLabel.Name = "mouthSlotLabel";
|
||||
this.mouthSlotLabel.Padding = new System.Windows.Forms.Padding(0, 6, 0, 0);
|
||||
this.mouthSlotLabel.Size = new System.Drawing.Size(56, 19);
|
||||
this.mouthSlotLabel.TabIndex = 2;
|
||||
this.mouthSlotLabel.Text = "Mouth slot";
|
||||
//
|
||||
// animationFileBindingSource
|
||||
//
|
||||
this.animationFileBindingSource.DataMember = "AnimationFileModel";
|
||||
this.animationFileBindingSource.DataSource = this.mainBindingSource;
|
||||
//
|
||||
// mouthShapesLabel
|
||||
//
|
||||
this.mouthShapesLabel.AutoSize = true;
|
||||
this.mouthShapesLabel.Location = new System.Drawing.Point(6, 79);
|
||||
this.mouthShapesLabel.Name = "mouthShapesLabel";
|
||||
this.mouthShapesLabel.Padding = new System.Windows.Forms.Padding(0, 6, 0, 0);
|
||||
this.mouthShapesLabel.Size = new System.Drawing.Size(74, 19);
|
||||
this.mouthShapesLabel.TabIndex = 4;
|
||||
this.mouthShapesLabel.Text = "Mouth shapes";
|
||||
//
|
||||
// audioEventsLabel
|
||||
//
|
||||
this.audioEventsLabel.AutoSize = true;
|
||||
this.audioEventsLabel.Location = new System.Drawing.Point(6, 98);
|
||||
this.audioEventsLabel.Name = "audioEventsLabel";
|
||||
this.audioEventsLabel.Padding = new System.Windows.Forms.Padding(0, 6, 0, 0);
|
||||
this.audioEventsLabel.Size = new System.Drawing.Size(69, 19);
|
||||
this.audioEventsLabel.TabIndex = 6;
|
||||
this.audioEventsLabel.Text = "Audio events";
|
||||
//
|
||||
// audioEventsDataGridView
|
||||
//
|
||||
this.audioEventsDataGridView.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCellsExceptHeaders;
|
||||
this.audioEventsDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||
this.audioEventsDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
|
||||
this.eventColumn,
|
||||
this.audioFileColumn,
|
||||
this.dialogColumn,
|
||||
this.statusColumn,
|
||||
this.actionColumn});
|
||||
dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
|
||||
dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window;
|
||||
dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText;
|
||||
dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Window;
|
||||
dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.ControlText;
|
||||
dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
|
||||
this.audioEventsDataGridView.DefaultCellStyle = dataGridViewCellStyle2;
|
||||
this.audioEventsDataGridView.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.animationFileErrorProvider.SetIconPadding(this.audioEventsDataGridView, -20);
|
||||
this.audioEventsDataGridView.Location = new System.Drawing.Point(93, 101);
|
||||
this.audioEventsDataGridView.Name = "audioEventsDataGridView";
|
||||
this.audioEventsDataGridView.ReadOnly = true;
|
||||
this.audioEventsDataGridView.RowHeadersVisible = false;
|
||||
this.audioEventsDataGridView.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
|
||||
this.audioEventsDataGridView.Size = new System.Drawing.Size(804, 505);
|
||||
this.audioEventsDataGridView.TabIndex = 7;
|
||||
this.audioEventsDataGridView.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.audioEventsDataGridView_CellClick);
|
||||
this.audioEventsDataGridView.CellPainting += new System.Windows.Forms.DataGridViewCellPaintingEventHandler(this.audioEventsDataGridView_CellPainting);
|
||||
//
|
||||
// mouthShapesResultLabel
|
||||
//
|
||||
this.mouthShapesResultLabel.AutoSize = true;
|
||||
this.mouthShapesResultLabel.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.animationFileBindingSource, "MouthShapesDisplayString", true));
|
||||
this.mouthShapesResultLabel.Location = new System.Drawing.Point(93, 79);
|
||||
this.mouthShapesResultLabel.Name = "mouthShapesResultLabel";
|
||||
this.mouthShapesResultLabel.Padding = new System.Windows.Forms.Padding(0, 6, 0, 0);
|
||||
this.mouthShapesResultLabel.Size = new System.Drawing.Size(16, 19);
|
||||
this.mouthShapesResultLabel.TabIndex = 8;
|
||||
this.mouthShapesResultLabel.Text = " ";
|
||||
//
|
||||
// mouthNamingLabel
|
||||
//
|
||||
this.mouthNamingLabel.AutoSize = true;
|
||||
this.mouthNamingLabel.Location = new System.Drawing.Point(6, 59);
|
||||
this.mouthNamingLabel.Name = "mouthNamingLabel";
|
||||
this.mouthNamingLabel.Padding = new System.Windows.Forms.Padding(0, 6, 0, 0);
|
||||
this.mouthNamingLabel.Size = new System.Drawing.Size(74, 19);
|
||||
this.mouthNamingLabel.TabIndex = 9;
|
||||
this.mouthNamingLabel.Text = "Mouth naming";
|
||||
//
|
||||
// mouthNamingResultLabel
|
||||
//
|
||||
this.mouthNamingResultLabel.AutoSize = true;
|
||||
this.mouthNamingResultLabel.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.animationFileBindingSource, "MouthNaming.DisplayString", true));
|
||||
this.mouthNamingResultLabel.Location = new System.Drawing.Point(93, 59);
|
||||
this.mouthNamingResultLabel.Name = "mouthNamingResultLabel";
|
||||
this.mouthNamingResultLabel.Padding = new System.Windows.Forms.Padding(0, 6, 0, 0);
|
||||
this.mouthNamingResultLabel.Size = new System.Drawing.Size(16, 19);
|
||||
this.mouthNamingResultLabel.TabIndex = 10;
|
||||
this.mouthNamingResultLabel.Text = " ";
|
||||
//
|
||||
// mainErrorProvider
|
||||
//
|
||||
this.mainErrorProvider.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.NeverBlink;
|
||||
this.mainErrorProvider.ContainerControl = this;
|
||||
this.mainErrorProvider.DataSource = this.mainBindingSource;
|
||||
//
|
||||
// animationFileErrorProvider
|
||||
//
|
||||
this.animationFileErrorProvider.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.NeverBlink;
|
||||
this.animationFileErrorProvider.ContainerControl = this;
|
||||
this.animationFileErrorProvider.DataSource = this.animationFileBindingSource;
|
||||
//
|
||||
// mainBindingSource
|
||||
//
|
||||
this.mainBindingSource.DataSource = typeof(rhubarb_for_spine.MainModel);
|
||||
//
|
||||
// mouthSlotComboBox
|
||||
//
|
||||
this.mouthSlotComboBox.Dock = System.Windows.Forms.DockStyle.Top;
|
||||
this.mouthSlotComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.mouthSlotComboBox.Location = new System.Drawing.Point(93, 35);
|
||||
this.mouthSlotComboBox.Name = "mouthSlotComboBox";
|
||||
this.mouthSlotComboBox.Size = new System.Drawing.Size(804, 21);
|
||||
this.mouthSlotComboBox.TabIndex = 3;
|
||||
//
|
||||
// eventColumn
|
||||
//
|
||||
this.eventColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
|
||||
this.eventColumn.DataPropertyName = "Name";
|
||||
this.eventColumn.HeaderText = "Event";
|
||||
this.eventColumn.Name = "eventColumn";
|
||||
this.eventColumn.ReadOnly = true;
|
||||
//
|
||||
// audioFileColumn
|
||||
//
|
||||
this.audioFileColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
|
||||
this.audioFileColumn.DataPropertyName = "DisplayFilePath";
|
||||
this.audioFileColumn.HeaderText = "Audio file";
|
||||
this.audioFileColumn.Name = "audioFileColumn";
|
||||
this.audioFileColumn.ReadOnly = true;
|
||||
//
|
||||
// dialogColumn
|
||||
//
|
||||
this.dialogColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
|
||||
this.dialogColumn.DataPropertyName = "Dialog";
|
||||
dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
|
||||
this.dialogColumn.DefaultCellStyle = dataGridViewCellStyle1;
|
||||
this.dialogColumn.FillWeight = 300F;
|
||||
this.dialogColumn.HeaderText = "Dialog";
|
||||
this.dialogColumn.Name = "dialogColumn";
|
||||
this.dialogColumn.ReadOnly = true;
|
||||
//
|
||||
// statusColumn
|
||||
//
|
||||
this.statusColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill;
|
||||
this.statusColumn.DataPropertyName = "Status";
|
||||
this.statusColumn.HeaderText = "Status";
|
||||
this.statusColumn.Name = "statusColumn";
|
||||
this.statusColumn.ReadOnly = true;
|
||||
//
|
||||
// actionColumn
|
||||
//
|
||||
this.actionColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
|
||||
this.actionColumn.DataPropertyName = "ActionLabel";
|
||||
this.actionColumn.HeaderText = "";
|
||||
this.actionColumn.Name = "actionColumn";
|
||||
this.actionColumn.ReadOnly = true;
|
||||
this.actionColumn.Resizable = System.Windows.Forms.DataGridViewTriState.True;
|
||||
this.actionColumn.Text = "";
|
||||
this.actionColumn.Width = 80;
|
||||
//
|
||||
// MainForm
|
||||
//
|
||||
this.AllowDrop = true;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(913, 622);
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Name = "MainForm";
|
||||
this.Padding = new System.Windows.Forms.Padding(5);
|
||||
this.Text = "Rhubarb Lip Sync for Spine";
|
||||
this.DragDrop += new System.Windows.Forms.DragEventHandler(this.MainForm_DragDrop);
|
||||
this.DragEnter += new System.Windows.Forms.DragEventHandler(this.MainForm_DragEnter);
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.panel1.ResumeLayout(false);
|
||||
this.panel1.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.animationFileBindingSource)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.audioEventsDataGridView)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.mainErrorProvider)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.animationFileErrorProvider)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.mainBindingSource)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Label filePathLabel;
|
||||
private System.Windows.Forms.Panel panel1;
|
||||
private System.Windows.Forms.TextBox filePathTextBox;
|
||||
private System.Windows.Forms.Button filePathBrowseButton;
|
||||
private System.Windows.Forms.Label mouthSlotLabel;
|
||||
private BindableComboBox mouthSlotComboBox;
|
||||
private System.Windows.Forms.Label mouthShapesLabel;
|
||||
private System.Windows.Forms.Label audioEventsLabel;
|
||||
private System.Windows.Forms.DataGridView audioEventsDataGridView;
|
||||
private System.Windows.Forms.BindingSource mainBindingSource;
|
||||
private System.Windows.Forms.ErrorProvider mainErrorProvider;
|
||||
private System.Windows.Forms.Label mouthShapesResultLabel;
|
||||
private System.Windows.Forms.Label mouthNamingLabel;
|
||||
private System.Windows.Forms.Label mouthNamingResultLabel;
|
||||
private System.Windows.Forms.BindingSource animationFileBindingSource;
|
||||
private System.Windows.Forms.ErrorProvider animationFileErrorProvider;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn eventColumn;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn audioFileColumn;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn dialogColumn;
|
||||
private System.Windows.Forms.DataGridViewTextBoxColumn statusColumn;
|
||||
private System.Windows.Forms.DataGridViewButtonColumn actionColumn;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
public partial class MainForm : Form {
|
||||
private MainModel MainModel { get; }
|
||||
|
||||
public MainForm(MainModel mainModel) {
|
||||
InitializeComponent();
|
||||
MainModel = mainModel;
|
||||
mainBindingSource.DataSource = mainModel;
|
||||
}
|
||||
|
||||
private void filePathBrowseButton_Click(object sender, EventArgs e) {
|
||||
var openFileDialog = new OpenFileDialog {
|
||||
Filter = "JSON files (*.json)|*.json",
|
||||
InitialDirectory = filePathTextBox.Text
|
||||
};
|
||||
if (openFileDialog.ShowDialog() == DialogResult.OK) {
|
||||
filePathTextBox.Text = openFileDialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
private void MainForm_DragEnter(object sender, DragEventArgs e) {
|
||||
if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
|
||||
e.Effect = DragDropEffects.Copy;
|
||||
}
|
||||
}
|
||||
|
||||
private void MainForm_DragDrop(object sender, DragEventArgs e) {
|
||||
var files = (string[]) e.Data.GetData(DataFormats.FileDrop);
|
||||
if (files.Any()) {
|
||||
filePathTextBox.Text = files.First();
|
||||
}
|
||||
}
|
||||
|
||||
private void audioEventsDataGridView_CellClick(object sender, DataGridViewCellEventArgs e) {
|
||||
if (e.ColumnIndex != actionColumn.Index) return;
|
||||
|
||||
AudioFileModel audioFileModel = GetAudioFileModel(e.RowIndex);
|
||||
audioFileModel?.PerformAction();
|
||||
}
|
||||
|
||||
private void audioEventsDataGridView_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) {
|
||||
if (e.ColumnIndex != statusColumn.Index || e.RowIndex == -1) return;
|
||||
|
||||
e.PaintBackground(e.CellBounds, false);
|
||||
AudioFileModel audioFileModel = GetAudioFileModel(e.RowIndex);
|
||||
if (audioFileModel == null) return;
|
||||
|
||||
string text = string.Empty;
|
||||
StringAlignment horizontalTextAlignment = StringAlignment.Near;
|
||||
Color backgroundColor = Color.Transparent;
|
||||
double? progress = null;
|
||||
switch (audioFileModel.Status) {
|
||||
case AudioFileStatus.NotAnimated:
|
||||
break;
|
||||
case AudioFileStatus.Scheduled:
|
||||
text = "Waiting";
|
||||
backgroundColor = SystemColors.Highlight.WithOpacity(0.2);
|
||||
break;
|
||||
case AudioFileStatus.Animating:
|
||||
progress = audioFileModel.AnimationProgress ?? 0.0;
|
||||
text = $"{(int) (progress * 100)}%";
|
||||
horizontalTextAlignment = StringAlignment.Center;
|
||||
break;
|
||||
case AudioFileStatus.Done:
|
||||
text = "Done";
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
// Draw background
|
||||
var bounds = e.CellBounds;
|
||||
e.Graphics.FillRectangle(new SolidBrush(backgroundColor), bounds);
|
||||
|
||||
// Draw progress bar
|
||||
if (progress.HasValue) {
|
||||
e.Graphics.FillRectangle(
|
||||
SystemBrushes.Highlight,
|
||||
bounds.X, bounds.Y, bounds.Width * (float) progress, bounds.Height);
|
||||
}
|
||||
|
||||
// Draw text
|
||||
var stringFormat = new StringFormat {
|
||||
Alignment = horizontalTextAlignment,
|
||||
LineAlignment = StringAlignment.Center
|
||||
};
|
||||
e.Graphics.DrawString(
|
||||
text,
|
||||
e.CellStyle.Font, new SolidBrush(e.CellStyle.ForeColor),
|
||||
bounds, stringFormat);
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private AudioFileModel GetAudioFileModel(int rowIndex) {
|
||||
return rowIndex < 0
|
||||
? null
|
||||
: MainModel.AnimationFileModel?.AudioFileModels[rowIndex];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,521 @@
|
|||
<?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.Runtime.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:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<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" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="mainBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="mainErrorProvider.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>209, 17</value>
|
||||
</metadata>
|
||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>67</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
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
|
||||
KACJUEvJ7Rf2A3d1MKefic/TkDT6AUIBSJgyiDgShSCoqL46ugDjhsHD2OeiSDHS4DOAAjBgyilEGzdo
|
||||
5IQCQEjGUAAIyRgKACEZQwEgJGMoAIRkDAWAkIyhABCSMRQAQjKGAkBIxlAACMkYCgAhGUMBICRjKACE
|
||||
ZAwFgJCMoQAQkjEUAEIyhgJASMZQAAjJGAoAIRlDASAkYygAhGQMBYCQjKEAEJIxFABCMub/Ad+EZyuP
|
||||
PIEJAAAAAElFTkSuQmCCKAAAADAAAABgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAASUpKEElKSkBJSkpASUpKQElKSkBJSkoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAASUpKEElKSoBJSkrPSUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpKr0lKSmAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAABJSkqASUpK70lKSt9JSkqPSUpKQElKSkAAAAAASUpKMElKSkBJSkqASUpKz0lKSv9J
|
||||
SkrfSUpKUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAASUpKEElKSr9JSkrfSUpKYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAElKSiBJSkqfSUpK/0lKSq9JSkoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkoQSUpKz0lKSo8AAAAAAAAAAAAAAAAAAAAAAAAAAElKSkBJ
|
||||
SkpASUpKQElKSkAAAAAAAAAAAAAAAAAAAAAASUpKQElKSt9JSkrPSUpKEAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKShBJSkrPSUpKYAAAAAAAAAAASUpKEElKSoBJ
|
||||
SkrPSUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpKr0lKSmAAAAAAAAAAAElKShBJSkrPSUpKz0lKShAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSo9JSkpgAAAAAAAAAABJ
|
||||
SkpwSUpK70lKSv9JSkr/XFB3/29Wpf9vVqX/b1al/2pUmf9XTmz/SUpK/0lKSv9JSkrfSUpKQAAAAABJ
|
||||
SkoQSUpKn0lKSq8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSlAA
|
||||
AAAAAAAAAElKSp9JSkr/TktV/29Wpf+MXuj/lWH//5Vh//+VYf//lWH//5Vh//+VYf//jF7o/2pUmf9J
|
||||
Skr/SUpK/0lKSoAAAAAAAAAAAElKSr9JSkpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAASUpKn0lKSv9cUHf/jF7o/5Vh//+VYf//lWH//5Vh//+VYf//lWH//5Vh//+V
|
||||
Yf//lWH//5Vh//+MXuj/V05s/0lKSv9JSkqfAAAAAElKShBJSkpgAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkqfSUpK/2ZTjv+VYf//lWH//5Vh//+VYf//lWH//5Vh//+V
|
||||
Yf//lWH//5Vh//+VYf//lWH//5Vh//+VYf//kGD0/2ZTjv9JSkr/SUpKgAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSnBJSkr/b1al/5Vh//+VYf//lWH//5Vh//+V
|
||||
Yf//lWH//5Vh//+VYf//lWH//5Vh//+VYf//lWH//5Vh//+VYf//lWH//5Vh//9XTmz/SUpK/0lKSkAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASUpKMElKSv9mU47/lWH//5Vh//+V
|
||||
Yf//lWH//5Vh//+VYf//lWH//5Vh//+VYf//glvS/5Vh//+VYf//lWH//5Vh//+VYf//lWH//5Vh//+M
|
||||
Xuj/SUpK/0lKSt9JSkoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASUpKv0lKSv+M
|
||||
Xuj/lWH//5Vh//+VYf//lWH//5Vh//+VYf//lWH//5Vh//90V7D/glvS/5Vh//+VYf//lWH//5Vh//+V
|
||||
Yf//lWH//5Vh//9hUYP/SUpK/0lKSv9JSkqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJ
|
||||
SkpASUpK/0lKSv9XTmz/kGD0/5Vh//+VYf//lWH//5Vh//+VYf//lWH//3lYu/9hUYP/lWH//5Vh//+V
|
||||
Yf//lWH//5Vh//+VYf//lWH//2ZTjv9JSkr/SUpK/0lKSv9JSkr/SUpKIAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAABJSkrfSUpK/0lKSv9JSkr/TktV/29Wpf+MXuj/lWH//5Vh//99Wsb/YVGD/0lKSv9h
|
||||
UYP/jF7o/5Vh//+VYf//lWH//5Vh//+CW9L/XFB3/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpKvwAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAElKSmBJSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/1NNYf9cUHf/XFB3/05LVf9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSr9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSq8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASUpKQElKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkogAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASUpKn0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkqPAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJ
|
||||
SkoQSUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
SkrfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAABJSkpgSUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpKUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkqvSUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpKnwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKShBJSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK7wAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSmBJSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSjAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSp9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/2tsbP+Njo7/pKWl/8bGxv/S
|
||||
0tL/u7u7/42Ojv/Gxsb/pKWl/42Ojv9rbGz/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSo8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAElKSu9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/VFVV/1RVVf/Gxsb/9PT0////////
|
||||
////////////////////0tLS/6Slpf//////////////////////9PT0/7u7u/93d3f/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSt8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAASUpKMElKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/2BhYf+7u7v/9PT0/42Ojv//
|
||||
////////////////////////////////////0tLS/6Slpf//////////////////////////////////
|
||||
////mZmZ/3d3d/9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkogAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASUpKj0lKSv9JSkr/SUpK/0lKSv9JSkr/pKWl////////
|
||||
////0tLS/7u7u///////////////////////////////////////0tLS/6Slpf//////////////////
|
||||
////////////////////0tLS/7u7u//S0tL/a2xs/0lKSv9JSkr/SUpK/0lKSv9JSkpwAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASUpKz0lKSv9JSkr/SUpK/2tsbP/o
|
||||
6Oj/////////////////pKWl/+jo6P//////////////////////////////////////0tLS/6Slpf//
|
||||
/////////////////////////////////////////42Ojv///////////6Slpf9JSkr/SUpK/0lKSv9J
|
||||
SkrPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkogSUpK/0lKSv9J
|
||||
Skr/SUpK/9LS0v//////////////////////goOD///////S0tL/u7u7/6Slpf93d3f/d3d3/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/3d3d/+Njo7/r7Cw/93d3f///////////42Ojv////////////////+v
|
||||
sLD/SUpK/0lKSv9JSkr/SUpKIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJ
|
||||
SkpwSUpK/0lKSv9JSkr/a2xs/////////////////93d3f+ZmZn/YGFh/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skq/SUpKr0lKSoBJSkqASUpKgElKSoBJSkqASUpKgElKSp9JSkq/SUpK/0lKSv9gYWH/mZmZ/4KDg//o
|
||||
6Oj/////////////////VFVV/0lKSv9JSkr/SUpKcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAABJSkrPSUpK/0lKSv9JSkr/u7u7/8bGxv+Njo7/VFVV/0lKSv9JSkr/SUpKz0lKSo9J
|
||||
SkpgSUpKMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSkBJ
|
||||
SkqPSUpK30lKSv9gYWH/u7u7////////////mZmZ/0lKSv9JSkr/SUpK3wAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAElKSkBJSkr/SUpK/0lKSv9JSkr/VFVV/0lKSv9JSkr/SUpKr0lKSmBJ
|
||||
SkogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAElKSlBJSkq/SUpK/2BhYf/Gxsb/3d3d/0lKSv9JSkr/SUpK/0lKSjAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAElKSt9JSkpwAAAAAElKSp9JSkr/SUpK/0lKSv9JSkr/SUpKv0lKSmBJ
|
||||
SkoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASUpKQElKSr9JSkr/d3d3/1RVVf9J
|
||||
Skr/SUpK/0lKSp8AAAAAAAAAAAAAAAAAAAAAAAAAAElKSjBJSkrPSUpK30lKSv9JSkr/SUpK/0lKSp9J
|
||||
SkowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJ
|
||||
SkpQSUpK30lKSv9JSkr/SUpK/0lKSu9JSkoQAAAAAAAAAAAAAAAAAAAAAAAAAABJSkoQSUpKgElKSu9J
|
||||
Skr/SUpK30lKSp9JSkqASUpKnwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAASUpKEElKSq9JSkr/SUpK/0lKSv9JSkpgAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAElKShBJSkpASUpKgElKSnBJSkpASUpKEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkpgSUpK70lKSv9JSkrfAAAAAAAAAABJ
|
||||
SkpwSUpKrwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSiBJSkpASUpKcElKSu9J
|
||||
Skr/SUpKr0lKSu9JSkrfSUpKMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSiBJ
|
||||
SkqASUpKv0lKSr9JSkq/SUpKr0lKSmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////xVa////////FVr///////8VWv//////
|
||||
/xVa///wP///FVr//4AP//8VWv//AgP//xVa//w/4P//FVr/+Ph4f/8VWv/xgAw//xVa//MAAj//FVr/
|
||||
9gABn/8VWv/8AACf/xVa//gAAH//FVr/8AAAP/8VWv/gAAAf/xVa/+AAAB//FVr/wAAAD/8VWv/AAAAP
|
||||
/xVa/4AAAAf/FVr/gAAAB/8VWv8AAAAD/xVa/wAAAAP/FVr+AAAAA/8VWv4AAAAB/xVa/gAAAAH/FVr8
|
||||
AAAAAf8VWvwAAAAA/xVa/AAAAAD/FVr8AAAAAP8VWvgAAAAAfxVa+AAAAAB/FVr4AAAAAH8VWvAAAAAA
|
||||
PxVa8AAAAAA/FVrwAD/8AD8VWuAD//+AHxVaIB///+AfFVoA////+A8VWoB////8DxVa4H////8MFVr/
|
||||
/////gAVWv/////+AxVa////////FVr///////8VWv///////xVa////////FVr///////8VWigAAAAg
|
||||
AAAAQAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkpgSUpKr0lKSr9J
|
||||
Skr/SUpKv0lKSo9JSkowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkpASUpK30lKSp9J
|
||||
SkpgSUpKQElKSkBJSkpASUpKj0lKSt9JSkq/SUpKIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASUpKYElKSs9J
|
||||
SkogAAAAAAAAAABJSkowSUpKQElKSiAAAAAAAAAAAElKSoBJSkrvSUpKQAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSlBJ
|
||||
SkqfAAAAAElKSkBJSkqvSUpK/0lKSv9JSkr/SUpK/0lKSu9JSkqPSUpKEElKSjBJSkrfSUpKMAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAASUpKgAAAAABJSkqASUpK/1xQd/99Wsb/kGD0/5Vh//+MXuj/eVi7/1NNYf9JSkrvSUpKUElKSjBJ
|
||||
SkrPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAASUpKn05LVf99Wsb/lWH//5Vh//+VYf//lWH//5Vh//+VYf//lWH//3RXsP9J
|
||||
Skr/SUpKYElKSjBJSkogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAElKSnBOS1X/h13d/5Vh//+VYf//lWH//5Vh//+VYf//lWH//5Vh//+V
|
||||
Yf//lWH//3lYu/9JSkrvSUpKMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkogTktV/4dd3f+VYf//lWH//5Vh//+VYf//lWH//31axv+V
|
||||
Yf//lWH//5Vh//+VYf//lWH//1NNYf9JSkrPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSr9OS1X/fVrG/5Vh//+VYf//lWH//5Vh//95
|
||||
WLv/dFew/5Vh//+VYf//lWH//5Vh//9mU47/SUpK/0lKSv9JSkpwAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkpASUpK/0lKSv9JSkr/YVGD/3lYu/90
|
||||
V7D/XFB3/0lKSv9mU47/glvS/4Jb0v95WLv/XFB3/0lKSv9JSkr/SUpK/0lKSu9JSkoQAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSr9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSoAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkogSUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSo9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpKUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAASUpK30lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkqvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAElKSjBJSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAASUpKj0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv93
|
||||
d3f/VFVV/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSlAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkrPSUpK/0lKSv9JSkr/SUpK/0lKSv9UVVX/r7Cw/9LS0v//
|
||||
//////////////+kpaX//////+jo6P+7u7v/jY6O/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpKnwAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASUpKIElKSv9JSkr/SUpK/0lKSv+Njo7/6Ojo/5mZmf//
|
||||
/////////////////////////6Slpf//////////////////////xsbG/4KDg/9JSkr/SUpK/0lKSv9J
|
||||
SkrvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkpwSUpK/0lKSv9rbGz/3d3d///////S
|
||||
0tL/xsbG///////////////////////S0tL/pKWl////////////////////////////pKWl/+jo6P9r
|
||||
bGz/SUpK/0lKSv9JSkowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSq9JSkr/SUpK/9LS0v//
|
||||
/////////5mZmf+kpaX/jY6O/3d3d/9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv93d3f/mZmZ/93d3f+Z
|
||||
mZn///////////9rbGz/SUpK/0lKSp8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkogSUpK/0lKSv9r
|
||||
bGz/xsbG/42Ojv9UVVX/SUpK70lKSq9JSkqASUpKQElKSiAAAAAAAAAAAAAAAAAAAAAASUpKEElKSkBJ
|
||||
SkqPSUpK30lKSv+vsLD/9PT0/6+wsP9JSkr/SUpK7wAAAAAAAAAAAAAAAAAAAABJSkpgAAAAAElKSoBJ
|
||||
Skr/SUpK/0lKSv9JSkrfSUpKj0lKSkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAASUpKQElKSr9UVVX/pKWl/0lKSv9JSkr/SUpKUAAAAAAAAAAAAAAAAElKSmBJ
|
||||
SkrfSUpK30lKSv9JSkq/SUpKUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSkBJSkrPSUpK/0lKSv9JSkqvAAAAAAAAAAAA
|
||||
AAAAAAAAAElKSjBJSkqfSUpK30lKSs9JSkqASUpKEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkqASUpK/0lKSv9J
|
||||
SkogAAAAAElKSkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElKSjBJ
|
||||
SkqASUpK/0lKSr9JSkrfSUpKjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAASUpKIElKSnBJSkqASUpKcElKSiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////
|
||||
//////Af///AB///jGP//yAB//9AAf//gAD//wAB//4AAf/+AAD//AAAf/wAAH/4AAB/+AAAP/gAAD/w
|
||||
AAA/8AAAH/AAAB/gAAAf4AAAD+AAAA/AA8APQH/8BwP//weB///C////wP///8H///////////////8o
|
||||
AAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJ
|
||||
SkqzSUpK/0lKSv9JSkr/SUpKswAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJ
|
||||
SkqzAAAAAAAAAAAAAAAAAAAAAAAAAABJSkqzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAElKSv9JSkr/SUpK/0lKSv9JSkr/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAElKSv+AU6b/lWH//5Vh//+VYf//gFOm/0lKSv8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAElKSrOAU6b/lWH//5Vh//9/Uqb/lWH//5Vh//+AU6b/SUpKswAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAABJSkr/SUpK/4BTpv+AU6b/gFOm/4BTpv+AU6b/SUpK/0lKSv8AAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAABJSkqzSUpK/1lZWf9cXV3/WFlZ/1JTU/9cXV3/WFlZ/1tcXP9JSkr/SUpKswAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAASUpK/0lKSv9mZ2f/amtr/29wcP90dXX/aGlp/21ubv9vcHD/SktL/0lKSv8A
|
||||
AAAAAAAAAAAAAAAAAAAASUpKs0lKSv9JSkr/WFhY/1hYWP9KS0v/SktL/0pLS/9KS0v/SktL/1NUVP9J
|
||||
Skr/AAAAAAAAAAAAAAAAAAAAAElKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9JSkr/SUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSrMAAAAAAAAAAAAAAABJSkr/SUpK/0lKSv+jpKT///////////+jpKT///////////+j
|
||||
pKT/SUpK/0lKSv9JSkr/AAAAAAAAAABJSkqzSUpK/0lKSv//////o6Sk////////////o6Sk////////
|
||||
////o6Sk//////9JSkr/SUpK/wAAAABJSkqzSUpK/0lKSv//////o6Sk/6OkpP9JSkqzSUpK/0lKSv9J
|
||||
Skr/SUpK/0lKSrOjpKT//////0lKSv9JSkqzSUpK/0lKSrNJSkr/o6Sk/0lKSv9JSkqzAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAASUpKs0lKSv+jpKT/SUpK/0lKSrMAAAAASUpK/0lKSv9JSkqzAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkqzSUpK/0lKSv8AAAAAAAAAAAAAAABJSkqzSUpK/0lKSrMA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJSkqzSUpK/0lKSrMAAAAAAAAAAPg/rEH336xB+D+sQfAfrEHg
|
||||
D6xB4A+sQcAHrEHAB6xBgAesQYADrEGAA6xBAAKsQQAArEEH4KxBj/GsQcfjrEE=
|
||||
</value>
|
||||
</data>
|
||||
<metadata name="animationFileBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>359, 17</value>
|
||||
</metadata>
|
||||
<metadata name="eventColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="audioFileColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="dialogColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="statusColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="actionColumn.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="animationFileErrorProvider.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>563, 17</value>
|
||||
</metadata>
|
||||
</root>
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
public class MainModel : ModelBase {
|
||||
// For the time being, we're allowing only one file to be animated at a time.
|
||||
// Rhubarb tries to use all processor cores, so there isn't much to be gained by allowing
|
||||
// multiple parallel jobs. On the other hand, too many parallel Rhubarb instances may
|
||||
// seriously slow down the system.
|
||||
readonly SemaphoreSlim semaphore = new SemaphoreSlim(1);
|
||||
|
||||
private string _filePath;
|
||||
private AnimationFileModel _animationFileModel;
|
||||
|
||||
public MainModel(string filePath = null) {
|
||||
FilePath = filePath;
|
||||
}
|
||||
|
||||
public string FilePath {
|
||||
get { return _filePath; }
|
||||
set {
|
||||
_filePath = value;
|
||||
AnimationFileModel = null;
|
||||
if (string.IsNullOrEmpty(_filePath)) {
|
||||
SetError(nameof(FilePath), "No input file specified.");
|
||||
} else if (!File.Exists(_filePath)) {
|
||||
SetError(nameof(FilePath), "File does not exist.");
|
||||
} else {
|
||||
try {
|
||||
AnimationFileModel = new AnimationFileModel(_filePath, semaphore);
|
||||
SetError(nameof(FilePath), null);
|
||||
} catch (Exception e) {
|
||||
SetError(nameof(FilePath), e.Message);
|
||||
}
|
||||
}
|
||||
OnPropertyChanged(nameof(FilePath));
|
||||
}
|
||||
}
|
||||
|
||||
public AnimationFileModel AnimationFileModel {
|
||||
get { return _animationFileModel; }
|
||||
set {
|
||||
_animationFileModel = value;
|
||||
OnPropertyChanged(nameof(AnimationFileModel));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
public class ModelBase : INotifyPropertyChanged, IDataErrorInfo {
|
||||
private readonly Dictionary<string, string> errors = new Dictionary<string, string>();
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged(string propertyName) {
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
protected void SetError(string propertyName, string error) {
|
||||
errors[propertyName] = error;
|
||||
}
|
||||
|
||||
string IDataErrorInfo.this[string propertyName] =>
|
||||
errors.ContainsKey(propertyName) ? errors[propertyName] : null;
|
||||
|
||||
string IDataErrorInfo.Error => null;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
public class MouthCue {
|
||||
public MouthCue(TimeSpan time, MouthShape mouthShape) {
|
||||
Time = time;
|
||||
MouthShape = mouthShape;
|
||||
}
|
||||
|
||||
public TimeSpan Time { get; }
|
||||
public MouthShape MouthShape { get; }
|
||||
|
||||
public override string ToString() {
|
||||
return $"{Time}: {MouthShape}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
public class MouthNaming {
|
||||
public MouthNaming(string prefix, string suffix, MouthShapeCasing mouthShapeCasing) {
|
||||
Prefix = prefix;
|
||||
Suffix = suffix;
|
||||
MouthShapeCasing = mouthShapeCasing;
|
||||
}
|
||||
|
||||
public string Prefix { get; }
|
||||
public string Suffix { get; }
|
||||
public MouthShapeCasing MouthShapeCasing { get; }
|
||||
|
||||
public static MouthNaming Guess(IReadOnlyCollection<string> mouthNames) {
|
||||
string firstMouthName = mouthNames.First();
|
||||
if (mouthNames.Count == 1) {
|
||||
return firstMouthName == string.Empty
|
||||
? new MouthNaming(string.Empty, string.Empty, MouthShapeCasing.Lower)
|
||||
: new MouthNaming(
|
||||
firstMouthName.Substring(0, firstMouthName.Length - 1),
|
||||
string.Empty,
|
||||
GuessMouthShapeCasing(firstMouthName.Last()));
|
||||
}
|
||||
|
||||
string commonPrefix = mouthNames.GetCommonPrefix();
|
||||
string commonSuffix = mouthNames.GetCommonSuffix();
|
||||
var mouthShapeCasing = firstMouthName.Length > commonPrefix.Length
|
||||
? GuessMouthShapeCasing(firstMouthName[commonPrefix.Length])
|
||||
: MouthShapeCasing.Lower;
|
||||
return new MouthNaming(commonPrefix, commonSuffix, mouthShapeCasing);
|
||||
}
|
||||
|
||||
public string DisplayString {
|
||||
get {
|
||||
string casing = MouthShapeCasing == MouthShapeCasing.Upper
|
||||
? "<UPPER-CASE SHAPE NAME>"
|
||||
: "<lower-case shape name>";
|
||||
return $"\"{Prefix}{casing}{Suffix}\"";
|
||||
}
|
||||
}
|
||||
|
||||
public string GetName(MouthShape mouthShape) {
|
||||
string name = MouthShapeCasing == MouthShapeCasing.Upper
|
||||
? mouthShape.ToString()
|
||||
: mouthShape.ToString().ToLowerInvariant();
|
||||
return $"{Prefix}{name}{Suffix}";
|
||||
}
|
||||
|
||||
private static MouthShapeCasing GuessMouthShapeCasing(char mouthShape) {
|
||||
return char.IsUpper(mouthShape) ? MouthShapeCasing.Upper : MouthShapeCasing.Lower;
|
||||
}
|
||||
}
|
||||
|
||||
public enum MouthShapeCasing {
|
||||
Upper,
|
||||
Lower
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
public enum MouthShape {
|
||||
A, B, C, D, E, F, G, H, X
|
||||
}
|
||||
|
||||
public static class MouthShapes {
|
||||
public static IReadOnlyCollection<MouthShape> All =>
|
||||
Enum.GetValues(typeof(MouthShape))
|
||||
.Cast<MouthShape>()
|
||||
.ToList();
|
||||
|
||||
public const int BasicShapesCount = 6;
|
||||
|
||||
public static bool IsBasic(MouthShape mouthShape) =>
|
||||
(int) mouthShape < BasicShapesCount;
|
||||
|
||||
public static IReadOnlyCollection<MouthShape> Basic =>
|
||||
All.Take(BasicShapesCount).ToList();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Nito.AsyncEx;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
public static class ProcessTools {
|
||||
|
||||
public static async Task<int> RunProcessAsync(
|
||||
string processFilePath,
|
||||
string processArgs,
|
||||
Action<string> receiveStdout,
|
||||
Action<string> receiveStderr,
|
||||
CancellationToken cancellationToken
|
||||
) {
|
||||
var startInfo = new ProcessStartInfo {
|
||||
FileName = processFilePath,
|
||||
Arguments = processArgs,
|
||||
UseShellExecute = false, // Necessary to redirect streams
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true
|
||||
};
|
||||
using (var process = new Process { StartInfo = startInfo, EnableRaisingEvents = true }) {
|
||||
// Process all events in the original call's context
|
||||
var pendingActions = new AsyncProducerConsumerQueue<Action>();
|
||||
int? exitCode = null;
|
||||
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
process.Exited += (sender, args) =>
|
||||
pendingActions.Enqueue(() => exitCode = process.ExitCode);
|
||||
|
||||
process.OutputDataReceived += (sender, args) => {
|
||||
if (args.Data != null) {
|
||||
pendingActions.Enqueue(() => receiveStdout(args.Data));
|
||||
}
|
||||
};
|
||||
process.ErrorDataReceived += (sender, args) => {
|
||||
if (args.Data != null) {
|
||||
pendingActions.Enqueue(() => receiveStderr(args.Data));
|
||||
}
|
||||
};
|
||||
|
||||
process.Start();
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
|
||||
cancellationToken.Register(() => pendingActions.Enqueue(() => {
|
||||
try {
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
process.Kill();
|
||||
} catch (Exception e) {
|
||||
Debug.WriteLine($"Error terminating process: {e}");
|
||||
}
|
||||
// ReSharper disable once AccessToDisposedClosure
|
||||
process.WaitForExit();
|
||||
throw new OperationCanceledException();
|
||||
}));
|
||||
|
||||
while (exitCode == null) {
|
||||
Action action = await pendingActions.DequeueAsync(cancellationToken);
|
||||
action();
|
||||
}
|
||||
return exitCode.Value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
static class Program {
|
||||
[STAThread]
|
||||
static void Main(string[] args) {
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
string filePath = args.FirstOrDefault();
|
||||
MainModel mainModel = new MainModel(filePath);
|
||||
Application.Run(new MainForm(mainModel));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("rhubarb-for-spine")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("rhubarb-for-spine")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("c5ed6f8a-6141-4bae-be24-77dee23e495f")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// 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("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is automatically generated by Visual Studio .Net. It is
|
||||
used to store generic object data source configuration information.
|
||||
Renaming the file extension or editing the content of this file may
|
||||
cause the file to be unrecognizable by the program.
|
||||
-->
|
||||
<GenericObjectDataSource DisplayName="MainModel" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
|
||||
<TypeInfo>rhubarb_for_spine.MainModel, rhubarb-for-spine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
|
||||
</GenericObjectDataSource>
|
|
@ -0,0 +1,26 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <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 rhubarb_for_spine.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.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="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
public static class RhubarbCli {
|
||||
public static async Task<IReadOnlyCollection<MouthCue>> AnimateAsync(
|
||||
string audioFilePath, string dialog, ISet<MouthShape> extendedMouthShapes,
|
||||
IProgress<double> progress, CancellationToken cancellationToken
|
||||
) {
|
||||
if (cancellationToken.IsCancellationRequested) {
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
if (!File.Exists(audioFilePath)) {
|
||||
throw new ArgumentException($"File '{audioFilePath}' does not exist.");
|
||||
}
|
||||
|
||||
using (var dialogFile = dialog != null ? new TemporaryTextFile(dialog) : null) {
|
||||
string rhubarbExePath = GetRhubarbExePath();
|
||||
string args = CreateArgs(audioFilePath, extendedMouthShapes, dialogFile?.FilePath);
|
||||
|
||||
bool success = false;
|
||||
string errorMessage = null;
|
||||
string resultString = "";
|
||||
|
||||
await ProcessTools.RunProcessAsync(
|
||||
rhubarbExePath, args,
|
||||
outString => resultString += outString,
|
||||
errString => {
|
||||
dynamic json = JObject.Parse(errString);
|
||||
switch ((string) json.type) {
|
||||
case "progress":
|
||||
progress.Report((double) json.value);
|
||||
break;
|
||||
case "success":
|
||||
success = true;
|
||||
break;
|
||||
case "failure":
|
||||
errorMessage = json.reason;
|
||||
break;
|
||||
}
|
||||
},
|
||||
cancellationToken);
|
||||
|
||||
if (errorMessage != null) {
|
||||
throw new ApplicationException(errorMessage);
|
||||
}
|
||||
if (success) {
|
||||
progress.Report(1.0);
|
||||
return ParseRhubarbResult(resultString);
|
||||
}
|
||||
throw new ApplicationException("Rhubarb did not return a result.");
|
||||
}
|
||||
}
|
||||
|
||||
private static string CreateArgs(
|
||||
string audioFilePath, ISet<MouthShape> extendedMouthShapes, string dialogFilePath
|
||||
) {
|
||||
string extendedShapesString =
|
||||
string.Join("", extendedMouthShapes.Select(shape => shape.ToString()));
|
||||
string args = "--machineReadable"
|
||||
+ " --exportFormat json"
|
||||
+ $" --extendedShapes \"{extendedShapesString}\""
|
||||
+ $" \"{audioFilePath}\"";
|
||||
if (dialogFilePath != null) {
|
||||
args = $"--dialogFile \"{dialogFilePath}\" " + args;
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<MouthCue> ParseRhubarbResult(string jsonString) {
|
||||
dynamic json = JObject.Parse(jsonString);
|
||||
JArray mouthCues = json.mouthCues;
|
||||
return mouthCues
|
||||
.Cast<dynamic>()
|
||||
.Select(mouthCue => {
|
||||
TimeSpan time = TimeSpan.FromSeconds((double) mouthCue.start);
|
||||
MouthShape mouthShape = (MouthShape) Enum.Parse(typeof(MouthShape), (string) mouthCue.value);
|
||||
return new MouthCue(time, mouthShape);
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static string GetRhubarbExePath() {
|
||||
bool onUnix = Environment.OSVersion.Platform == PlatformID.Unix
|
||||
|| Environment.OSVersion.Platform == PlatformID.MacOSX;
|
||||
string exeName = "rhubarb" + (onUnix ? "" : ".exe");
|
||||
string guiExeDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
string currentDirectory = guiExeDirectory;
|
||||
while (currentDirectory != Path.GetPathRoot(guiExeDirectory)) {
|
||||
string candidate = Path.Combine(currentDirectory, exeName);
|
||||
if (File.Exists(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
currentDirectory = Path.GetDirectoryName(currentDirectory);
|
||||
}
|
||||
throw new ApplicationException(
|
||||
$"Could not find Rhubarb Lip Sync executable '{exeName}'."
|
||||
+ $" Expected to find it in '{guiExeDirectory}' or any directory above.");
|
||||
}
|
||||
|
||||
private class TemporaryTextFile : IDisposable {
|
||||
public string FilePath { get; }
|
||||
|
||||
public TemporaryTextFile(string text) {
|
||||
FilePath = Path.GetTempFileName();
|
||||
File.WriteAllText(FilePath, text);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
File.Delete(FilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
public static class SpineJson {
|
||||
public static JObject ReadJson(string filePath) {
|
||||
string jsonString = File.ReadAllText(filePath);
|
||||
try {
|
||||
return JObject.Parse(jsonString);
|
||||
} catch (Exception) {
|
||||
throw new ApplicationException("Wrong file format. This is not a valid JSON file.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void ValidateJson(JObject json, string animationFileDirectory) {
|
||||
// This method doesn't validate the entire JSON.
|
||||
// It merely checks that there are no obvious problems.
|
||||
|
||||
dynamic skeleton = ((dynamic) json).skeleton;
|
||||
if (skeleton == null) {
|
||||
throw new ApplicationException("JSON file is corrupted.");
|
||||
}
|
||||
if (skeleton.images == null) {
|
||||
throw new ApplicationException(
|
||||
"JSON file is incomplete. Make sure you checked 'Nonessential data' when exporting.");
|
||||
}
|
||||
if (((dynamic) json).skins["default"] == null) {
|
||||
throw new ApplicationException("JSON file has no default skin.");
|
||||
}
|
||||
GetImagesDirectory(json, animationFileDirectory);
|
||||
GetAudioDirectory(json, animationFileDirectory);
|
||||
}
|
||||
|
||||
public static string GetImagesDirectory(JObject json, string animationFileDirectory) {
|
||||
string result = Path.GetFullPath(Path.Combine(animationFileDirectory, (string) ((dynamic) json).skeleton.images));
|
||||
if (!Directory.Exists(result)) {
|
||||
throw new ApplicationException(
|
||||
"Could not find images directory relative to the JSON file."
|
||||
+ " Make sure the JSON file is in the same directory as the original Spine file.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string GetAudioDirectory(JObject json, string animationFileDirectory) {
|
||||
string result = Path.GetFullPath(Path.Combine(animationFileDirectory, (string) ((dynamic) json).skeleton.audio));
|
||||
if (!Directory.Exists(result)) {
|
||||
throw new ApplicationException(
|
||||
"Could not find audio directory relative to the JSON file."
|
||||
+ " Make sure the JSON file is in the same directory as the original Spine file.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static double GetFrameRate(JObject json) {
|
||||
return (double?) ((dynamic) json).skeleton.fps ?? 30.0;
|
||||
}
|
||||
|
||||
public static IReadOnlyCollection<string> GetSlots(JObject json) {
|
||||
return ((JArray) ((dynamic) json).slots)
|
||||
.Cast<dynamic>()
|
||||
.Select(slot => (string) slot.name)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static string GuessMouthSlot(IReadOnlyCollection<string> slots) {
|
||||
return slots.FirstOrDefault(slot => slot.Contains("mouth", StringComparison.InvariantCultureIgnoreCase))
|
||||
?? slots.FirstOrDefault();
|
||||
}
|
||||
|
||||
public class AudioEvent {
|
||||
public AudioEvent(string name, string relativeAudioFilePath, string dialog) {
|
||||
Name = name;
|
||||
RelativeAudioFilePath = relativeAudioFilePath;
|
||||
Dialog = dialog;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public string RelativeAudioFilePath { get; }
|
||||
public string Dialog { get; }
|
||||
}
|
||||
|
||||
public static IReadOnlyCollection<AudioEvent> GetAudioEvents(JObject json) {
|
||||
return ((IEnumerable<KeyValuePair<string, JToken>>) ((dynamic) json).events)
|
||||
.Select(pair => {
|
||||
string name = pair.Key;
|
||||
dynamic value = pair.Value;
|
||||
string relativeAudioFilePath = value.audio;
|
||||
string dialog = value["string"];
|
||||
return new AudioEvent(name, relativeAudioFilePath, dialog);
|
||||
})
|
||||
.Where(audioEvent => audioEvent.RelativeAudioFilePath != null)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static IReadOnlyCollection<string> GetSlotAttachmentNames(JObject json, string slotName) {
|
||||
return ((JObject) ((dynamic) json).skins["default"][slotName])
|
||||
.Properties()
|
||||
.Select(property => property.Name)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static bool HasAnimation(JObject json, string animationName) {
|
||||
JObject animations = ((dynamic) json).animations;
|
||||
if (animations == null) return false;
|
||||
|
||||
return animations.Properties().Any(property => property.Name == animationName);
|
||||
}
|
||||
|
||||
public static void CreateOrUpdateAnimation(
|
||||
JObject json, IReadOnlyCollection<MouthCue> animationResult,
|
||||
string eventName, string animationName, string mouthSlot, MouthNaming mouthNaming
|
||||
) {
|
||||
dynamic dynamicJson = json;
|
||||
dynamic animations = dynamicJson.animations;
|
||||
if (animations == null) {
|
||||
animations = dynamicJson.animations = new JObject();
|
||||
}
|
||||
|
||||
// Round times to full frame. Always round down.
|
||||
// If events coincide, prefer the later one.
|
||||
double frameRate = GetFrameRate(json);
|
||||
var keyframes = new Dictionary<int, MouthShape>();
|
||||
foreach (MouthCue mouthCue in animationResult) {
|
||||
int frameNumber = (int) (mouthCue.Time.TotalSeconds * frameRate);
|
||||
keyframes[frameNumber] = mouthCue.MouthShape;
|
||||
}
|
||||
|
||||
animations[animationName] = new JObject {
|
||||
["slots"] = new JObject {
|
||||
[mouthSlot] = new JObject {
|
||||
["attachment"] = new JArray(
|
||||
keyframes
|
||||
.OrderBy(pair => pair.Key)
|
||||
.Select(pair => new JObject {
|
||||
["time"] = pair.Key / frameRate,
|
||||
["name"] = mouthNaming.GetName(pair.Value)
|
||||
})
|
||||
.Cast<object>()
|
||||
.ToArray()
|
||||
)
|
||||
}
|
||||
},
|
||||
["events"] = new JArray {
|
||||
new JObject {
|
||||
["time"] = 0.0,
|
||||
["name"] = eventName,
|
||||
["string"] = ""
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
|
||||
namespace rhubarb_for_spine {
|
||||
public static class Tools {
|
||||
public static string GetCommonPrefix(this IReadOnlyCollection<string> strings) {
|
||||
return strings.Any()
|
||||
? strings.First().Substring(0, GetCommonPrefixLength(strings))
|
||||
: string.Empty;
|
||||
}
|
||||
|
||||
public static int GetCommonPrefixLength(this IReadOnlyCollection<string> strings) {
|
||||
if (!strings.Any()) return 0;
|
||||
|
||||
string first = strings.First();
|
||||
int result = first.Length;
|
||||
foreach (string s in strings) {
|
||||
for (int i = 0; i < Math.Min(result, s.Length); i++) {
|
||||
if (s[i] != first[i]) {
|
||||
result = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string GetCommonSuffix(this IReadOnlyCollection<string> strings) {
|
||||
if (!strings.Any()) return string.Empty;
|
||||
|
||||
int commonSuffixLength = GetCommonSuffixLength(strings);
|
||||
string first = strings.First();
|
||||
return first.Substring(first.Length - commonSuffixLength);
|
||||
}
|
||||
|
||||
public static int GetCommonSuffixLength(this IReadOnlyCollection<string> strings) {
|
||||
if (!strings.Any()) return 0;
|
||||
|
||||
string first = strings.First();
|
||||
int result = first.Length;
|
||||
foreach (string s in strings) {
|
||||
for (int i = 0; i < Math.Min(result, s.Length); i++) {
|
||||
if (s[s.Length - 1 - i] != first[first.Length - 1 - i]) {
|
||||
result = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool Contains(this string s, string value, StringComparison stringComparison) {
|
||||
return s.IndexOf(value, stringComparison) >= 0;
|
||||
}
|
||||
|
||||
public static string Join(this IEnumerable<string> values, string separator) {
|
||||
return string.Join(separator, values);
|
||||
}
|
||||
|
||||
public static Color WithOpacity(this Color color, double opacity) {
|
||||
return Color.FromArgb((int) (opacity * 255), color);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net46" />
|
||||
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net46" />
|
||||
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net46" />
|
||||
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net45" />
|
||||
<package id="Nito.AsyncEx" version="4.0.1" targetFramework="net46" />
|
||||
</packages>
|
|
@ -0,0 +1,138 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" 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>{C5ED6F8A-6141-4BAE-BE24-77DEE23E495F}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>rhubarb_for_spine</RootNamespace>
|
||||
<AssemblyName>rhubarb-for-spine</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</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>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</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>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>rhubarb.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Nito.AsyncEx, Version=4.0.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Nito.AsyncEx.4.0.1\lib\net45\Nito.AsyncEx.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Nito.AsyncEx.Concurrent, Version=4.0.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Nito.AsyncEx.4.0.1\lib\net45\Nito.AsyncEx.Concurrent.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Nito.AsyncEx.Enlightenment, Version=4.0.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Nito.AsyncEx.4.0.1\lib\net45\Nito.AsyncEx.Enlightenment.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AnimationFileModel.cs" />
|
||||
<Compile Include="AudioFileModel.cs" />
|
||||
<Compile Include="BindableComboBox.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainForm.Designer.cs">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="MainModel.cs" />
|
||||
<Compile Include="ModelBase.cs" />
|
||||
<Compile Include="MouthCue.cs" />
|
||||
<Compile Include="MouthNaming.cs" />
|
||||
<Compile Include="MouthShape.cs" />
|
||||
<Compile Include="ProcessTools.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="RhubarbCli.cs" />
|
||||
<Compile Include="SpineJson.cs" />
|
||||
<Compile Include="Tools.cs" />
|
||||
<EmbeddedResource Include="MainForm.resx">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\DataSources\MainModel.datasource" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="rhubarb.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
|
||||
</Target>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
Loading…
Reference in New Issue