Changeset 3d27dc6 in Readingame
- Timestamp:
- Aug 18, 2023, 10:58:35 PM (10 months ago)
- Branches:
- main
- Children:
- 3d333fd
- Parents:
- bd5d630
- Files:
-
- 2 added
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
build.sh
rbd5d630 r3d27dc6 1 1 #!sh 2 2 3 g++ main.cpp -o main -lbe3 g++ -gdwarf-3 *.cpp -o main -lbe -
main.cpp
rbd5d630 r3d27dc6 5 5 */ 6 6 7 #include "HyperTextView.h" 8 7 9 #include <Application.h> 10 #include <Button.h> 8 11 #include <ByteOrder.h> 9 12 #include <Directory.h> 10 13 #include <Entry.h> 11 14 #include <File.h> 15 #include <GroupLayout.h> 16 #include <LayoutBuilder.h> 12 17 #include <String.h> 18 #include <TextEncoding.h> 19 #include <TextView.h> 13 20 #include <Window.h> 14 21 … … 151 158 } 152 159 153 int prettyPrintEscape(const std::string& message, int cursor) 154 { 155 if (message[cursor + 1] == 'o') { 156 printf("\n" BOLD "OK"); 157 int v = 0; 158 int i = 2; 159 while (isdigit(message[cursor + i])) { 160 v = v * 10 + message[cursor + i] - '0'; 161 i++; 162 } 163 if (i != 0) 164 printf("[Hook %x]", v); 165 printf(NORMAL "\n"); 166 return i; 167 } else if (message[cursor + 1] == '!') { 168 printf(BOLD "APPEND" NORMAL "\n"); 169 return 2; 170 } else { 171 // TODO unknown escape sequence 172 printf("%c", message[cursor]); 173 return 1; 174 } 175 } 176 177 int prettyPrintHyperlink(const std::string& message, int cursor) 178 { 179 int v = 0; 180 int i = 1; 181 while (isdigit(message[cursor + i])) { 182 v = v * 10 + message[cursor + i] - '0'; 183 i++; 184 } 185 186 printf(BOLD "[Hook %x]", v); 187 188 while (message[cursor + i] != ']') { 189 printf("%c", message[cursor + i]); 190 i++; 191 } 192 193 printf(NORMAL); 194 return i + 1; 195 } 196 197 void prettyPrint(const std::string& message) 198 { 199 size_t cursor = 0; 200 while (cursor < message.size()) { 201 switch(message[cursor]) { 202 case '\\': 203 cursor += prettyPrintEscape(message, cursor); 204 break; 205 case '[': 206 cursor += prettyPrintHyperlink(message, cursor); 207 break; 208 case '@': 209 case '$': 210 // @ Gender mark (print an e if the player identifies as female) 211 // $ Plural mark (print an s if the counter is plural) 212 printf(BOLD "%c" NORMAL, message[cursor]); 213 cursor++; 214 break; 215 default: 216 printf("%c", message[cursor]); 217 cursor++; 218 break; 219 } 220 } 221 printf("\n"); 222 } 160 const char* cp850_lookup[0x80] = { 161 "Ç", "ü", "é", "â", "ä", "à", "å", "ç", "ê", "ë", "è", "ï", "î", "ì", "Ä", "Å", "É", "æ", "Æ", 162 "ô", "ö", "ò", "û", "ù", "ÿ", "Ö", "Ü", "ø", "£", "Ø", "×", "ƒ", "á", "í", "ó", "ú", "ñ", "Ñ", 163 "ª", "º", "¿", "®", "¬", "½", "¼", "¡", "«", "»", "░", "▒", "▓", "│", "┤", "Á", "Â", "À", "©", 164 "╣", "║", "╗", "╝", "¢", "¥", "┐", "└", "┴", "┬", "├", "─", "┼", "ã", "Ã", "╚", "╔", "╩", "╦", 165 "╠", "═", "╬", "¤", "ð", "Ð", "Ê", "Ë", "È", "ı", "Í", "Î", "Ï", "┘", "┌", "█", "▄", "¦", "Ì", 166 "▀", "Ó", "ß", "Ô", "Ò", "õ", "Õ", "µ", "þ", "Þ", "Ú", "Û", "Ù", "ý", "Ý", "¯", "´", "\u00AD", 167 "±", "‗", "¾", "¶", "§", "÷", "¸", "°", "¨", "·", "¹", "³", "²", "■", "\u00A0" 168 }; 223 169 224 170 class Room { … … 229 175 230 176 std::vector<Room> gRooms; 177 std::vector<uint8_t> gVar8; 178 std::vector<uint16_t> gVar16; 179 180 uint8_t gCurrentRoom; 181 uint8_t gCurrentScreen; 182 uint8_t gTrigger; 231 183 232 184 // TODO objects … … 236 188 // TODO initial room 237 189 // TODO other things from the init script 190 191 class GameWindow: public BWindow 192 { 193 public: 194 GameWindow() 195 : BWindow(BRect(40, 40, 500, 400), "Readingame", B_DOCUMENT_WINDOW, 196 B_AUTO_UPDATE_SIZE_LIMITS | B_QUIT_ON_WINDOW_CLOSE) 197 , fDecoder("cp-850") 198 { 199 fMainText = new HyperTextView("main text", B_WILL_DRAW); 200 201 // TODO making it non-editable while having custom insets breaks the layout somehow? 202 fMainText->MakeEditable(false); 203 fMainText->MakeSelectable(false); 204 //fMainText->SetInsets(10, 10, 10, 10); 205 206 fInlineButton = new BButton("button", NULL); 207 208 BLayoutBuilder::Group<>(this, B_VERTICAL) 209 .SetInsets(0, 0, 0, B_USE_WINDOW_SPACING) 210 .Add(fMainText, 999) 211 .AddGroup(B_HORIZONTAL) 212 .AddGlue() 213 .Add(fInlineButton) 214 .AddGlue() 215 .End() 216 .AddGlue(0) 217 .End(); 218 } 219 220 void Show() override 221 { 222 BWindow::Show(); 223 Lock(); 224 fInlineButton->SetLowColor(make_color(255, 128, 255)); 225 fMainText->SetViewColor(make_color(196, 255, 255)); 226 fInlineButton->SetTarget(be_app); 227 Unlock(); 228 } 229 230 void SetMainText(const std::string& text) 231 { 232 Lock(); 233 fInlineButton->Hide(); 234 fMainText->SetText(""); 235 236 BString decoded; 237 PrettyPrint(text, decoded); 238 239 text_run_array runs; 240 runs.count = 1; 241 runs.runs->font = be_plain_font; 242 runs.runs->offset = 0; 243 runs.runs->color = make_color(0, 0, 0); 244 fMainText->Insert(decoded, &runs); 245 fMainText->Invalidate(); 246 Unlock(); 247 } 248 249 void PrettyPrint(const std::string& message, BString& decoded) 250 { 251 size_t cursor = 0; 252 while (cursor < message.size()) { 253 switch(message[cursor]) { 254 case '\\': 255 cursor += HandleEscape(message, cursor + 1); 256 break; 257 case '[': 258 { 259 text_run_array runs; 260 runs.count = 1; 261 runs.runs->font = be_plain_font; 262 runs.runs->offset = 0; 263 runs.runs->color = make_color(0, 0, 0); 264 fMainText->Insert(decoded, &runs); 265 decoded = ""; 266 cursor += HandleHyperlink(message, cursor); 267 break; 268 } 269 case '@': 270 case '$': 271 // @ Gender mark (print an e if the player identifies as female) 272 // $ Plural mark (print an s if the counter is plural) 273 printf(BOLD "%c" NORMAL, message[cursor]); 274 cursor++; 275 break; 276 case '_': 277 // Used as a non-breakable space 278 decoded += "\u00A0"; 279 cursor++; 280 break; 281 default: 282 if (message[cursor] >= 0) 283 decoded += message[cursor]; 284 else 285 decoded += cp850_lookup[(message[cursor] - 0x80) & 0xFF]; 286 cursor++; 287 break; 288 } 289 } 290 decoded += '\n'; 291 } 292 293 int HandleHyperlink(const std::string& message, int cursor) 294 { 295 BString linkText; 296 297 int v = 0; 298 int i = 1; 299 while (isdigit(message[cursor + i])) { 300 v = v * 10 + message[cursor + i] - '0'; 301 i++; 302 } 303 304 while (message[cursor + i] != ']') { 305 if (message[cursor + i] >= 0) 306 linkText += message[cursor + i]; 307 else 308 linkText += cp850_lookup[(message[cursor + i] - 0x80) & 0xFF]; 309 i++; 310 } 311 312 text_run_array single_run; 313 single_run.count = 1; 314 single_run.runs->offset = 0; 315 single_run.runs->font = be_plain_font; 316 single_run.runs->color = make_color(0, 0, 196); 317 fMainText->InsertHyperText(linkText, new HyperTextAction(v), &single_run); 318 319 return i + 1; 320 } 321 322 int HandleEscape(const std::string& message, int cursor) 323 { 324 if (message[cursor] == 'o') { 325 fInlineButton->SetLabel("OK"); 326 fInlineButton->Show(); 327 int v = 0; 328 int i = 1; 329 while (isdigit(message[cursor + i])) { 330 v = v * 10 + message[cursor + i] - '0'; 331 i++; 332 } 333 fInlineButton->SetMessage(new BMessage(v)); 334 return i + 1; 335 } else if (message[cursor] == '!') { 336 printf(BOLD "APPEND" NORMAL "\n"); 337 return 2; 338 } else { 339 // TODO unknown escape sequence 340 fprintf(stderr, "ERROR: unknown escape sequence %c", message[cursor]); 341 return 2; 342 } 343 } 344 345 private: 346 HyperTextView* fMainText; 347 BButton* fInlineButton; 348 BPrivate::BTextEncoding fDecoder; 349 }; 350 351 class Readingame: public BApplication 352 { 353 public: 354 Readingame() 355 : BApplication("application/x-vnd.PulkoMandy.Readingame") 356 { 357 } 358 359 void ReadyToRun() override 360 { 361 // TODO check if we have a game loaded 362 fMainWindow = new GameWindow(); 363 364 // FIXME add the global buttons to the main window 365 366 BWindow* debugWindow = new BWindow(BRect(600, 40, 900, 200), "Readingame debugger", 367 B_DOCUMENT_WINDOW, B_AUTO_UPDATE_SIZE_LIMITS | B_QUIT_ON_WINDOW_CLOSE); 368 369 fMainWindow->Show(); 370 fMainWindow->CenterOnScreen(); 371 372 // TODO: only if asked from the command line 373 debugWindow->Show(); 374 375 bool runNextLine = true; 376 int line = 0; 377 while (runNextLine) { 378 runNextLine = RunScriptLine(gRooms[0].fScripts[line]); 379 line++; 380 } 381 fMainWindow->SetMainText(gRooms[gCurrentRoom].fMessages[gCurrentScreen - 1]); 382 } 383 384 void MessageReceived(BMessage* message) override 385 { 386 if (message->what < 256) { 387 gTrigger = message->what; 388 int line = 0; 389 bool runNextLine = true; 390 while (runNextLine) { 391 runNextLine = RunScriptLine(gRooms[0].fScripts[line]); 392 line++; 393 } 394 line = 0; 395 while (runNextLine) { 396 runNextLine = RunScriptLine(gRooms[gCurrentRoom].fScripts[line]); 397 line++; 398 } 399 fMainWindow->SetMainText(gRooms[gCurrentRoom].fMessages[gCurrentScreen - 1]); 400 } else { 401 BApplication::MessageReceived(message); 402 } 403 } 404 405 bool RunScriptLine(const std::vector<uint8_t>& script) 406 { 407 int pc = 0; 408 bool done = false; 409 bool runNextLine = false; 410 411 while (!done) { 412 switch(script[pc++]) { 413 case 0x00: 414 { 415 done = true; 416 break; 417 } 418 case 0x09: 419 { 420 uint8_t varAddress = script[pc++]; 421 uint16_t varValue = script[pc++]; 422 varValue = varValue | (script[pc++] << 8); 423 if (gVar16[varAddress] == varValue) { 424 continue; 425 } else { 426 done = true; 427 runNextLine = true; 428 } 429 } 430 case 0x0D: 431 { 432 uint8_t eventToCheck = script[pc++]; 433 if (gTrigger == eventToCheck) { 434 fprintf(stderr, "unknown param to 0D: %02x\n", script[pc++]); 435 disassemble(script); 436 continue; 437 } else { 438 done = true; 439 runNextLine = true; 440 } 441 break; 442 } 443 case 0x0E: 444 { 445 uint8_t eventToCheck = script[pc++]; 446 // FIXME is it really gTrigger here, or should it be something else? 447 if (gTrigger == eventToCheck) 448 continue; 449 else { 450 done = true; 451 runNextLine = true; 452 } 453 break; 454 } 455 case 0x7F: 456 { 457 // FIXME figure this out 458 pc++; 459 break; 460 } 461 case 0x87: 462 { 463 uint8_t varAddress = script[pc++]; 464 uint16_t varValue = script[pc++]; 465 varValue = varValue | (script[pc++] << 8); 466 gVar16[varAddress] = varValue; 467 break; 468 } 469 case 0x8F: 470 { 471 gCurrentRoom = script[pc++]; 472 break; 473 } 474 case 0x90: 475 { 476 gCurrentScreen = script[pc++]; 477 break; 478 } 479 case 0x93: 480 { 481 gCurrentScreen = script[pc++]; 482 break; 483 } 484 case 0x95: 485 { 486 uint8_t varAddress = script[pc++]; 487 uint8_t varValue = script[pc++]; 488 gVar8[varAddress] = varValue; 489 break; 490 } 491 default: 492 fprintf(stderr, "ERROR: unknown opcode %02x at %04x\n", script[pc - 1], pc - 1); 493 disassemble(script); 494 done = true; 495 break; 496 } 497 } 498 499 return runNextLine; 500 } 501 502 private: 503 GameWindow* fMainWindow; 504 }; 238 505 239 506 void usage() … … 344 611 { 345 612 if (argc != 2) { 613 // TODO open a filepanel instead to load a game 614 // TODO or just look for the TITRES.DAT file 346 615 usage(); 347 616 return 1; … … 413 682 ReadRoomsMessages(msgRoomsFile); 414 683 684 // TODO how many variables are there? 685 gVar8.resize(16); 686 gVar16.resize(16); 687 415 688 /* 416 689 int i = 0; … … 435 708 */ 436 709 437 BApplication app("application/x-vnd.PulkoMandy.Readingame"); 438 439 BWindow* window = new BWindow(BRect(40, 40, 300, 200), "Readingame", B_DOCUMENT_WINDOW, 440 B_AUTO_UPDATE_SIZE_LIMITS | B_QUIT_ON_WINDOW_CLOSE); 441 442 window->Show(); 710 Readingame app; 443 711 444 712 app.Run();
Note:
See TracChangeset
for help on using the changeset viewer.