Skip to content

Commit

Permalink
[WIP] byte colouring
Browse files Browse the repository at this point in the history
  • Loading branch information
solemnwarning committed Apr 20, 2024
1 parent 35cad11 commit 44f06e9
Show file tree
Hide file tree
Showing 8 changed files with 919 additions and 33 deletions.
4 changes: 4 additions & 0 deletions Makefile
Expand Up @@ -351,6 +351,7 @@ APP_OBJS := \
src/BitmapTool.$(BUILD_TYPE).o \
src/buffer.$(BUILD_TYPE).o \
src/BytesPerLineDialog.$(BUILD_TYPE).o \
src/ByteColourMap.$(BUILD_TYPE).o \
src/ByteRangeSet.$(BUILD_TYPE).o \
src/CharacterEncoder.$(BUILD_TYPE).o \
src/CharacterFinder.$(BUILD_TYPE).o \
Expand Down Expand Up @@ -396,6 +397,7 @@ APP_OBJS := \
src/RangeProcessor.$(BUILD_TYPE).o \
src/search.$(BUILD_TYPE).o \
src/SettingsDialog.$(BUILD_TYPE).o \
src/SettingsDialogByteColour.$(BUILD_TYPE).o \
src/SettingsDialogHighlights.$(BUILD_TYPE).o \
src/StringPanel.$(BUILD_TYPE).o \
src/textentrydialog.$(BUILD_TYPE).o \
Expand Down Expand Up @@ -451,6 +453,7 @@ TEST_OBJS := \
src/BitOffset.$(BUILD_TYPE).o \
src/BitmapTool.$(BUILD_TYPE).o \
src/buffer.$(BUILD_TYPE).o \
src/ByteColourMap.$(BUILD_TYPE).o \
src/ByteRangeSet.$(BUILD_TYPE).o \
src/BytesPerLineDialog.$(BUILD_TYPE).o \
src/CharacterEncoder.$(BUILD_TYPE).o \
Expand Down Expand Up @@ -488,6 +491,7 @@ TEST_OBJS := \
src/RangeProcessor.$(BUILD_TYPE).o \
src/search.$(BUILD_TYPE).o \
src/SettingsDialog.$(BUILD_TYPE).o \
src/SettingsDialogByteColour.$(BUILD_TYPE).o \
src/SettingsDialogHighlights.$(BUILD_TYPE).o \
src/StringPanel.$(BUILD_TYPE).o \
src/Tab.$(BUILD_TYPE).o \
Expand Down
199 changes: 199 additions & 0 deletions src/ByteColourMap.cpp
@@ -0,0 +1,199 @@
/* Reverse Engineer's Hex Editor
* Copyright (C) 2024 Daniel Collins <solemnwarning@solemnwarning.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#include "App.hpp"
#include "ByteColourMap.hpp"

void REHex::ByteColourMap::set_colour(unsigned char byte, Colour colour1, Colour colour2, int colour_delta_steps, int colour_delta_pos)
{
auto &elem = bytes[byte];

assert(colour_delta_steps >= colour_delta_pos);

elem.colour1 = colour1;
elem.colour2 = colour2;
elem.colour_delta_steps = colour_delta_steps;
elem.colour_delta_pos = colour_delta_pos;
}

wxColour REHex::ByteColourMap::get_colour(unsigned char byte) const
{
auto &elem = bytes[byte];

return elem.get_colour();
}

const REHex::ByteColourMap::Value &REHex::ByteColourMap::get_value(unsigned char byte) const
{
return bytes[byte];
}

bool REHex::ByteColourMap::Value::is_single() const
{
return colour_delta_steps == 0;
}

bool REHex::ByteColourMap::Value::is_start() const
{
return colour_delta_pos == 0 && colour_delta_steps > 0;
}

bool REHex::ByteColourMap::Value::is_end() const
{
return colour_delta_pos == colour_delta_steps && colour_delta_steps > 0;
}

wxColour REHex::ByteColourMap::Value::get_colour() const
{
wxColour c1, c2;

if(colour1.is_palette_colour())
{
c1 = (*active_palette)[colour1.palette_colour()];
}
else if(colour1.is_highlight_colour())
{
HighlightColourMap highlight_colours = wxGetApp().settings->get_highlight_colours();
c2 = highlight_colours[colour1.highlight_colour()].primary_colour;
}
else{
abort(); /* Unreachable. */
}

if(colour_delta_steps == 0)
{
return c1;
}

if(colour2.is_palette_colour())
{
c2 = (*active_palette)[colour2.palette_colour()];
}
else if(colour2.is_highlight_colour())
{
HighlightColourMap highlight_colours = wxGetApp().settings->get_highlight_colours();
c2 = highlight_colours[colour2.highlight_colour()].primary_colour;
}
else{
abort(); /* Unreachable. */
}

float alpha = (float)(colour_delta_pos) / (float)(colour_delta_steps);

unsigned char r = wxColour::AlphaBlend(c2.Red(), c1.Red(), alpha);
unsigned char g = wxColour::AlphaBlend(c2.Green(), c1.Green(), alpha);
unsigned char b = wxColour::AlphaBlend(c2.Blue(), c1.Blue(), alpha);

return wxColour(r, g, b);
}

std::string REHex::ByteColourMap::serialise_colour_index(Colour index)
{
char buf[64];

if(index.is_palette_colour() && index.palette_colour() == Palette::PAL_NORMAL_TEXT_FG)
{
strcpy(buf, "PAL_NORMAL_TEXT_FG");
}
else if(index.is_highlight_colour())
{
snprintf(buf, sizeof(buf), "highlight.%zu.primary", index.highlight_colour());
}
else{
throw std::invalid_argument("Unexpected index in REHex::ByteColourMap::serialise_colour_index()");
}

return buf;
}

REHex::ByteColourMap::Colour REHex::ByteColourMap::deserialise_colour_index(const std::string &string)
{
int index;

if(string == "PAL_NORMAL_TEXT_FG")
{
return Colour::PaletteColour(Palette::PAL_NORMAL_TEXT_FG);
}
else if(sscanf(string.c_str(), "highlight.%d.primary", &index) == 1 && index >= 0 && index < (int)(HighlightColourMap::MAX_NUM))
{
return Colour::HighlightColour(index);
}

throw std::invalid_argument("Invalid string passed to REHex::ByteColourMap::deserialise_colour_index()");
}

REHex::ByteColourMap REHex::ByteColourMap::load(const wxConfigBase *config)
{
ByteColourMap map;

map.label = config->Read("label", map.label);

for(int i = 0; i < 256; ++i)
{
char path[8];
snprintf(path, sizeof(path), "%d/", i);

if(!config->HasGroup(path))
{
continue;
}

wxConfigPathChanger scoped_path(config, path);

map.bytes[i].colour1 = deserialise_colour_index(config->Read("colour1", wxEmptyString).ToStdString());
map.bytes[i].colour2 = deserialise_colour_index(config->Read("colour2", wxEmptyString).ToStdString());

map.bytes[i].colour_delta_steps = config->Read("colour_delta_steps", (long)(-1));
map.bytes[i].colour_delta_pos = config->Read("colour_delta_pos", (long)(-1));

if(map.bytes[i].colour_delta_steps < 0
|| map.bytes[i].colour_delta_pos < 0
|| map.bytes[i].colour_delta_pos > map.bytes[i].colour_delta_steps)
{
throw std::invalid_argument("invalid colour_delta_steps/colour_delta_pos values");
}
}

return map;
}

void REHex::ByteColourMap::save(wxConfigBase *config)
{
config->Write("label", label);

for(int i = 0; i < 256; ++i)
{
if(bytes[i] == Value())
{
/* Skip serialising default colours. */
continue;
}

char path[8];
snprintf(path, sizeof(path), "%d/", i);
wxConfigPathChanger scoped_path(config, path);

std::string colour1 = serialise_colour_index(bytes[i].colour1);
std::string colour2 = serialise_colour_index(bytes[i].colour2);

config->Write("colour1", wxString(colour1));
config->Write("colour2", wxString(colour2));

config->Write("colour_delta_steps", (long)(bytes[i].colour_delta_steps));
config->Write("colour_delta_pos", (long)(bytes[i].colour_delta_pos));
}
}
162 changes: 162 additions & 0 deletions src/ByteColourMap.hpp
@@ -0,0 +1,162 @@
/* Reverse Engineer's Hex Editor
* Copyright (C) 2024 Daniel Collins <solemnwarning@solemnwarning.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#ifndef REHEX_BYTECOLOURMAP_HPP
#define REHEX_BYTECOLOURMAP_HPP

#include <wx/colour.h>
#include <wx/config.h>

#include "Palette.hpp"
#include "HighlightColourMap.hpp"

namespace REHex
{
class ByteColourMap
{
public:
/**
* @brief A colour from the active palette or application highlight map.
*/
class Colour
{
private:
static constexpr int PALETTE_COLOUR_BASE = 0;
static constexpr int PALETTE_COLOUR_MAX = Palette::PAL_MAX;

static constexpr int HIGHLIGHT_COLOUR_BASE = PALETTE_COLOUR_MAX + 1;
static constexpr int HIGHLIGHT_COLOUR_MAX = HIGHLIGHT_COLOUR_BASE + HighlightColourMap::MAX_NUM - 1;

int value;

Colour(int value): value(value) {}

public:
/**
* @brief Create a Colour for a Palette colour slot.
*/
static Colour PaletteColour(Palette::ColourIndex colour_idx)
{
return Colour(PALETTE_COLOUR_BASE + colour_idx);
}

/**
* @brief Check if this Colour is a Palette colour.
*/
bool is_palette_colour() const
{
return value >= HIGHLIGHT_COLOUR_BASE && value <= HIGHLIGHT_COLOUR_MAX;
}

/**
* @brief Get the Palette colour index from this Colour.
*
* NOTE: Ensure is_palette_colour() is true before calling
* this method.
*/
Palette::ColourIndex palette_colour() const
{
assert(is_palette_colour());
return (Palette::ColourIndex)(value - PALETTE_COLOUR_BASE);
}

/**
* @brief Create a colour for a highlight primary colour.
*/
static Colour HighlightColour(size_t highlight_idx)
{
return Colour(HIGHLIGHT_COLOUR_BASE + highlight_idx);
}

/**
* @brief Check if this Colour is a highlight (primary) colour.
*/
bool is_highlight_colour() const
{
return value >= HIGHLIGHT_COLOUR_BASE && value <= HIGHLIGHT_COLOUR_MAX;
}

/**
* @brief Get the (primary) highlight colour index from this Colour.
*
* NOTE: Ensure is_highlight_colour() is true before
* calling this method.
*/
size_t highlight_colour() const
{
assert(is_highlight_colour());
return value - HIGHLIGHT_COLOUR_BASE;
}

bool operator==(const Colour &rhs) const
{
return value == rhs.value;
}
};

struct Value
{
Colour colour1;
Colour colour2;

int colour_delta_steps;
int colour_delta_pos;

Value():
colour1(Colour::PaletteColour(Palette::PAL_NORMAL_TEXT_FG)),
colour2(Colour::PaletteColour(Palette::PAL_NORMAL_TEXT_FG)),
colour_delta_steps(0),
colour_delta_pos(0) {}

bool operator==(const Value &rhs) const
{
return colour1 == rhs.colour1
&& colour2 == rhs.colour2
&& colour_delta_steps == rhs.colour_delta_steps
&& colour_delta_pos == rhs.colour_delta_pos;
}

bool is_single() const;
bool is_start() const;
bool is_end() const;

wxColour get_colour() const;
};

private:
wxString label;
Value bytes[256];

static std::string serialise_colour_index(Colour index);
static Colour deserialise_colour_index(const std::string &string);

public:
const wxString &get_label() const;
void set_label(const wxString &label);

void set_colour(unsigned char byte, Colour colour1, Colour colour2, int colour_delta_steps, int colour_delta_pos);

wxColour get_colour(unsigned char byte) const;

const Value &get_value(unsigned char byte) const;

static ByteColourMap load(const wxConfigBase *config);
void save(wxConfigBase *config);
};
}

#endif /* !REHEX_BYTECOLOURMAP_HPP */

0 comments on commit 44f06e9

Please sign in to comment.