C# - Reading Sequence of Hex Bytes in Binary - c#

So I've been googling & googling for this, but I can't find a solution for my case. I could find things about byte arrays. but I hope there's also a simpler solution for my case.
Maybe it's just me using the wrong search terms, don't know.
Anyways, I already have a kinda working code which is:
static void Main(string[] args)
{
// Open the file to search in
BinaryReader br = new BinaryReader(File.OpenRead("D:/Users/Joey/Desktop/prod"));
for (int i = 0; i <= br.BaseStream.Length; i++)
{
// Search the file for the given byte
if (br.BaseStream.ReadByte() == (byte)0xC0)
{
Console.WriteLine("Found the byte at offset " + i); //write to the console on which offset it has been found
}
}
}
This example works.
However, I need it to be able to search for more than just one byte.
For example: C0035FD6
I feel like I'm missing something so simple, but I just can't seem to figure it out.
If anyone has gotten a solution for me, that would be great :D

You can use this extension to search for AOB:
public static class StreamExtensions
{
public static IEnumerable<long> ScanAOB(this Stream stream, params byte[] aob)
{
long position;
byte[] buffer = new byte[aob.Length - 1];
while ((position = stream.Position) < stream.Length)
{
if (stream.ReadByte() != aob[0]) continue;
if (stream.Read(buffer, 0, aob.Length - 1) == 0) continue;
if (buffer.SequenceEqual(aob.Skip(1)))
{
yield return position;
}
}
}
public static IEnumerable<long> ScanAOB(this Stream stream, params byte?[] aob)
{
long position;
byte[] buffer = new byte[aob.Length - 1];
while ((position = stream.Position) < stream.Length)
{
if (stream.ReadByte() != aob[0]) continue;
if (stream.Read(buffer, 0, aob.Length - 1) == 0) continue;
if (buffer.Cast<byte?>().SequenceEqual(aob.Skip(1), new AobComparer()))
{
yield return position;
}
}
}
private class AobComparer : IEqualityComparer<byte?>
{
public bool Equals(byte? x, byte? y) => x == null || y == null || x == y;
public int GetHashCode(byte? obj) => obj?.GetHashCode() ?? 0;
}
}
Example:
void Main()
{
using (var stream = new MemoryStream(FakeData().ToArray()))
{
stream.ScanAOB(0x1, 0x2).Dump("Addresses of: 01 02");
stream.Position = 0;
stream.ScanAOB(0x03, 0x12).Dump("Addresses of: 03 12");
stream.Position = 0;
stream.ScanAOB(0x04, null, 0x06).Dump("Addresses of: 04 ?? 06");
}
}
// Define other methods and classes here
IEnumerable<byte> FakeData()
{
return Enumerable.Range(0, 2)
.SelectMany(_ => Enumerable.Range(0, 255))
.Select(x => (byte)x);
}

Give this a shot. You will need to verify the arrays are correct. In a binary stream, a byte array is just a collection of bytes starting at offset with count bytes as its size.
//here is where you initialize your array. you may need to tweak the values to match your byte range (array)
byte[] dataArray = new byte[9] { 0x93, 0x0E, 0x40, 0xF9, 0x53, 0x00, 0x00, 0xB5, 0xDE };
//here is where you initialize the NEW array you want to write where your matching array lives
byte[] newArray = new byte[9] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
// Open the file to search in
BinaryReader br = new BinaryReader(File.OpenRead("D:/Users/Joey/Desktop/prod"));
for (int i = 0; i <= br.BaseStream.Length; i++)
{
// Search the file for the STARTING byte of my match
if (br.BaseStream.ReadByte() == (byte)0x93)
{
Console.WriteLine("Found the starting byte at offset " + i); //write to the console on which offset it has been found
byte[] tempArray = new byte[9];
tempArray = br.ReadBytes(9);
//now compare the arrays to see if you have a full match:
int matched = 0;
for (int j=0; j<tempArray.Length; j++)
{
if(tempArray[j] == dataArray[j])
{
matched++;
}
}
//if the arrays match, write your new values:
if(matched == tempArray.Length-1)
{
br.BaseStream.Write(newArray, i, 9);
break; //exit the loop when finished
}
}
}

Related

Decreasing volume of .wav file creates heavy distortion

I have a problem that just baffles me. I import a .wav file and read them as bytes. Then I turn them into integers that I then all divide by 2 (or some other number) in order to decrease the volume. Then I make a new .wav file into which I put the new data. The result is loud and heavy distortion over the original track.
Scroll to the Main() method for the relevant (C#-)code:
using System;
using System.IO;
namespace ConsoleApp2 {
class basic {
public static byte[] bit32(int num) { //turns int into byte array of length 4
byte[] numbyt = new byte[4] { 0x00, 0x00, 0x00, 0x00 };
int pow;
for (int k = 3; k >= 0; k--) {
pow = (int)Math.Pow(16, 2*k + 1);
numbyt[k] += (byte)(16*(num/pow));
num -= numbyt[k]*(pow/16);
numbyt[k] += (byte)(num/(pow/16));
num -= (num/(pow/16))*pow/16;
}
return numbyt;
}
public static byte[] bit16(int num) { //turns int into byte array of length 2
if (num < 0) {
num += 65535;
}
byte[] numbyt = new byte[2] { 0x00, 0x00 };
int pow;
for (int k = 1; k >= 0; k--) {
pow = (int)Math.Pow(16, 2*k + 1);
numbyt[k] += (byte)(16*(num/pow));
num -= numbyt[k]*(pow/16);
numbyt[k] += (byte)(num/(pow/16));
num -= (num/(pow/16))*pow/16;
}
return numbyt;
}
public static int bitint16(byte[] numbyt) { //turns byte array of length 2 into int
int num = 0;
num += (int)Math.Pow(16, 2)*numbyt[1];
num += numbyt[0];
return num;
}
}
class wavfile: FileStream {
public wavfile(string name, int len) : base(name, FileMode.Create) {
int samplerate = 44100;
byte[] riff = new byte[] { 0x52, 0x49, 0x46, 0x46 };
this.Write(riff, 0, 4);
byte[] chunksize;
chunksize = basic.bit32(36 + len*4);
this.Write(chunksize, 0, 4);
byte[] wavebyte = new byte[4] { 0x57, 0x41, 0x56, 0x45 };
this.Write(wavebyte, 0, 4);
byte[] fmt = new byte[] { 0x66, 0x6d, 0x74, 0x20 };
this.Write(fmt, 0, 4);
byte[] subchunk1size = new byte[] { 0x10, 0x00, 0x00, 0x00 };
this.Write(subchunk1size, 0, 4);
byte[] formchann = new byte[] { 0x01, 0x00, 0x02, 0x00 };
this.Write(formchann, 0, 4);
byte[] sampleratebyte = basic.bit32(samplerate);
this.Write(sampleratebyte, 0, 4);
byte[] byterate = basic.bit32(samplerate*4);
this.Write(byterate, 0, 4);
byte[] blockalign = new byte[] { 0x04, 0x00 };
this.Write(blockalign, 0, 2);
byte[] bits = new byte[] { 0x10, 0x00 };
this.Write(bits, 0, 2);
byte[] data = new byte[] { 0x64, 0x61, 0x74, 0x61 };
this.Write(data, 0, 4);
byte[] samplesbyte = basic.bit32(len*4);
this.Write(samplesbyte, 0, 4);
}
public void sound(int[] w, int len, wavfile wavorigin = null) {
byte[] wavbyt = new byte[len*4];
for (int t = 0; t < len*2; t++) {
byte[] wavbit16 = basic.bit16(w[t]);
wavbyt[2*t] = wavbit16[0];
wavbyt[2*t + 1] = wavbit16[1];
}
this.Write(wavbyt, 0, len*4);
System.Media.SoundPlayer player = new System.Media.SoundPlayer();
player.SoundLocation = this.Name;
while (true) {
player.Play();
Console.WriteLine("repeat?");
if (Console.ReadLine() == "no") {
break;
}
}
}
}
class Program {
static void Main() {
int[] song = new int[45000*2];
byte[] songbyt = File.ReadAllBytes("name.wav"); //use your stereo, 16bits per sample wav-file
for (int t = 0; t < 45000*2; t++) {
byte[] songbytsamp = new byte[2] { songbyt[44 + 2*t], songbyt[44 + 2*t + 1] }; //I skip the header
song[t] = basic.bitint16(songbytsamp)/2; //I divide by 2 here, remove the "/2" to hear the normal sound again
//song[t] *= 2;
}
wavfile wav = new wavfile("test.wav", 45000); //constructor class that writes the header of a .wav file
wav.sound(song, 45000); //method that writes the data from "song" into the .wav file
}
}
}
The problem is not the rounding down that happens when you divide an odd number by 2; you can uncomment the line that says song[t] *= 2; and hear for yourself that all of the distortion has completely disappeared again.
I must be making a small stupid mistake somewhere, but I cannot find it. I just want to make the sound data quieter to avoid distortion when I add more sounds to it.
Well, I knew it would be something stupid, and I was right. I forgot to account for the fact that negative numbers are written in signed 16 bit language as the numbers above 2^15, and when you divide by 2, you push them into (very large) positive values. I altered my code to substract 2^16 from any number that's above 2^15 before dividing by 2. I have to thank this person though: How to reduce volume of wav stream?
If this means that my question was a duplicate, then go ahead and delete it, but I'm letting it stay for now, because someone else might find it helpful.
Using Math.Pow to do bit and byte operations is a really bad idea. That function takes double values as inputs and returns a double. It also does exponentiation (not a trivial operation). Using traditional bit shift and mask operations is clearer, much faster and less likely to introduce noise (because of the inaccuracy of doubles).
As you noticed, you really want to work with unsigned quantities (like uint/UInt32 and ushort/UInt16). Sign extension trips up everyone when doing this sort of work.
This is not a full answer to your question, but it does present a way to do the byte operations that is arguably better.
First, create a small struct to hold a combination of a bit-mask and a shift quantity:
public struct MaskAndShift {
public uint Mask {get; set;}
public int Shift {get; set;}
}
Then I create two arrays of these structs for describing what should be done to extract individual bytes from a uint or a ushort. I put them both in a static class named Worker:
public static class Worker {
public static MaskAndShift[] Mask32 = new MaskAndShift[] {
new MaskAndShift {Mask = 0xFF000000, Shift = 24},
new MaskAndShift {Mask = 0x00FF0000, Shift = 16},
new MaskAndShift {Mask = 0x0000FF00, Shift = 8},
new MaskAndShift {Mask = 0x000000FF, Shift = 0},
};
public static MaskAndShift[] Mask16 = new MaskAndShift[] {
new MaskAndShift {Mask = 0x0000FF00, Shift = 8},
new MaskAndShift {Mask = 0x000000FF, Shift = 0},
};
}
Looking at the first entry in the first array, it says "to extract the first byte from a uint, mask that uint with 0xFF000000 and shift the result 24 bits to the right". If you have endian-ness issues, you can simply re-order the entries in the array.
Then I created this static function (in the Worker class) to convert a uint / UInt32 to an array of four bytes:
public static byte[] UintToByteArray (uint input) {
var bytes = new byte[4];
int i = 0;
foreach (var maskPair in Mask32) {
var masked = input & maskPair.Mask;
if (maskPair.Shift != 0) {
masked >>= maskPair.Shift;
}
bytes[i++] = (byte) masked;
}
return bytes;
}
The code to do the same operation for a 16 bit ushort (aka UInt16) looks nearly the same (there's probably an opportunity for some refactoring here):
public static byte[] UShortToByteArray (ushort input) {
var bytes = new byte[2];
int i = 0;
foreach (var maskPair in Mask16) {
var masked = input & maskPair.Mask;
if (maskPair.Shift != 0) {
masked >>= maskPair.Shift;
}
bytes[i++] = (byte) masked;
}
return bytes;
}
The reverse operation is much simpler (however, if you have endian-ness issues, you'll need to write the code). Here I just take the entries of the array, add them into a value and shift the result:
public static uint ByteArrayToUint (byte[] bytes) {
uint result = 0;
//note that the first time through, result is zero, so shifting is a noop
foreach (var b in bytes){
result <<= 8;
result += b;
}
return result;
}
Doing this for the 16 bit version ends up being effectively the same code, so...
public static ushort ByteArrayToUshort (byte[] bytes) {
return (ushort) ByteArrayToUint(bytes);
}
Bit-twiddling never works the first time. So I wrote some test code:
public static void Main(){
//pick a nice obvious pattern
uint bit32Test = (((0xF1u * 0x100u) + 0xE2u) * 0x100u + 0xD3u) * 0x100u + 0xC4u;
Console.WriteLine("Start");
Console.WriteLine("Input 32 Value: " + bit32Test.ToString("X"));
var bytes32 = Worker.UintToByteArray(bit32Test);
foreach (var b in bytes32){
Console.WriteLine(b.ToString("X"));
}
Console.WriteLine();
ushort bit16Test = (ushort)((0xB5u * 0x100u) + 0xA6u);
Console.WriteLine("Input 16 Value: " + bit16Test.ToString("X"));
var bytes16 = Worker.UShortToByteArray(bit16Test);
foreach (var b in bytes16){
Console.WriteLine(b.ToString("X"));
}
Console.WriteLine("\r\nNow the reverse");
uint reconstitued32 = Worker.ByteArrayToUint(bytes32);
Console.WriteLine("Reconstituted 32: " + reconstitued32.ToString("X"));
ushort reconstitued16 = Worker.ByteArrayToUshort(bytes16);
Console.WriteLine("Reconstituted 16: " + reconstitued16.ToString("X"));
}
The output from that test code looks like:
Start
Input 32 Value: F1E2D3C4
F1
E2
D3
C4
Input 16 Value: B5A6
B5
A6
Now the reverse
Reconstituted 32: F1E2D3C4
Reconstituted 16: B5A6
Also note that I do everything in hexadecimal - it makes everything so much easier to read and to understand.

Want to write to a file converting an integer list of Hex to Char

So I have a list full of integers. These integers are hexadecimals. I would like to convert this list to ASCII Chars. Once that is done I would like to write the ASCII chars to a file. Here is what I have so far:
public byte[] buffer;
public List<int> list= new List<int>(new int[3]);
list[0] = 5445535420; //AKA header[0] represents the hex integers for Test_ where _ is a space
list[1] = 0; // so the char would be null
list[2] = 4a4153; // would be JAS
System.IO.FileStream fs;
fs = new FileStream(filename, FileMode.OpenOrCreate);
if (fs.CanWrite)
{
for (int i=0;i<list.Count();i++)
{
buffer = Encoding.ASCII.GetBytes(list[i].ToString());
Convert.ToChar(header[i]);
fs.Write(buffer, 0, buffer.Length);
}
}
Would this work for you? Hope comments are self-explenatory
int intFromHexLiteral = 0x4a4153;
var hexString = intFromHexLiteral.ToString("X"); // "4A4153"
var hexCharsList = Split(hexString, 2).ToList(); // ["4A", "41", "53"]
var charsArray = hexCharsList
.Select(hexChar => Convert.ToInt32(hexChar, 16)) // [74, 65, 83]
.Select(i => (char) i) // ['J', 'A', 'S']
.ToArray();
var word = new string(charsArray); // "JAS"
private static IEnumerable<string> Split(string str, int chunkSize) =>
Enumerable.Range(0, str.Length / chunkSize)
.Select(i => str.Substring(i * chunkSize, chunkSize));
An integer does not equal the bytes of the ASCII characters. I.e 1010 is not 0x1010 in hex. In your case it would make more sense to use byte[] an write each hex character explicitly.
class Program
{
static void Main(string[] args)
{
List<byte[]> list = new List<byte[]>();
list.Add(new byte[]{0x54, 0x45, 0x53, 0x54, 0x20}); //AKA header[0] represents the hex integers for Test_ where _ is a space
list.Add(new byte[]{0x0}); // so the char would be null
list.Add(new byte[]{ 0x4a, 0x41, 0x53 }); // would be JAS
foreach (var b in list)
{
var chars = Encoding.ASCII.GetChars(b);
var s = new string(chars);
Console.WriteLine(s);
}
}
}
I see that you've selected an answer, but I wanted to show you this method of solving your problem dealing with your data strictly as numeric data. The values you stick into your List<int> are small enough to fit into a long so I changed it to a List<long>. If they ever become bigger than that, than this solution would not work.
See how I broke each long element, byte by byte, in reverse and stored the conversion into a StringBuilder before writing it to the screen. In your case, you would write to a file instead, but could use the same conversion method.
using System;
using System.Collections.Generic;
using System.Text;
public class Program
{
public static void Main(string[] args)
{
List<long> list = new List<long>(new long[3]);
list[0] = 0x5445535420; // AKA header[0] represents the hex integers for Test_ where _ is a space
list[1] = 0; // so the char would be null
list[2] = 0x4a4153; // would be JAS
for (int i = 0; i < list.Count; i++)
{
StringBuilder sb = new StringBuilder();
// Break down each element byte by byte in reverse
while (list[i] > 0)
{
// Anding against 0xFF to only have the least significant byte to convert into a char
sb.Insert(0, Convert.ToChar(list[i] & 0xFF));
list[i] >>= 8; // Remove the least significant byte
}
Console.WriteLine(sb);
}
}
}
Result:
TEST
JAS

Replace two bytes in a generic list

I want to replace every occurrence of one of those magic 2-byte packages in my List<byte> with a single byte:
{ 0xF8, 0x00 } -> Replace with 0xF8
{ 0xF8, 0x01 } -> Replace with 0xFB
{ 0xF8, 0x02 } -> Replace with 0xFD
{ 0xF8, 0x03 } -> Replace with 0xFE
For example:
List<byte> message
= new List<byte> { 0xFF, 0xFF, 0xFB, 0xF8, 0x00, 0xF8, 0x01, 0xF8, 0x02, 0xF8, 0x03, 0xFE };
// will be converted to:
List<byte> expected
= new List<byte> { 0xFF, 0xFF, 0xFB, 0xF8, 0xFB, 0xFD, 0xFE, 0xFE };
This is my solution so far, which works but I don't like it because its readability is quite bad:
public static void RemoveEscapeSequences(List<byte> message)
{
// skipped parameter checks
for (int index = 0; index < message.Count - 1; ++index)
{
if (message[index] == 0xF8)
{
// found an escaped byte, look at the following byte to determine replacement
switch (message[index + 1])
{
case 0x0:
message[index] = 0xF8;
message.RemoveAt(index + 1);
break;
case 0x1:
message[index] = 0xFB;
message.RemoveAt(index + 1);
break;
case 0x2:
message[index] = 0xFD;
message.RemoveAt(index + 1);
break;
case 0x3:
message[index] = 0xFE;
message.RemoveAt(index + 1);
break;
}
}
}
}
Is there a shorter solution with improved readability?
You can do something like this - it'll be slightly slower, though:
public static void RemoveEscapeSequences(List<byte> message)
{
var replaceBytes = new Dictionary<byte, byte>()
{
{0x00, 0xF8}, {0x01, 0xFB}, {0x02, 0xFD}, {0x03, 0xFE}
};
// skipped parameter checks
for (int index = 0; index < message.Count - 1; ++index)
{
if (message[index] == 0xF8)
{
if(replaceBytes.ContainsKey(message[index + 1]))
{
message[index] = replaceBytes[message[index + 1]];
message.RemoveAt(index + 1);
}
}
}
}
You can use following extension method:
public static IEnumerable<byte> Escape(this IEnumerable<byte> source)
{
if (source == null)
throw new ArgumentNullException("source");
using (var enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
byte current = enumerator.Current;
if (current != 0xF8)
{
yield return current;
continue;
}
if (!enumerator.MoveNext())
yield return current;
byte next = enumerator.Current;
switch (next)
{
case 0x00: yield return 0xF8; break;
case 0x01: yield return 0xFB; break;
case 0x02: yield return 0xFD; break;
case 0x03: yield return 0xFE; break;
default:
yield return current;
yield return next;
break;
}
}
}
}
Usage:
List<byte> result = message.Escape().ToList();
foreach(var b in message.Escape())
Console.Write("0x{0:x} ", b);
Here's a very simple/understandable version, that should be efficient too:
private static List<byte> ComputeBytes(List<byte> input)
{
byte magicValueFirstByte = 0xF8;
var secondByteToBeReplaced = new List<byte> { 0x00, 0x01, 0x02, 0x03 };
var replacements = new List<byte> { 0xF8, 0xFB, 0xFD, 0xFE };
var output = new List<byte>();
for (int i = 0; i < input.Count; i++)
{
var currentValue = input[i];
if (currentValue == magicValueFirstByte && i < input.Count - 1)
{
int index = secondByteToBeReplaced.IndexOf(input[i + 1]);
if (index >= 0)
{
// Then when must replace
output.Add(replacements[index]);
// Skip next item
i++;
continue;
}
}
// Won't replace value, so add current one
output.Add(currentValue);
}
return output;
}
Why not put the replacement bytes in a small array?
private static byte[] EscapeBytes = new byte[]
{
/* 0x00 */ 0xF8,
/* 0x01 */ 0xFB,
/* 0x02 */ 0xFD,
/* 0x03 */ 0xFE
};
Then you can simply index the array:
public static List<byte> RemoveEscapeSequences(List<byte> message)
{
List<byte> result = new List<byte>(message.Count);
bool escape = false;
foreach (byte value in message)
{
if (escape)
{
escape = false;
// Replace the byte. NOTE 1!
result.Add(EscapeBytes[value]);
}
else if (value == 0xF8)
{
// Started an escape sequence.
escape = true;
}
else
{
// Just add the byte.
result.Add(value);
}
}
return result;
}
By adding the bytes to a new list whose capacity is already sufficient to hold the result, you even get a little bit more performance and a lot more readability.
Note 1: When the byte after the 0xF8 escape byte is not between 0 and 3, an IndexOutOfRangeException will occur. If you care, you'll have to add a small check to see whether the byte value is within range, and decide on what to do if its not (not replacing the bytes, removing the escape, throwing an exception).

Replace sequence of bytes in binary file

What is the best method to replace sequence of bytes in binary file to the same length of other bytes? The binary files will be pretty large, about 50 mb and should not be loaded at once in memory.
Update: I do not know location of bytes which needs to be replaced, I need to find them first.
Assuming you're trying to replace a known section of the file.
Open a FileStream with read/write access
Seek to the right place
Overwrite existing data
Sample code coming...
public static void ReplaceData(string filename, int position, byte[] data)
{
using (Stream stream = File.Open(filename, FileMode.Open))
{
stream.Position = position;
stream.Write(data, 0, data.Length);
}
}
If you're effectively trying to do a binary version of a string.Replace (e.g. "always replace bytes { 51, 20, 34} with { 20, 35, 15 } then it's rather harder. As a quick description of what you'd do:
Allocate a buffer of at least the size of data you're interested in
Repeatedly read into the buffer, scanning for the data
If you find a match, seek back to the right place (e.g. stream.Position -= buffer.Length - indexWithinBuffer; and overwrite the data
Sounds simple so far... but the tricky bit is if the data starts near the end of the buffer. You need to remember all potential matches and how far you've matched so far, so that if you get a match when you read the next buffer's-worth, you can detect it.
There are probably ways of avoiding this trickiness, but I wouldn't like to try to come up with them offhand :)
EDIT: Okay, I've got an idea which might help...
Keep a buffer which is at least twice as big as you need
Repeatedly:
Copy the second half of the buffer into the first half
Fill the second half of the buffer from the file
Search throughout the whole buffer for the data you're looking for
That way at some point, if the data is present, it will be completely within the buffer.
You'd need to be careful about where the stream was in order to get back to the right place, but I think this should work. It would be trickier if you were trying to find all matches, but at least the first match should be reasonably simple...
My solution :
/// <summary>
/// Copy data from a file to an other, replacing search term, ignoring case.
/// </summary>
/// <param name="originalFile"></param>
/// <param name="outputFile"></param>
/// <param name="searchTerm"></param>
/// <param name="replaceTerm"></param>
private static void ReplaceTextInBinaryFile(string originalFile, string outputFile, string searchTerm, string replaceTerm)
{
byte b;
//UpperCase bytes to search
byte[] searchBytes = Encoding.UTF8.GetBytes(searchTerm.ToUpper());
//LowerCase bytes to search
byte[] searchBytesLower = Encoding.UTF8.GetBytes(searchTerm.ToLower());
//Temporary bytes during found loop
byte[] bytesToAdd = new byte[searchBytes.Length];
//Search length
int searchBytesLength = searchBytes.Length;
//First Upper char
byte searchByte0 = searchBytes[0];
//First Lower char
byte searchByte0Lower = searchBytesLower[0];
//Replace with bytes
byte[] replaceBytes = Encoding.UTF8.GetBytes(replaceTerm);
int counter = 0;
using (FileStream inputStream = File.OpenRead(originalFile)) {
//input length
long srcLength = inputStream.Length;
using (BinaryReader inputReader = new BinaryReader(inputStream)) {
using (FileStream outputStream = File.OpenWrite(outputFile)) {
using (BinaryWriter outputWriter = new BinaryWriter(outputStream)) {
for (int nSrc = 0; nSrc < srcLength; ++nSrc)
//first byte
if ((b = inputReader.ReadByte()) == searchByte0
|| b == searchByte0Lower) {
bytesToAdd[0] = b;
int nSearch = 1;
//next bytes
for (; nSearch < searchBytesLength; ++nSearch)
//get byte, save it and test
if ((b = bytesToAdd[nSearch] = inputReader.ReadByte()) != searchBytes[nSearch]
&& b != searchBytesLower[nSearch]) {
break;//fail
}
//Avoid overflow. No need, in my case, because no chance to see searchTerm at the end.
//else if (nSrc + nSearch >= srcLength)
// break;
if (nSearch == searchBytesLength) {
//success
++counter;
outputWriter.Write(replaceBytes);
nSrc += nSearch - 1;
}
else {
//failed, add saved bytes
outputWriter.Write(bytesToAdd, 0, nSearch + 1);
nSrc += nSearch;
}
}
else
outputWriter.Write(b);
}
}
}
}
Console.WriteLine("ReplaceTextInBinaryFile.counter = " + counter);
}
You can use my BinaryUtility to search and replace one or more bytes without loading the entire file into memory like this:
var searchAndReplace = new List<Tuple<byte[], byte[]>>()
{
Tuple.Create(
BitConverter.GetBytes((UInt32)0xDEADBEEF),
BitConverter.GetBytes((UInt32)0x01234567)),
Tuple.Create(
BitConverter.GetBytes((UInt32)0xAABBCCDD),
BitConverter.GetBytes((UInt16)0xAFFE)),
};
using(var reader =
new BinaryReader(new FileStream(#"C:\temp\data.bin", FileMode.Open)))
{
using(var writer =
new BinaryWriter(new FileStream(#"C:\temp\result.bin", FileMode.Create)))
{
BinaryUtility.Replace(reader, writer, searchAndReplace);
}
}
BinaryUtility code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
public static class BinaryUtility
{
public static IEnumerable<byte> GetByteStream(BinaryReader reader)
{
const int bufferSize = 1024;
byte[] buffer;
do
{
buffer = reader.ReadBytes(bufferSize);
foreach (var d in buffer) { yield return d; }
} while (bufferSize == buffer.Length);
}
public static void Replace(BinaryReader reader, BinaryWriter writer, IEnumerable<Tuple<byte[], byte[]>> searchAndReplace)
{
foreach (byte d in Replace(GetByteStream(reader), searchAndReplace)) { writer.Write(d); }
}
public static IEnumerable<byte> Replace(IEnumerable<byte> source, IEnumerable<Tuple<byte[], byte[]>> searchAndReplace)
{
foreach (var s in searchAndReplace)
{
source = Replace(source, s.Item1, s.Item2);
}
return source;
}
public static IEnumerable<byte> Replace(IEnumerable<byte> input, IEnumerable<byte> from, IEnumerable<byte> to)
{
var fromEnumerator = from.GetEnumerator();
fromEnumerator.MoveNext();
int match = 0;
foreach (var data in input)
{
if (data == fromEnumerator.Current)
{
match++;
if (fromEnumerator.MoveNext()) { continue; }
foreach (byte d in to) { yield return d; }
match = 0;
fromEnumerator.Reset();
fromEnumerator.MoveNext();
continue;
}
if (0 != match)
{
foreach (byte d in from.Take(match)) { yield return d; }
match = 0;
fromEnumerator.Reset();
fromEnumerator.MoveNext();
}
yield return data;
}
if (0 != match)
{
foreach (byte d in from.Take(match)) { yield return d; }
}
}
}
public static void BinaryReplace(string sourceFile, byte[] sourceSeq, string targetFile, byte[] targetSeq)
{
FileStream sourceStream = File.OpenRead(sourceFile);
FileStream targetStream = File.Create(targetFile);
try
{
int b;
long foundSeqOffset = -1;
int searchByteCursor = 0;
while ((b=sourceStream.ReadByte()) != -1)
{
if (sourceSeq[searchByteCursor] == b)
{
if (searchByteCursor == sourceSeq.Length - 1)
{
targetStream.Write(targetSeq, 0, targetSeq.Length);
searchByteCursor = 0;
foundSeqOffset = -1;
}
else
{
if (searchByteCursor == 0)
{
foundSeqOffset = sourceStream.Position - 1;
}
++searchByteCursor;
}
}
else
{
if (searchByteCursor == 0)
{
targetStream.WriteByte((byte) b);
}
else
{
targetStream.WriteByte(sourceSeq[0]);
sourceStream.Position = foundSeqOffset + 1;
searchByteCursor = 0;
foundSeqOffset = -1;
}
}
}
}
finally
{
sourceStream.Dispose();
targetStream.Dispose();
}
}

C# Replace bytes in Byte[]

What is the best way to replace some bytes in a byte array??
For instance i have bytesFromServer = listener.Receive(ref groupEP); and i can do BitConverter.ToString(bytesFromServer) to convert it into a readable format to return something along the lines of
48 65 6c 6c 6f 20
74 68 65 72 65 20
68 65 6c 70 66 75
6c 20 70 65 6f 70
6c 65
I would like to replace something inside of that making "68 65 6c" to something like "68 00 00" (just as an example). There is not .Replace() on a byte[].
Would there be an easy way to convert that back into a byte[]?
Any help appreciated. Thank you!
You could program it.... try this for a start... this is however not robust not production like code yet...beaware of off-by-one errors I didn't fully test this...
public int FindBytes(byte[] src, byte[] find)
{
int index = -1;
int matchIndex = 0;
// handle the complete source array
for(int i=0; i<src.Length; i++)
{
if(src[i] == find[matchIndex])
{
if (matchIndex==(find.Length-1))
{
index = i - matchIndex;
break;
}
matchIndex++;
}
else if (src[i] == find[0])
{
matchIndex = 1;
}
else
{
matchIndex = 0;
}
}
return index;
}
public byte[] ReplaceBytes(byte[] src, byte[] search, byte[] repl)
{
byte[] dst = null;
int index = FindBytes(src, search);
if (index>=0)
{
dst = new byte[src.Length - search.Length + repl.Length];
// before found array
Buffer.BlockCopy(src,0,dst,0, index);
// repl copy
Buffer.BlockCopy(repl,0,dst,index,repl.Length);
// rest of src array
Buffer.BlockCopy(
src,
index+search.Length ,
dst,
index+repl.Length,
src.Length-(index+search.Length));
}
return dst;
}
Implement as an extension method
public void Replace(this byte[] src, byte[] search, byte[] repl)
{
ReplaceBytes(src, search, repl);
}
usage normal method:
ReplaceBytes(bytesfromServer,
new byte[] {0x75, 0x83 } ,
new byte[]{ 0x68, 0x65, 0x6c});
Extension method usage:
bytesfromServer.Replace(
new byte[] {0x75, 0x83 },
new byte[]{ 0x68, 0x65, 0x6c});
Improving on rene's code, I created a while loop for it to replace all occurences:
public static byte[] ReplaceBytes(byte[] src, byte[] search, byte[] repl)
{
byte[] dst = null;
byte[] temp = null;
int index = FindBytes(src, search);
while (index >= 0)
{
if (temp == null)
temp = src;
else
temp = dst;
dst = new byte[temp.Length - search.Length + repl.Length];
// before found array
Buffer.BlockCopy(temp, 0, dst, 0, index);
// repl copy
Buffer.BlockCopy(repl, 0, dst, index, repl.Length);
// rest of src array
Buffer.BlockCopy(
temp,
index + search.Length,
dst,
index + repl.Length,
temp.Length - (index + search.Length));
index = FindBytes(dst, search);
}
return dst;
}
This method will work, but if the source bytes is too huge, I prefer to have a "windowing" function to process the bytes chunk by chunk. Else it will take a huge amount of memory.
How about Array.Copy?
Unfortunately there are issues with all of the posts (as already pointed out in comments). There is a correct answer in this other question
I needed a solution so for myself and wrote the following code. This is also more flexible in using enumerable and multiple search replace terms.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class ByteTools
{
static void ByteReplaceTests()
{
var examples = new(string source, string search, string replace)[]
{
("bababanana", "babanana", "apple"),
("hello guys", "hello", "hello world"),
("apple", "peach", "pear"),
("aaaa", "a", "abc"),
("pear", "pear", ""),
("good morning world", "morning", "morning"),
("ababab", "ab", "ababab"),
("ababab", "abab", "ab"),
("", "aa", "bb"),
};
int i = 0;
foreach (var (source, search, replace) in examples)
{
var stringReplaceResults = source.Replace(search, replace);
var sourceByte = Encoding.ASCII.GetBytes(source);
var searchByte = Encoding.ASCII.GetBytes(search);
var replaceByte = Encoding.ASCII.GetBytes(replace);
//converts string values to bytes, does the replace, then converts back to string
var byteReplaceResults = Encoding.ASCII.GetString(
ByteReplace(sourceByte, (searchByte, replaceByte)).ToArray());
Console.WriteLine($"{i}: {source}, {search}, {replace}");
Console.WriteLine($" String.Replace() => {stringReplaceResults}");
Console.WriteLine($" BytesReplace() => {byteReplaceResults}");
i++;
}
}
static IEnumerable<byte> ByteReplace(IEnumerable<byte> source, params (byte[] search, byte[] replace)[] replacements)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (replacements == null)
throw new ArgumentNullException(nameof(replacements));
if (replacements.Any(r => r.search == null || r.search.Length == 0))
throw new ArgumentOutOfRangeException(nameof(replacements), "Search parameter cannot be null or empty");
if (replacements.Any(r => r.replace == null))
throw new ArgumentOutOfRangeException(nameof(replacements), "Replace parameter cannot be null");
var maxMatchSize = replacements.Select(r => r.search.Length).Max();
var bufferSize = maxMatchSize * 2;
var buffer = new byte[bufferSize];
int bufferStart = 0;
int bufferPosition = 0;
byte[] nextBytes()
{
foreach ((byte[] search, byte[] replace) in replacements)
{
if (ByteStartsWith(buffer, bufferStart, bufferPosition - bufferStart, search))
{
bufferStart += search.Length;
return replace;
}
}
var returnBytes = new byte[] { buffer[bufferStart] };
bufferStart++;
return returnBytes;
}
foreach (var dataByte in source)
{
buffer[bufferPosition] = dataByte;
bufferPosition++;
if (bufferPosition - bufferStart >= maxMatchSize)
{
foreach (var resultByte in nextBytes())
yield return resultByte;
}
if (bufferPosition == bufferSize - 1)
{
Buffer.BlockCopy(buffer, bufferStart, buffer, 0, bufferPosition - bufferStart);
bufferPosition -= bufferStart;
bufferStart = 0;
}
}
while (bufferStart < bufferPosition)
{
foreach (var resultByte in nextBytes())
yield return resultByte;
}
}
static bool ByteStartsWith(byte[] data, int dataOffset, int dataLength, byte[] startsWith)
{
if (data == null)
throw new ArgumentNullException(nameof(data));
if (startsWith == null)
throw new ArgumentNullException(nameof(startsWith));
if (dataLength < startsWith.Length)
return false;
for (int i = 0; i < startsWith.Length; i++)
{
if (data[i + dataOffset] != startsWith[i])
return false;
}
return true;
}
}
public static byte[] ReplaceBytes(byte[] src, byte[] search, byte[] repl)
{
if (repl == null) return src;
int index = FindBytes(src, search);
if (index < 0) return src;
byte[] dst = new byte[src.Length - search.Length + repl.Length];
Buffer.BlockCopy(src, 0, dst, 0, index);
Buffer.BlockCopy(repl, 0, dst, index, repl.Length);
Buffer.BlockCopy(src, index + search.Length, dst, index + repl.Length,src.Length - (index + search.Length));
return dst;
}
public static int FindBytes(byte[] src, byte[] find)
{
if(src==null|| find==null|| src.Length==0|| find.Length == 0 || find.Length> src.Length) return -1;
for (int i = 0; i < src.Length - find.Length +1 ; i++)
{
if (src[i] == find[0])
{
for(int m=1;m< find.Length;m++)
{
if (src[i + m] != find[m]) break;
if (m == find.Length - 1) return i;
}
}
}
return -1;
}
this may be a good method , i have test in lots of codes.
Something i pieced together... Going to test it soon. Credits from How do you convert Byte Array to Hexadecimal String, and vice versa?
public byte[] ReplaceBytes(byte[] src, string replace, string replacewith)
{
string hex = BitConverter.ToString(src);
hex = hex.Replace("-", "");
hex = hex.Replace(replace, replacewith);
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}

Categories

Resources