diff --git a/GleeBug/GleeBug.vcxproj b/GleeBug/GleeBug.vcxproj
index 3e3d3e3..c46c4e6 100644
--- a/GleeBug/GleeBug.vcxproj
+++ b/GleeBug/GleeBug.vcxproj
@@ -165,6 +165,7 @@
+
@@ -181,6 +182,7 @@
+
diff --git a/GleeBug/GleeBug.vcxproj.filters b/GleeBug/GleeBug.vcxproj.filters
index 1557404..bafd179 100644
--- a/GleeBug/GleeBug.vcxproj.filters
+++ b/GleeBug/GleeBug.vcxproj.filters
@@ -71,6 +71,9 @@
Source Files
+
+ Source Files
+
@@ -121,5 +124,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/GleeBug/Static.Pattern.cpp b/GleeBug/Static.Pattern.cpp
new file mode 100644
index 0000000..d16d9ed
--- /dev/null
+++ b/GleeBug/Static.Pattern.cpp
@@ -0,0 +1,171 @@
+#include "Static.Pattern.h"
+
+using namespace std;
+
+namespace GleeBug
+{
+ string Pattern::FormatPattern(const string & patterntext)
+ {
+ string result;
+ result.reserve(patterntext.length());
+ for (auto ch : patterntext)
+ {
+ if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f' || ch == '?')
+ result += ch;
+ }
+ return result;
+ }
+
+ bool Pattern::Transform(const std::string & patterntext, std::vector & pattern)
+ {
+ pattern.clear();
+ auto formattext = FormatPattern(patterntext);
+ auto len = formattext.length();
+ if (!len)
+ return false;
+
+ if (len % 2) //not a multiple of 2
+ {
+ formattext += '?';
+ len++;
+ }
+
+ auto hexChToInt = [](char ch)
+ {
+ if (ch >= '0' && ch <= '9')
+ return ch - '0';
+ if (ch >= 'A' && ch <= 'F')
+ return ch - 'A' + 10;
+ if (ch >= 'a' && ch <= 'f')
+ return ch - 'a' + 10;
+ return -1;
+ };
+
+ Byte newByte;
+ auto j = 0;
+ for (auto ch : formattext)
+ {
+ if (ch == '?') //wildcard
+ {
+ newByte.nibble[j].wildcard = true; //match anything
+ }
+ else //hex
+ {
+ newByte.nibble[j].wildcard = false;
+ newByte.nibble[j].data = hexChToInt(ch) & 0xF;
+ }
+
+ j++;
+ if (j == 2) //two nibbles = one byte
+ {
+ j = 0;
+ pattern.push_back(newByte);
+ }
+ }
+ return true;
+ }
+
+ size_t Pattern::Find(const uint8* data, size_t datasize, const std::vector & pattern)
+ {
+ auto MatchByte = [](uint8 byte, const Byte & pbyte)
+ {
+ auto matched = 0;
+
+ unsigned char n1 = (byte >> 4) & 0xF;
+ if (pbyte.nibble[0].wildcard)
+ matched++;
+ else if (pbyte.nibble[0].data == n1)
+ matched++;
+
+ unsigned char n2 = byte & 0xF;
+ if (pbyte.nibble[1].wildcard)
+ matched++;
+ else if (pbyte.nibble[1].data == n2)
+ matched++;
+
+ return matched == 2;
+ };
+
+ auto searchpatternsize = pattern.size();
+ for (size_t i = 0, pos = 0; i < datasize; i++) //search for the pattern
+ {
+ if (MatchByte(data[i], pattern.at(pos))) //check if our pattern matches the current byte
+ {
+ pos++;
+ if (pos == searchpatternsize) //everything matched
+ return i - searchpatternsize + 1;
+ }
+ else if (pos > 0) //fix by Computer_Angel
+ {
+ i -= pos;
+ pos = 0; //reset current pattern position
+ }
+ }
+ return -1;
+ }
+
+ size_t Pattern::Find(const uint8* data, size_t datasize, unsigned char* pattern, size_t patternsize)
+ {
+ if (patternsize > datasize)
+ patternsize = datasize;
+ for (size_t i = 0, pos = 0; i < datasize; i++)
+ {
+ if (data[i] == pattern[pos])
+ {
+ pos++;
+ if (pos == patternsize)
+ return i - patternsize + 1;
+ }
+ else if (pos > 0)
+ {
+ i -= pos;
+ pos = 0; //reset current pattern position
+ }
+ }
+ return -1;
+ }
+
+ size_t Pattern::Find(const uint8* data, size_t datasize, const char* pattern)
+ {
+ string patterntext(pattern);
+ vector searchpattern;
+ if (!Transform(patterntext, searchpattern))
+ return -1;
+ return Find(data, datasize, searchpattern);
+ }
+
+ void Pattern::Write(uint8* data, size_t datasize, const char* pattern)
+ {
+ vector writepattern;
+ string patterntext(pattern);
+ if (!Transform(patterntext, writepattern))
+ return;
+
+ auto writepatternsize = writepattern.size();
+ if (writepatternsize > datasize)
+ writepatternsize = datasize;
+
+ auto WriteByte = [](uint8* byte, const Byte & pbyte)
+ {
+ unsigned char n1 = (*byte >> 4) & 0xF;
+ unsigned char n2 = *byte & 0xF;
+ if (!pbyte.nibble[0].wildcard)
+ n1 = pbyte.nibble[0].data;
+ if (!pbyte.nibble[1].wildcard)
+ n2 = pbyte.nibble[1].data;
+ *byte = ((n1 << 4) & 0xF0) | (n2 & 0xF);
+ };
+
+ for (size_t i = 0; i < writepatternsize; i++)
+ WriteByte(&data[i], writepattern.at(i));
+ }
+
+ bool Pattern::Snr(uint8* data, size_t datasize, const char* searchpattern, const char* replacepattern)
+ {
+ auto found = Find(data, datasize, searchpattern);
+ if (found == -1)
+ return false;
+ Write(data + found, datasize - found, replacepattern);
+ return true;
+ }
+};
\ No newline at end of file
diff --git a/GleeBug/Static.Pattern.h b/GleeBug/Static.Pattern.h
new file mode 100644
index 0000000..3ed0482
--- /dev/null
+++ b/GleeBug/Static.Pattern.h
@@ -0,0 +1,30 @@
+#ifndef STATIC_PATTERN_H
+#define STATIC_PATTERN_H
+
+#include "Static.Global.h"
+
+namespace GleeBug
+{
+ class Pattern
+ {
+ public:
+ struct Byte
+ {
+ struct Nibble
+ {
+ uint8 data;
+ bool wildcard;
+ } nibble[2];
+ };
+
+ static std::string FormatPattern(const std::string & pattern);
+ static bool Transform(const std::string & patterntext, std::vector & pattern);
+ static size_t Find(const uint8* data, size_t datasize, const std::vector & pattern);
+ static size_t Find(const uint8* data, size_t datasize, unsigned char* pattern, size_t patternsize);
+ static size_t Find(const uint8* data, size_t datasize, const char* pattern);
+ static void Write(uint8* data, size_t datasize, const char* pattern);
+ static bool Snr(uint8* data, size_t datasize, const char* searchpattern, const char* replacepattern);
+ };
+};
+
+#endif //STATIC_PATTERN_H
\ No newline at end of file