Skip to content

Commit

Permalink
make bindiff much better
Browse files Browse the repository at this point in the history
  • Loading branch information
fuzziqersoftware committed Feb 22, 2025
1 parent 9ab6090 commit 6d9db04
Showing 1 changed file with 165 additions and 38 deletions.
203 changes: 165 additions & 38 deletions src/BinDiff.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,63 +4,190 @@

#include <string>

#include "Arguments.hh"
#include "Filesystem.hh"
#include "JSON.hh"

using namespace std;
using namespace phosg;

void print_usage() {
fprintf(stderr, "\
Usage: bindiff file1 file2\n\
\n\
If either file1 or file2 is -, read from standard input. At most one can be -.\n\
\n");
}
void print_binary_diff(
FILE* stream,
const void* data1v,
size_t size1,
const void* data2v,
size_t size2,
bool use_color,
size_t context_lines) {
const uint8_t* data1 = reinterpret_cast<const uint8_t*>(data1v);
const uint8_t* data2 = reinterpret_cast<const uint8_t*>(data2v);

int main(int argc, char** argv) {
const char* filename1 = nullptr;
const char* filename2 = nullptr;
for (int x = 1; x < argc; x++) {
if (argv[x][0] == '-') {
if (!strcmp(argv[x], "--help")) {
print_usage();
return 1;
size_t max_data_size = std::max<size_t>(size1, size2);
int offset_width_digits;
if (max_data_size > 0x100000000) {
offset_width_digits = 16;
} else if (max_data_size > 0x10000) {
offset_width_digits = 8;
} else if (max_data_size > 0x100) {
offset_width_digits = 4;
} else {
offset_width_digits = 2;
}

auto print_diff_line = [&](char left_ch,
const uint8_t* data,
size_t size,
size_t line_index,
uint16_t diff_flags,
TerminalFormat color) {
size_t line_start_offset = line_index * 0x10;
if (use_color) {
print_color_escape(stream, color, TerminalFormat::END);
}
fprintf(stream, "%c %0*zX |", left_ch, offset_width_digits, line_start_offset);
for (size_t within_line_offset = 0; within_line_offset < 0x10; within_line_offset++) {
size_t offset = (line_index * 0x10) + within_line_offset;
if (offset < size) {
if (use_color && (diff_flags & (1 << within_line_offset))) {
print_color_escape(stream, color, TerminalFormat::BOLD, TerminalFormat::END);
fprintf(stream, " %02hhX", data[offset]);
print_color_escape(stream, TerminalFormat::NORMAL, color, TerminalFormat::END);
} else {
fprintf(stream, " %02hhX", data[offset]);
}
} else {
fprintf(stream, " ");
}
}
fprintf(stream, " | ");
for (size_t within_line_offset = 0; within_line_offset < 0x10; within_line_offset++) {
size_t offset = (line_index * 0x10) + within_line_offset;
if (offset < size) {
char ch = data[offset];
if (ch < 0x20 || ch > 0x7E) {
ch = ' ';
}
if (use_color && (diff_flags & (1 << within_line_offset))) {
print_color_escape(stream, color, TerminalFormat::BOLD, TerminalFormat::END);
fputc(ch, stream);
print_color_escape(stream, TerminalFormat::NORMAL, color, TerminalFormat::END);
} else {
fputc(ch, stream);
}
} else {
throw runtime_error(string_printf("unknown argument: %s\n", argv[x]));
fputc(' ', stream);
}
}
if (use_color) {
print_color_escape(stream, TerminalFormat::NORMAL, TerminalFormat::END);
}
fputc('\n', stream);
};

auto print_diff_line_pair = [&](size_t line_index, uint16_t diff_flags) -> void {
size_t line_start_offset = line_index * 0x10;
if (diff_flags == 0) {
print_diff_line(' ', data1, size1, line_index, diff_flags, TerminalFormat::NORMAL);
} else {
if (line_start_offset < size1) {
print_diff_line('-', data1, size1, line_index, diff_flags, TerminalFormat::FG_RED);
}
if (line_start_offset < size2) {
print_diff_line('+', data2, size2, line_index, diff_flags, TerminalFormat::FG_GREEN);
}
}
};

size_t first_unprinted_line_index = 0;
ssize_t last_different_line_index = -(context_lines + 1);
size_t num_lines = ((max_data_size + 0x0F) >> 4);
for (size_t line_index = 0; line_index < num_lines; line_index++) {
uint16_t diff_flags = 0;
for (size_t within_line_offset = 0; within_line_offset < 0x10; within_line_offset++) {
size_t offset = (line_index * 0x10) + within_line_offset;
uint16_t data1_value = (offset < size1) ? static_cast<uint8_t>(data1[offset]) : 0xFFFF;
uint16_t data2_value = (offset < size2) ? static_cast<uint8_t>(data2[offset]) : 0xFFFF;
if (data1_value != data2_value) {
diff_flags |= (1 << within_line_offset);
}
}
if (diff_flags == 0) {
if (static_cast<ssize_t>(line_index) <= last_different_line_index + static_cast<ssize_t>(context_lines)) {
print_diff_line_pair(line_index, diff_flags);
first_unprinted_line_index = line_index + 1;
}
} else if (!filename1) {
filename1 = argv[x];
} else if (!filename2) {
filename2 = argv[x];

} else {
throw runtime_error("too many positional arguments given");
bool has_unprinted_gap;
size_t chunk_start_line_index;
if ((first_unprinted_line_index + context_lines) >= line_index) {
chunk_start_line_index = first_unprinted_line_index;
has_unprinted_gap = false;
} else {
chunk_start_line_index = line_index - context_lines;
has_unprinted_gap = true;
}

if (has_unprinted_gap) {
fprintf(stream, " ...\n");
}

for (size_t z = chunk_start_line_index; z < line_index; z++) {
print_diff_line_pair(z, 0x0000);
}
print_diff_line_pair(line_index, diff_flags);
first_unprinted_line_index = line_index + 1;
last_different_line_index = line_index;
}
}

if (!filename1 || !filename2) {
throw runtime_error("two filenames are required");
if (first_unprinted_line_index < num_lines) {
fprintf(stream, " ...\n");
}
}

void print_usage() {
fprintf(stderr, "\
Usage: bindiff [options] file1 file2\n\
\n\
If either file1 or file2 is -, read from standard input. If both are -,\n\
bindiffexits immediately with no output.\n\
\n\
Options:\n\
--help: You're reading it now.\n\
--context=N: Show N lines of matching bytes around each differing byte.\n\
(Default 3)\n\
--color: Highlight differing bytes even if the output is not a TTY.\n\
--no-color: Don't highlight differing bytes even if the output is a TTY.\n\
\n");
}

string data1 = (strcmp(filename1, "-") == 0) ? read_all(stdin) : load_file(filename1);
string data2 = (strcmp(filename2, "-") == 0) ? read_all(stdin) : load_file(filename2);
if (data1 == data2) {
int main(int argc, char** argv) {
Arguments args(argv, argc);
if (args.get<bool>("help")) {
print_usage();
return 0;
}

auto lines1 = split(format_data(data1), '\n');
auto lines2 = split(format_data(data2), '\n');
auto filename1 = args.get<string>(1);
auto filename2 = args.get<string>(2);
if (filename1 == filename2) {
return 0;
}

fprintf(stdout, "--- %s\n", filename1);
fprintf(stdout, "+++ %s\n", filename1);
for (size_t z = 0; z < min<size_t>(lines1.size(), lines2.size()); z++) {
if (lines1[z] != lines2[z]) {
fprintf(stdout, "-%s\n", lines1[z].c_str());
fprintf(stdout, "+%s\n", lines2[z].c_str());
} else {
fprintf(stdout, " %s\n", lines1[z].c_str());
}
bool use_color;
if (args.get<bool>("no-color")) {
use_color = false;
} else if (args.get<bool>("color")) {
use_color = true;
} else {
use_color = isatty(fileno(stdout));
}
size_t context_lines = args.get<size_t>("context", 3);

string data1 = (filename1 == "-") ? read_all(stdin) : load_file(filename1);
string data2 = (filename2 == "-") ? read_all(stdin) : load_file(filename2);

return 4;
print_binary_diff(stdout, data1.data(), data1.size(), data2.data(), data2.size(), use_color, context_lines);
return 0;
}

0 comments on commit 6d9db04

Please sign in to comment.