Skip to content

Commit

Permalink
New Good Feature
Browse files Browse the repository at this point in the history
Now the SRL duplicate lines with different translation in the same database!
See the Readme for more info.
  • Loading branch information
marcussacana committed Aug 16, 2019
1 parent 054c0db commit a6a9c2d
Show file tree
Hide file tree
Showing 12 changed files with 407 additions and 50 deletions.
23 changes: 22 additions & 1 deletion Help/Readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,30 @@ At StringsReloader
-"AsianInput" Hint the Dialogue Detection algorithm saying if the game is a japanese game or not.
-"AutoUnks" Automatically create the unknow char reload list based on your reloads.
-"CaseSensitive" The SRL database match can match with case senstive or not, just change this
-"NotCachedOnly" Use the pointer cache to don't allow the SRL process again the same string
-"NotCachedOnly" Use the pointer cache to don't allow the SRL process again the same string, usefull when the SRL injection point is inside a for each char loop of the string
-"SetOutEncoding" If the Debug Low Window (aka console) display invalid chars, set true
-"AllowEmptyReloads" Keep in the database Reloads that don't change nothing
-"AllowDuplicates" Allow a single database contains more than one reload for the same match line

About the AllowDuplicates
Well, After basically 2 years devlopling the SRL, I added support to the SRL can match
different text for a same line, This feature is disabled by default because the old method
to match the string is 15x faster than the new method, in others words enabling this feature
make the SRL match the game text more slow, and of couse, will increase the cpu usage too.
Wow shit! 15x?! Yes, but you don't need worry much since the SRL is very, but very fast
to match a string, in a small database in the old method he can match like 500000 times
the reloads using only 0.01 ms, and when using the new method he will use 0.18 ms.
Then it's really more slow but isn't something that you will notice when use...
Oh Good! But if we have more gains than lost with this new method, why is disable by default?
Well, Well... Basically the SRL already can match a duplicate line in the old method,
You just need split the database in parts and the SRL will give priority to the last database
that he found a translation, and you can need use the ::SETDB-??:: sometimes too, like when
the duplicate is just after the last, But Enabling this new feature you don't need
split the database in many .lst's to match a duplicate, and don't need use the ::SETDB-??::,
The SRL will give priority to the closest line of the lastest matched line in the same database,
Keep in mind, when the SRL change to other database he don't reset the 'last match' position
of the related database, then if the game return to a .lst that has already matched something
can give unexpected results, then keep sure to test everything before you relase your patch.

At WordWrap
-"Enable" Disable or Enable the SRL Auto Wordwrap engine
Expand Down
4 changes: 2 additions & 2 deletions SRL/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,13 +296,13 @@ static void LoadData() {
Log("Chars Reloads Initialized, Total entries: {0} + {1}", true, UnkRld.Count, CharRld.Count);
Log("Processing String Reloads...", true);
List<string> Temp = new List<string>();
StrRld = new Dictionary<string, string>();
StrRld = CreateDictionary();
long ReloadEntries = 0, MaskEntries = 0;
foreach (SRLDatabase2 Database in Data.Databases) {
for (uint i = 0; i < Database.Original.LongLength; i++) {
Application.DoEvents();
string str = SimplfyMatch(Database.Original[i]);
if (!ContainsKey(str, true)) {
if (AllowDuplicates || !ContainsKey(str, true)) {
if (IsMask(Database.Original[i])) {
if (LiteralMaskMatch) {
AddEntry(str, ReplaceChars(Database.Replace[i]));
Expand Down
286 changes: 286 additions & 0 deletions SRL/DuplicableDictionary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace SRL {
public class DuplicableDictionary<TKey, TValue> : IDictionary<TKey, TValue> {
public int LastKeyIndex { get; private set; } = 0;

Collection<TKey> KeyCollection = new Collection<TKey>();
Collection<TValue> ValueCollection = new Collection<TValue>();
Dictionary<TKey, KeyValuePairs<TKey, TValue>> Base = new Dictionary<TKey, KeyValuePairs<TKey, TValue>>();

public bool NearToNext = true;

public TValue this[TKey key] {
get {
if (TryGetValue(key, out TValue value))
return value;
throw new KeyNotFoundException();
}
set => throw new NotImplementedException("You Must use the Item Index");
}

public TValue this[int index] {
get {
return ValueCollection[index];
}
set {
ValueCollection[index] = value;
var Entry = Base[KeyCollection[index]];
Entry.Update(index, value);
Base[KeyCollection[index]] = Entry;
}
}

public ICollection<TKey> Keys => KeyCollection;

public ICollection<TValue> Values => ValueCollection;

public int Count => ValueCollection.Count;

public bool IsReadOnly => false;

public void Add(TKey key, TValue value) {
Add(new KeyValuePair<TKey, TValue>(key, value));
}

public void Add(KeyValuePair<TKey, TValue> item) {
int ID = Count;
KeyCollection.Add(item.Key);
ValueCollection.Add(item.Value);

if (Base.ContainsKey(item.Key)) {
var Entry = Base[item.Key];
Entry.Insert(item.Value, ID);
Base[item.Key] = Entry;
} else
Base[item.Key] = new KeyValuePairs<TKey, TValue>(item.Key, item.Value, ID);
}

public void Clear() {
Base.Clear();
KeyCollection.Clear();
ValueCollection.Clear();
LastKeyIndex = 0;
}

public bool Contains(KeyValuePair<TKey, TValue> item) {
if (!Base.ContainsKey(item.Key))
return false;
foreach (var Pair in Base[item.Key].GetKeyPairs()) {
if (Pair.Key.Equals(item.Key) && Pair.Value.Equals(item.Value))
return true;
}

return false;
}

public bool ContainsKey(TKey key) {
return Base.ContainsKey(key);
}

public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
for (int i = 0; i < Count; i++)
array[i + arrayIndex] = new KeyValuePair<TKey, TValue>(KeyCollection[i], ValueCollection[i]);
}

public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
for (int i = 0; i < Count; i++)
yield return new KeyValuePair<TKey, TValue>(KeyCollection[i], ValueCollection[i]);
}

/// <summary>
/// This don't remove the entry, just disable the match
/// </summary>
public bool Remove(TKey key) {
if (!ContainsKey(key))
return false;

return Base.Remove(key);
}

/// <summary>
/// This don't remove the entry, just disable the match
/// </summary>
public void RemoveAt(int Index) {
var Key = KeyCollection[Index];
Base[Key].Remove(Index);
if (Base[Key].Count == 0)
Base.Remove(Key);
}

/// <summary>
/// This don't remove the entry, just disable the match
/// </summary>
public bool Remove(KeyValuePair<TKey, TValue> item) {
if (!Base.ContainsKey(item.Key))
return false;

int Removed = 0;

var Entry = Base[item.Key];

foreach (var Pair in Entry.GetIdPairs()) {
if (Pair.Value.Equals(item.Value)) {
Entry.Remove(Pair.Key);
Removed++;
}
}

if (Entry.Count == 0)
Base.Remove(item.Key);

return Removed > 0;
}

public bool TryGetValue(TKey key, out TValue value) {
if (!Base.ContainsKey(key)) {
value = default;
return false;
}

if (KeyCollection[LastKeyIndex].Equals(key)) {
value = ValueCollection[LastKeyIndex];
if (NearToNext && LastKeyIndex + 1 < Keys.Count)
LastKeyIndex++;
return true;
}

var Entry = Base[key];

var Closest = ClosestTo(Entry.Identifiers, LastKeyIndex);
if (Closest == int.MaxValue)
Closest = Entry.Identifiers.First();

LastKeyIndex = Closest;
if (NearToNext && LastKeyIndex + 1 < Keys.Count)
LastKeyIndex++;

foreach (var Pair in Entry.GetIdPairs()) {
if (Pair.Key == Closest) {
value = Pair.Value;
return true;
}
}
value = default;
return false;
}

public static int ClosestTo(IEnumerable<int> collection, int target) {
// NB Method will return int.MaxValue for a sequence containing no elements.
// Apply any defensive coding here as necessary.
var closest = int.MaxValue;
var minDifference = int.MaxValue;
foreach (var element in collection) {
var difference = Math.Abs((long)element - target);
if (minDifference > difference) {
minDifference = (int)difference;
closest = element;
}
}

return closest;
}

IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}

internal class Collection<Type> : ICollection<Type> {
List<Type> List = new List<Type>();
public int Count => List.Count;

public bool IsReadOnly => false;
public Type this[int index] { get => List[index]; set => List[index] = value; }

public void Add(Type item) {
List.Add(item);
}

public void Clear() {
List.Clear();
}

public bool Contains(Type item) {
return List.Contains(item);
}

public void CopyTo(Type[] array, int arrayIndex) {
List.CopyTo(array, arrayIndex);
}

public IEnumerator<Type> GetEnumerator() {
return List.GetEnumerator();
}

public bool Remove(Type item) {
return List.Remove(item);
}

public void RemoveAt(int index) {
List.RemoveAt(index);
}

IEnumerator IEnumerable.GetEnumerator() {
return List.GetEnumerator();
}
}

internal struct KeyValuePairs<TKey, TValue> {
public TKey Key { get; private set; }
public TValue[] Values { get; private set; }

public int[] Identifiers { get; private set; }

public int Count => Values.Length;

public KeyValuePairs(TKey Key, TValue Value, int Identifier) {
this.Key = Key;
Values = new TValue[] { Value };
Identifiers = new int[] { Identifier };
}

public void Insert(TValue Value, int Identifier) => Insert(new TValue[] { Value }, new int[] { Identifier });
public void Insert(TValue[] Values, int[] Identifiers) {
this.Values = this.Values.Concat(Values).ToArray();
this.Identifiers = this.Identifiers.Concat(Identifiers).ToArray();
}

public void Update(int Identifier, TValue Value) {
for (int i = 0; i < Values.Length; i++) {
if (Identifiers[i] == Identifier) {
Values[i] = Value;
break;
}
}
}

public void Remove(int Identifier) {
for (int i = 0; i < Values.Length; i++) {
if (Identifiers[i] == Identifier) {
var tmpVal = Values.ToList();
var tmpIds = Identifiers.ToList();

tmpVal.RemoveAt(i);
tmpIds.RemoveAt(i);

Values = tmpVal.ToArray();
Identifiers = tmpIds.ToArray();
break;
}
}
}

public IEnumerable<KeyValuePair<TKey, TValue>> GetKeyPairs() {
foreach (var Value in Values)
yield return new KeyValuePair<TKey, TValue>(Key, Value);
}
public IEnumerable<KeyValuePair<int, TValue>> GetIdPairs() {
for (int i = 0; i < Values.Length; i++)
yield return new KeyValuePair<int, TValue>(Identifiers[i], Values[i]);
}
}
}
8 changes: 7 additions & 1 deletion SRL/FileWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ static void ReadDump(string Path, ref List<string> In, ref List<string> Out, boo

if (L2 != L1 || (IgnoreMask && IsMask(L1))) {
if (!string.IsNullOrWhiteSpace(L2) || AllowEmpty) {
if (!In.Contains(L1)) {
if (!In.Contains(L1) || AllowDuplicates) {
In.Add(L1);
Out.Add(L2);
}
Expand Down Expand Up @@ -304,6 +304,12 @@ private static void LoadConfig() {
Log("Empty Reloader Filter Disabled", true);
}

if (!Initialized && Settings.AllowDuplicates) {
AllowDuplicates = true;
Log("Duplicate Reload Support Enabled", true);
} else if (Settings.AllowDuplicates != AllowDuplicates)
Warning("Duplicate Reload Support Changed - Restart Required");

if (Settings.SetOutputEncoding) {
Log("Console Output Encoding Changed", false);
if (!Debugging) {
Expand Down
5 changes: 4 additions & 1 deletion SRL/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ internal static IntPtr ProcessReal(IntPtr Target) {
return Target;

if (LogAll || LogOutput) {
Log("Output: {0}", true, Reloaded);
if (AllowDuplicates)
Log("Output: {0}\r\nDB Current Index: {1}", true, Reloaded);
else
Log("Output: {0}", true, Reloaded);
}

}
Expand Down
Loading

0 comments on commit a6a9c2d

Please sign in to comment.