rpgm_common_types/
table.rs1use ruby_marshal::FromValue;
2use ruby_marshal::FromValueContext;
3use ruby_marshal::FromValueError;
4use ruby_marshal::IntoValue;
5use ruby_marshal::IntoValueError;
6use ruby_marshal::SymbolValue;
7use ruby_marshal::UserDefinedValue;
8use ruby_marshal::Value;
9use ruby_marshal::ValueArena;
10use ruby_marshal::ValueHandle;
11
12#[derive(Debug)]
13pub enum TableFromValueError {
14 TooShort { len: usize },
15 OddSizedPayload { len: usize },
16 ItemSizeMismatch { expected: i32, actual: usize },
17}
18
19impl std::fmt::Display for TableFromValueError {
20 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
21 match self {
22 Self::TooShort { len } => {
23 write!(f, "the table payload (len {len}) is too short")
24 }
25 Self::OddSizedPayload { len } => {
26 write!(f, "the table payload (len {len}) is not a multiple of 2")
27 }
28 Self::ItemSizeMismatch { expected, actual } => {
29 write!(f, "the item array length is mismatched, expected {expected} bytes but got {actual}")
30 }
31 }
32 }
33}
34
35impl std::error::Error for TableFromValueError {}
36
37#[derive(Debug)]
38pub enum TableIntoValueError {
39 TooManyItems {
40 len: usize,
41 error: std::num::TryFromIntError,
42 },
43}
44
45impl std::fmt::Display for TableIntoValueError {
46 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
47 match self {
48 Self::TooManyItems { len, .. } => {
49 write!(f, "there are too many table items in table of len {len}")
50 }
51 }
52 }
53}
54
55impl std::error::Error for TableIntoValueError {
56 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
57 match self {
58 Self::TooManyItems { error, .. } => Some(error),
59 }
61 }
62}
63
64const HEADER_SIZE: usize = 4 * 5;
65
66const USER_DEFINED_NAME: &[u8] = b"Table";
67
68#[derive(Debug, serde::Deserialize, serde::Serialize)]
69pub struct Table {
70 pub dimensions: i32,
71 pub x_size: i32,
72 pub y_size: i32,
73 pub z_size: i32,
74 pub items: Vec<i16>,
75}
76
77impl<'a> FromValue<'a> for Table {
78 fn from_value(ctx: &FromValueContext<'a>, value: &Value) -> Result<Self, FromValueError> {
79 let user_defined: &UserDefinedValue = FromValue::from_value(ctx, value)?;
80 let name = user_defined.name();
81 let name: &SymbolValue = ctx.from_value(name.into())?;
82 let name = name.value();
83
84 if name != USER_DEFINED_NAME {
85 return Err(FromValueError::UnexpectedObjectName { name: name.into() });
86 }
87
88 let value = user_defined.value();
89
90 let value_len = value.len();
91 if value_len < HEADER_SIZE {
92 return Err(FromValueError::new_other(TableFromValueError::TooShort {
93 len: value_len,
94 }));
95 }
96 if value_len % 2 != 0 {
97 return Err(FromValueError::new_other(
98 TableFromValueError::OddSizedPayload { len: value_len },
99 ));
100 }
101
102 let (dimensions, value) = value.split_at(4);
103 let dimensions = i32::from_le_bytes(dimensions.try_into().unwrap());
104
105 let (x_size, value) = value.split_at(4);
106 let x_size = i32::from_le_bytes(x_size.try_into().unwrap());
107
108 let (y_size, value) = value.split_at(4);
109 let y_size = i32::from_le_bytes(y_size.try_into().unwrap());
110
111 let (z_size, value) = value.split_at(4);
112 let z_size = i32::from_le_bytes(z_size.try_into().unwrap());
113
114 let (size, value) = value.split_at(4);
115 let size = i32::from_le_bytes(size.try_into().unwrap());
116
117 let value_len = value.len();
118 if i32::try_from(value_len)
119 .ok()
120 .is_none_or(|value_len| value_len != 2 * size)
121 {
122 return Err(FromValueError::new_other(
123 TableFromValueError::ItemSizeMismatch {
124 expected: 2 * size,
125 actual: value_len,
126 },
127 ));
128 }
129
130 let items_len = usize::try_from(size).unwrap();
131 let mut items = Vec::with_capacity(items_len);
132 for chunk in value.chunks(2) {
133 items.push(i16::from_le_bytes(chunk.try_into().unwrap()));
134 }
135
136 Ok(Self {
137 dimensions,
138 x_size,
139 y_size,
140 z_size,
141 items,
142 })
143 }
144}
145
146impl IntoValue for Table {
147 fn into_value(self, arena: &mut ValueArena) -> Result<ValueHandle, IntoValueError> {
148 let name = arena.create_symbol(USER_DEFINED_NAME.into());
149 let mut value = Vec::with_capacity(HEADER_SIZE + self.items.len());
150
151 value.extend(self.dimensions.to_le_bytes());
152 value.extend(self.x_size.to_le_bytes());
153 value.extend(self.y_size.to_le_bytes());
154 value.extend(self.z_size.to_le_bytes());
155
156 let items_len = self.items.len();
157 let size = i32::try_from(items_len).map_err(|error| {
158 IntoValueError::new_other(TableIntoValueError::TooManyItems {
159 len: items_len,
160 error,
161 })
162 })?;
163 value.extend(size.to_le_bytes());
164
165 for item in self.items.iter() {
166 value.extend(item.to_le_bytes());
167 }
168
169 let user_defined = arena.create_user_defined(name, value);
170 Ok(user_defined.into())
171 }
172}