11#include " Pawn.h"
22#include < stdio.h>
3+ #include < charconv>
34
45using namespace Pinetime ::Applications::Screens;
56
@@ -81,7 +82,7 @@ static cell AMX_NATIVE_CALL F_lv_label_set_text(AMX* amx, const cell* params) {
8182 if (text != NULL )
8283 lv_label_set_text (label, text);
8384 else
84- lv_label_set_text (label, " <invalid>" );
85+ lv_label_set_text_static (label, " <invalid>" );
8586
8687 return 0 ;
8788}
@@ -151,66 +152,154 @@ static cell AMX_NATIVE_CALL F_lv_obj_align(AMX* amx, const cell* params) {
151152 return 0 ;
152153}
153154
155+ static cell AMX_NATIVE_CALL F_lv_obj_realign (AMX* amx, const cell* params) {
156+ ASSERT_PARAMS (1 )
157+ lv_obj_realign (PARAMS_OBJ (1 ));
158+ return 0 ;
159+ }
160+
161+ /* *
162+ * Hand-written implementation of sprintf with limited functionality in order to support reading strings from parameters.
163+ * Supported interpolations:
164+ * %%
165+ * %d and %x, including padding flags
166+ * %s with packed strings
167+ */
154168static cell AMX_NATIVE_CALL F_sprintf (AMX* amx, const cell* params) {
155- // param[0] is the number of total parameter bytes, divide it by cell size and subtract 3 to account for the fixed parameters
156- int args_count = params[0 ] / sizeof (cell) - 3 ;
169+ // param[0] is the number of total parameter bytes, divide it by cell size to get the parameter count
170+ int args_count = params[0 ] / sizeof (cell);
171+ if (args_count < 4 ) {
172+ amx_RaiseError (amx, AMX_ERR_PARAMCOUNT);
173+ return 0 ;
174+ }
157175
158176 cell* output = amx_Address (amx, params[1 ]);
159- cell output_size = params[2 ] * sizeof (cell); // We assume the output array is packed, TODO: add a separate sprintf_unpacked function?
177+ cell max_size = params[2 ] * sizeof (cell); // We assume the output array is packed so each cell contains one character per byte
178+ // TODO: add a separate sprintf_unpacked function?
160179
161- char buf[output_size];
180+ char buf[max_size];
181+ char * bufc = buf;
182+ char * bufmax = buf + max_size - 1 ;
162183
163184 char * fmt;
164185 amx_StrParam_Type (amx, params[3 ], fmt, char *);
165186 if (fmt == NULL )
166187 return 0 ;
167188
168- cell ret = 0 ;
169-
170- #pragma GCC diagnostic ignored "-Wformat-nonliteral"
171- switch (args_count) {
172- case 0 :
173- strcpy (buf, fmt);
174- ret = strlen (fmt) + 1 ;
175- break ;
176- case 1 :
177- ret = snprintf (buf, output_size, fmt, *amx_Address (amx, params[4 ]));
178- break ;
179- case 2 :
180- ret = snprintf (buf, output_size, fmt, *amx_Address (amx, params[4 ]), *amx_Address (amx, params[5 ]));
181- break ;
182- case 3 :
183- ret = snprintf (buf, output_size, fmt, *amx_Address (amx, params[4 ]), *amx_Address (amx, params[5 ]), *amx_Address (amx, params[6 ]));
184- break ;
185- case 4 :
186- ret = snprintf (buf,
187- output_size,
188- fmt,
189- *amx_Address (amx, params[4 ]),
190- *amx_Address (amx, params[5 ]),
191- *amx_Address (amx, params[6 ]),
192- *amx_Address (amx, params[7 ]));
193- break ;
194- case 5 :
195- ret = snprintf (buf,
196- output_size,
197- fmt,
198- *amx_Address (amx, params[4 ]),
199- *amx_Address (amx, params[5 ]),
200- *amx_Address (amx, params[6 ]),
201- *amx_Address (amx, params[7 ]),
202- *amx_Address (amx, params[8 ]));
203- break ;
204- default :
205- return 0 ;
189+ size_t paramc = 4 ;
190+
191+ size_t fmt_len = strlen (fmt);
192+ bool in_pc = false ;
193+
194+ char flags[4 ];
195+ size_t flagc = 0 ;
196+
197+ for (size_t i = 0 ; i < fmt_len; i++) {
198+ char c = fmt[i];
199+
200+ if (c == ' %' ) {
201+ if (in_pc) {
202+ *bufc++ = ' %' ;
203+ in_pc = false ;
204+ } else {
205+ flagc = 0 ;
206+ in_pc = true ;
207+ }
208+ } else if (in_pc) {
209+ switch (c) {
210+ case ' x' :
211+ case ' d' : {
212+ int padding = 0 ;
213+ char pad = ' ' ;
214+
215+ if (flagc == 1 ) {
216+ padding = flags[0 ] - ' 0' ;
217+ } else if (flagc == 2 ) {
218+ pad = flags[0 ];
219+ padding = flags[1 ] - ' 0' ;
220+ }
221+
222+ std::to_chars_result result = std::to_chars (bufc, bufmax, (int ) *amx_Address (amx, params[paramc++]), c == ' x' ? 16 : 10 );
223+
224+ int padlen = padding - (result.ptr - bufc);
225+ for (int n = 0 ; n < padlen && bufc < bufmax; n++) {
226+ bufc[1 ] = bufc[0 ];
227+ bufc[0 ] = pad;
228+ bufc++;
229+ }
230+
231+ bufc = result.ptr + (padlen > 0 ? padlen : 0 );
232+ in_pc = false ;
233+ break ;
234+ }
235+
236+ case ' s' : {
237+ cell param = params[paramc++];
238+ int len;
239+ amx_StrLen (amx_Address (amx, param), &len);
240+
241+ if (len > 0 && bufc + len < bufmax - 1 ) {
242+ amx_GetString (bufc, amx_Address (amx, param), 0 , len + 1 );
243+ bufc += len;
244+ }
245+ in_pc = false ;
246+ break ;
247+ }
248+
249+ default :
250+ if (flagc < sizeof (flags))
251+ flags[flagc++] = c;
252+ break ;
253+ }
254+ } else {
255+ *bufc++ = c;
256+ }
206257 }
207- # pragma GCC diagnostic warning "-Wformat-nonliteral"
258+ *bufc = 0 ;
208259
209- amx_SetString (output, buf, 1 , 0 , output_size );
260+ amx_SetString (output, buf, 1 , 0 , max_size );
210261
211- return ret ;
262+ return bufc - buf ;
212263}
213264
265+ static cell AMX_NATIVE_CALL F_get_datetime (AMX* amx, const cell* params) {
266+ ASSERT_PARAMS (1 );
267+
268+ Pawn* pawn = (Pawn*) amx->userdata [0 ];
269+
270+ pawn->currentDateTime = std::chrono::time_point_cast<std::chrono::minutes>(pawn->dateTimeController .CurrentDateTime ());
271+
272+ cell* ret = amx_Address (amx, params[1 ]);
273+
274+ ret[0 ] = pawn->currentDateTime .IsUpdated ();
275+ ret[1 ] = pawn->dateTimeController .Seconds ();
276+ ret[2 ] = pawn->dateTimeController .Minutes ();
277+ ret[3 ] = pawn->dateTimeController .Hours ();
278+ ret[4 ] = pawn->dateTimeController .Day ();
279+ ret[5 ] = pawn->dateTimeController .Year ();
280+
281+ return 0 ;
282+ }
283+
284+ static cell AMX_NATIVE_CALL F_get_datetime_short_str (AMX* amx, const cell* params) {
285+ ASSERT_PARAMS (2 );
286+
287+ Pawn* pawn = (Pawn*) amx->userdata [0 ];
288+
289+ cell* ret_day = amx_Address (amx, params[1 ]);
290+ cell* ret_month = amx_Address (amx, params[2 ]);
291+
292+ if (ret_day != NULL ) {
293+ const char * day = pawn->dateTimeController .DayOfWeekShortToString ();
294+ amx_SetString (ret_day, day, true , false , 4 );
295+ }
296+ if (ret_month != NULL ) {
297+ const char * month = pawn->dateTimeController .MonthShortToString ();
298+ amx_SetString (ret_month, month, true , false , 4 );
299+ }
300+
301+ return 0 ;
302+ }
214303
215304static int load_program (AMX* amx, const uint8_t * data) {
216305 AMX_HEADER hdr;
@@ -236,9 +325,9 @@ static int load_program(AMX* amx, const uint8_t* data) {
236325 return result;
237326}
238327
239- Pawn::Pawn (Controllers::DateTime& dateTimeController) : dateTimeController(dateTimeController) {
240328#include " program.h"
241329
330+ Pawn::Pawn (Controllers::DateTime& dateTimeController) : dateTimeController(dateTimeController) {
242331 load_program (&amx, program);
243332 (void ) program_len;
244333
@@ -252,6 +341,8 @@ Pawn::Pawn(Controllers::DateTime& dateTimeController) : dateTimeController(dateT
252341
253342 const AMX_NATIVE_INFO natives[] = {
254343 {" sprintf" , F_sprintf},
344+ {" get_datetime" , F_get_datetime},
345+ {" get_datetime_short_str" , F_get_datetime_short_str},
255346 {" lv_scr_act" , F_lv_scr_act},
256347 {" lv_label_create" , F_lv_label_create},
257348 {" lv_btn_create" , F_lv_btn_create},
@@ -264,6 +355,7 @@ Pawn::Pawn(Controllers::DateTime& dateTimeController) : dateTimeController(dateT
264355 {" lv_obj_set_style_local_opa" , F_lv_obj_set_style_local_opa},
265356 {" lv_obj_set_style_local_ptr" , F_lv_obj_set_style_local_ptr},
266357 {" lv_obj_align" , F_lv_obj_align},
358+ {" lv_obj_realign" , F_lv_obj_realign},
267359 {0 , 0 } /* terminator */
268360 };
269361 amx_Register (&amx, natives, -1 );
0 commit comments