//! logic processors and stuff
use std::borrow::Cow;
use std::string::FromUtf8Error;
use image::{Rgb, RgbImage};
use crate::block::simple::*;
use crate::data::dynamic::{DynSerializer, DynType};
use crate::{block::*, Serializer};
use crate::data::{self, CompressError, DataRead, DataWrite};
make_simple!(LogicBlock);
make_simple!(
MemoryBlock,
|_, _, _, _, _, _| None,
|_, _, _, buff: &mut DataRead| {
// format:
// - iterate [`u32`]
// - memory: [`f64`]
let n = buff.read_u32()? as usize;
buff.skip(n * 8)
}
);
make_register! {
"reinforced-message" => MessageLogic::new(1, true, cost!(Graphite: 10, Beryllium: 5));
"message" => MessageLogic::new(1, true, cost!(Copper: 5, Graphite: 5));
"switch" => SwitchLogic::new(1, true, cost!(Copper: 5, Graphite: 5));
"micro-processor" => ProcessorLogic::new(1, true, cost!(Copper: 90, Lead: 50, Silicon: 50));
"logic-processor" => ProcessorLogic::new(2, true, cost!(Lead: 320, Graphite: 60, Thorium: 50, Silicon: 80));
"hyper-processor" => ProcessorLogic::new(3, true, cost!(Lead: 450, Thorium: 75, Silicon: 150, SurgeAlloy: 50));
"memory-cell" => MemoryBlock::new(1, true, cost!(Copper: 30, Graphite: 30, Silicon: 30));
"memory-bank" => MemoryBlock::new(2, true, cost!(Copper: 30, Graphite: 80, Silicon: 80, PhaseFabric: 30));
"logic-display" => LogicBlock::new(3, true, cost!(Lead: 100, Metaglass: 50, Silicon: 50));
"large-logic-display" => LogicBlock::new(6, true, cost!(Lead: 200, Metaglass: 100, Silicon: 150, PhaseFabric: 75));
"canvas" => CanvasBlock::new(2, true, cost!(Silicon: 30, Beryllium: 10), 12);
// editor only
"world-processor" => LogicBlock::new(1, true, &[]);
"world-message" => MessageLogic::new(1, true, &[]);
"world-cell" => MemoryBlock::new(1, true, &[]);
}
pub struct CanvasBlock {
size: u8,
symmetric: bool,
build_cost: BuildCost,
canvas_size: u8,
}
macro_rules! h {
($x:literal) => {
Rgb(color_hex::color_from_hex!($x))
};
}
const PALETTE: &[Rgb<u8>; 8] = &[
h!("#362944"),
h!("#c45d9f"),
h!("#e39aac"),
h!("#f0dab1"),
h!("#6461c2"),
h!("#2ba9b4"),
h!("#93d4b5"),
h!("#f0f6e8"),
];
impl CanvasBlock {
#[must_use]
pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost, canvas_size: u8) -> Self {
assert!(size != 0, "invalid size");
assert!(canvas_size != 0, "invalid size");
Self {
canvas_size,
size,
symmetric,
build_cost,
}
}
state_impl!(pub RgbImage);
}
fn deser_canvas_image(b: Vec<u8>, size: usize) -> RgbImage {
let mut p = RgbImage::new(size as u32, size as u32);
for i in 0..(size * size) {
let offset = i * 3;
let mut n = 0;
for i in 0..3 {
let word = (i + offset) >> 3;
n |= (((b[word] & (1 << ((i + offset) & 7))) != 0) as u8) << i;
}
p.put_pixel(
i as u32 % size as u32,
i as u32 / size as u32,
PALETTE[n as usize],
)
}
p
}
impl BlockLogic for CanvasBlock {
impl_block!();
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
Ok(DynData::Empty)
}
fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
match data {
DynData::ByteArray(b) => Ok(Some(Self::create_state(deser_canvas_image(
b,
self.canvas_size as usize,
)))),
_ => Err(DeserializeError::InvalidType {
have: data.get_type(),
expect: DynType::String,
}),
}
}
fn clone_state(&self, state: &State) -> State {
Box::new(Self::get_state(state).clone())
}
fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
let mut o = vec![0; self.canvas_size as usize * self.canvas_size as usize * 3];
let p = Self::get_state(state);
for i in 0..(self.canvas_size * self.canvas_size) as usize {
let color = p.get_pixel(
i as u32 % self.canvas_size as u32,
i as u32 / self.canvas_size as u32,
);
let index = PALETTE.iter().position(|v| v == color).unwrap();
let offset = i * 3;
for i in 0..3 {
let word = (i + offset) >> 3;
if index >> i & 1 == 0 {
o[word] &= !(1 << ((i + offset) & 7));
} else {
o[word] |= 1 << ((i + offset) & 7);
}
}
}
Ok(DynData::ByteArray(o))
}
/// i thought about drawing the borders and stuff but it felt like too much work
fn draw(
&self,
c: &str,
n: &str,
state: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
) -> Option<ImageHolder> {
if let Some(state) = state {
let state = self.clone_state(state);
let p = state.downcast::<RgbImage>().unwrap();
// SAFETY: canvas_size cannot be 0, so width & height musnt be 0, and size cannot be 0
let p = unsafe {
DynamicImage::from(
RgbImage::from_raw(
self.canvas_size as u32,
self.canvas_size as u32,
p.into_raw(),
)
.unwrap(),
)
.into_rgba8()
.scale((self.size as u32 * 32) - 14)
};
let mut borders = load(c, n).unwrap().to_owned();
borders.overlay_at(&p, 7, 7);
return Some(ImageHolder::from(borders));
}
Some(ImageHolder::from(RgbaImage::new(
self.size as u32 * 32,
self.size as u32 * 32,
)))
}
/// format:
/// - len: [`i32`]
/// - read(len) -> [`deser_canvas_image`]
fn read(
&self,
build: &mut Build,
_: &BlockRegistry,
_: &EntityMapping,
buff: &mut DataRead,
) -> Result<(), DataReadError> {
let n = buff.read_i32()? as usize;
let mut b = vec![0; n];
buff.read_bytes(&mut b)?;
build.state = Some(Self::create_state(deser_canvas_image(
b,
self.canvas_size as usize,
)));
Ok(())
}
}
pub struct MessageLogic {
size: u8,
symmetric: bool,
build_cost: BuildCost,
}
impl MessageLogic {
#[must_use]
pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost) -> Self {
assert!(size != 0, "invalid size");
Self {
size,
symmetric,
build_cost,
}
}
state_impl!(pub String);
}
impl BlockLogic for MessageLogic {
impl_block!();
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
Ok(DynData::Empty)
}
fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
match data {
DynData::Empty | DynData::String(None) => Ok(Some(Self::create_state(String::new()))),
DynData::String(Some(s)) => Ok(Some(Self::create_state(s))),
_ => Err(DeserializeError::InvalidType {
have: data.get_type(),
expect: DynType::String,
}),
}
}
fn clone_state(&self, state: &State) -> State {
Box::new(Self::get_state(state).clone())
}
fn mirror_state(&self, _: &mut State, _: bool, _: bool) {}
fn rotate_state(&self, _: &mut State, _: bool) {}
fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
Ok(DynData::String(Some(Self::get_state(state).clone())))
}
fn read(
&self,
b: &mut Build,
_: &BlockRegistry,
_: &EntityMapping,
buff: &mut DataRead,
) -> Result<(), DataReadError> {
b.state = Some(Self::create_state(buff.read_utf()?.to_string()));
Ok(())
}
}
pub struct SwitchLogic {
size: u8,
symmetric: bool,
build_cost: BuildCost,
}
impl SwitchLogic {
#[must_use]
pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost) -> Self {
assert!(size != 0, "invalid size");
Self {
size,
symmetric,
build_cost,
}
}
state_impl!(pub bool);
}
impl BlockLogic for SwitchLogic {
impl_block!();
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
Ok(DynData::Empty)
}
fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
match data {
DynData::Empty => Ok(Some(Self::create_state(true))),
DynData::Boolean(enabled) => Ok(Some(Self::create_state(enabled))),
_ => Err(DeserializeError::InvalidType {
have: data.get_type(),
expect: DynType::Boolean,
}),
}
}
fn clone_state(&self, state: &State) -> State {
Box::new(*Self::get_state(state))
}
fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
Ok(DynData::Boolean(*Self::get_state(state)))
}
fn read(
&self,
build: &mut Build,
_: &BlockRegistry,
_: &EntityMapping,
buff: &mut DataRead,
) -> Result<(), DataReadError> {
build.state = Some(Self::create_state(buff.read_bool()?));
Ok(())
}
fn draw(
&self,
_: &str,
_: &str,
state: Option<&State>,
_: Option<&RenderingContext>,
_: Rotation,
) -> Option<ImageHolder> {
let base = load("logic", "switch").unwrap();
if let Some(state) = state {
if *Self::get_state(state) {
let mut base = base.clone();
let on = load("logic", "switch-on").unwrap();
base.overlay(&on);
return Some(ImageHolder::from(base));
}
}
Some(ImageHolder::from(base))
}
}
pub struct ProcessorLogic {
size: u8,
symmetric: bool,
build_cost: BuildCost,
}
impl ProcessorLogic {
#[must_use]
pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost) -> Self {
assert!(size != 0, "invalid size");
Self {
size,
symmetric,
build_cost,
}
}
state_impl!(pub ProcessorState);
}
fn read_decompressed(buff: &mut DataRead) -> Result<ProcessorState, ProcessorDeserializeError> {
let ver = buff.read_u8()?;
if ver != 1 {
return Err(ProcessorDeserializeError::Version(ver));
}
let code_len = buff.read_u32()? as usize;
if !(0..=500 * 1024).contains(&code_len) {
return Err(ProcessorDeserializeError::CodeLength(code_len));
}
let mut code = vec![];
code.resize(code_len, 0);
buff.read_bytes(&mut code)?;
let code = String::from_utf8(code)?;
let link_cnt = buff.read_u32()? as usize;
let mut links = vec![];
links.reserve(link_cnt);
for _ in 0..link_cnt {
let name = buff.read_utf()?;
let x = buff.read_i16()?;
let y = buff.read_i16()?;
links.push(ProcessorLink {
name: String::from(name),
x,
y,
});
}
Ok(ProcessorState { code, links })
}
impl BlockLogic for ProcessorLogic {
impl_block!();
fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
Ok(DynData::Empty)
}
fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
match data {
DynData::Empty => Ok(Some(Self::create_state(ProcessorState::default()))),
DynData::ByteArray(arr) => {
let input = arr.as_ref();
let buff = DataRead::new(input).deflate()?;
Ok(Some(Self::create_state(
ProcessorDeserializeError::forward(read_decompressed(&mut DataRead::new(
&buff,
)))?,
)))
}
_ => Err(DeserializeError::InvalidType {
have: data.get_type(),
expect: DynType::Boolean,
}),
}
}
fn read(
&self,
b: &mut Build,
_: &BlockRegistry,
_: &EntityMapping,
buff: &mut DataRead,
) -> Result<(), DataReadError> {
let n = buff.read_u32()? as usize;
let mut v = vec![0; n];
buff.read_bytes(&mut v)?;
v = DataRead::new(&v).deflate().unwrap();
b.state = Some(Self::create_state(
read_decompressed(&mut DataRead::new(&v)).unwrap(),
));
for _ in 0..buff.read_u32()? {
let _ = buff.read_utf()?;
let _ = DynSerializer.deserialize(buff).unwrap();
}
let memory = buff.read_u32()? as usize;
buff.skip(memory * 8)?;
Ok(())
}
fn clone_state(&self, state: &State) -> State {
Box::new(Self::get_state(state).clone())
}
fn mirror_state(&self, state: &mut State, horizontally: bool, vertically: bool) {
for link in &mut Self::get_state_mut(state).links {
if horizontally {
link.x = -link.x;
}
if vertically {
link.y = -link.y;
}
}
}
fn rotate_state(&self, state: &mut State, clockwise: bool) {
for link in &mut Self::get_state_mut(state).links {
let (cdx, cdy) = link.get_pos();
link.x = if clockwise { cdy } else { -cdy };
link.y = if clockwise { -cdx } else { cdx };
}
}
fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
let state = Self::get_state(state);
let mut rbuff = DataWrite::default();
ProcessorSerializeError::forward(rbuff.write_u8(1))?;
assert!(state.code.len() < 500 * 1024);
ProcessorSerializeError::forward(rbuff.write_i32(state.code.len() as i32))?;
ProcessorSerializeError::forward(rbuff.write_bytes(state.code.as_bytes()))?;
assert!(state.links.len() < i32::MAX as usize);
ProcessorSerializeError::forward(rbuff.write_i32(state.links.len() as i32))?;
for link in &state.links {
ProcessorSerializeError::forward(rbuff.write_utf(&link.name))?;
ProcessorSerializeError::forward(rbuff.write_i16(link.x))?;
ProcessorSerializeError::forward(rbuff.write_i16(link.y))?;
}
let mut out = DataWrite::default();
rbuff.inflate(&mut out)?;
Ok(DynData::ByteArray(out.consume()))
}
}
#[derive(Debug, thiserror::Error)]
pub enum ProcessorDeserializeError {
#[error("failed to read state data")]
Read(#[from] data::ReadError),
#[error("malformed utf-8 in processor code")]
FromUtf8(#[from] FromUtf8Error),
#[error("unsupported version ({0})")]
Version(u8),
#[error("invalid code length ({0})")]
CodeLength(usize),
}
impl ProcessorDeserializeError {
pub fn forward<T, E: Into<Self>>(result: Result<T, E>) -> Result<T, DeserializeError> {
match result {
Ok(v) => Ok(v),
Err(e) => Err(DeserializeError::Custom(Box::new(e.into()))),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum ProcessorSerializeError {
#[error("failed to write state data")]
Write(#[from] data::WriteError),
#[error(transparent)]
Compress(#[from] CompressError),
}
impl ProcessorSerializeError {
pub fn forward<T, E: Into<Self>>(result: Result<T, E>) -> Result<T, SerializeError> {
match result {
Ok(v) => Ok(v),
Err(e) => Err(SerializeError::Custom(Box::new(e.into()))),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct ProcessorLink {
name: String,
x: i16,
y: i16,
}
impl ProcessorLink {
#[must_use]
pub fn new(name: Cow<'_, str>, x: i16, y: i16) -> Self {
assert!(
u16::try_from(name.len()).is_ok(),
"name too long ({})",
name.len()
);
Self {
name: name.into_owned(),
x,
y,
}
}
#[must_use]
pub fn get_name(&self) -> &str {
&self.name
}
#[must_use]
pub fn get_pos(&self) -> (i16, i16) {
(self.x, self.y)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct ProcessorState {
code: String,
links: Vec<ProcessorLink>,
}
impl ProcessorState {
#[must_use]
pub fn get_code(&self) -> &str {
&self.code
}
pub fn set_code(&mut self, code: Cow<'_, str>) -> Result<(), CodeError> {
let as_str = &code as &str;
if as_str.len() > 500 * 1024 {
return Err(CodeError::TooLong(as_str.len()));
}
match code {
Cow::Borrowed(s) => {
self.code.clear();
self.code.push_str(s);
}
Cow::Owned(s) => self.code = s,
}
Ok(())
}
#[must_use]
pub fn get_links(&self) -> &[ProcessorLink] {
&self.links
}
pub fn create_link(
&mut self,
mut name: String,
x: i16,
y: i16,
) -> Result<&ProcessorLink, CreateError> {
if name.len() > u16::MAX as usize {
return Err(CreateError::NameLength(name.len()));
}
for curr in &self.links {
if name == curr.name {
return Err(CreateError::DuplicateName(name));
}
if x == curr.x && y == curr.y {
name.clear();
name.push_str(&curr.name);
return Err(CreateError::DuplicatePos { name, x, y });
}
}
let idx = self.links.len();
self.links.push(ProcessorLink { name, x, y });
Ok(&self.links[idx])
}
pub fn add_link(&mut self, link: ProcessorLink) -> Result<&ProcessorLink, CreateError> {
self.create_link(link.name, link.x, link.y)
}
pub fn remove_link(&mut self, idx: usize) -> Option<ProcessorLink> {
if idx < self.links.len() {
Some(self.links.remove(idx))
} else {
None
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum CodeError {
#[error("code too long ({0} bytes)")]
TooLong(usize),
}
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum CreateError {
#[error("link name too long ({0} bytes)")]
NameLength(usize),
#[error("there is already a link named {0}")]
DuplicateName(String),
#[error("link {name} already points to ({x}, {y})")]
DuplicatePos { name: String, x: i16, y: i16 },
}