wordplay.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. #include "images.h"
  2. #include "lastseen.h"
  3. #include "render.h"
  4. #include "terminal.h"
  5. #include "utils.h"
  6. #include <iomanip>
  7. // #include <regex.h>
  8. #include <sstream>
  9. #include <string.h>
  10. #include <string>
  11. #include <vector>
  12. #include "charman.h"
  13. #include "zf_log.h"
  14. #include <unistd.h> // write
  15. extern struct console_details console;
  16. #define BSIZE 512
  17. /*
  18. * harry_idle_event(fd)
  19. *
  20. * User is idle, let's let them know we're here. HAHAHA!
  21. *
  22. */
  23. void harry_idle_event(int fd) {
  24. // Make something happen
  25. std::ostringstream buffer;
  26. int r;
  27. // This is no where near finished, BUT!
  28. // Do not put any ^ codes in these -- the strlen() would be wrong.
  29. const char *phrases[] = {"Hahaha", "Snicker, snicker", "Boo!",
  30. "MeOW", "I see U", "Arrooo!",
  31. "Ahh-wooo!", "Aaaooo!"};
  32. static LastSeen last_seen_harry_event(2);
  33. do {
  34. r = randint((sizeof(phrases) / sizeof(char *)));
  35. } while (last_seen_harry_event.seen_before(r));
  36. int color = random() % 15 + 1;
  37. // %02d = std::setfill('0') << std::setw(2) << (int)
  38. buffer << "^S2^C" << std::setfill('0') << std::setw(2) << color << phrases[r]
  39. << "^P2^CR^D" << std::setw(2) << strlen(phrases[r]);
  40. /*
  41. slen = snprintf(buffer, sizeof(buffer), "^S2^C%02d%s^P2^CR^D%02d", color, cp,
  42. (int)strlen(cp));
  43. if (slen >= sizeof(buffer)) {
  44. ZF_LOGE("snprintf %d > size %d", slen, (int)sizeof(buffer));
  45. buffer[0] = 0;
  46. }
  47. */
  48. std::string str = buffer.str();
  49. ZF_LOGD("harry_event: render(%d, \"%s\")", fd, str.c_str());
  50. render(fd, str);
  51. }
  52. void init_harry() {
  53. // init_have_seen(last_seen_harry_event, MAX_HARRY_EVENT_DUPS);
  54. // ZF_LOGD("init => %d %d", last_seen_harry_event[0],
  55. // last_seen_harry_event[1]);
  56. console_init(&console);
  57. }
  58. // char words[] = "[a-zA-Z]+( [a-zA-Z]+)+";
  59. int mangle_clrscr(std::string &buffer, std::string &work, size_t pos) {
  60. static int ANSI_CLS_count = 0;
  61. ZF_LOGI("seen: ANSI_CLS");
  62. ANSI_CLS_count++;
  63. if (ANSI_CLS_count > 1) {
  64. // get the restore color value
  65. struct console_details temp_console;
  66. memcpy(&temp_console, &console, sizeof(console));
  67. console_receive(&temp_console, buffer.substr(0, pos));
  68. std::string restore_color;
  69. restore_color.assign(color_restore(&temp_console));
  70. if (random_activate(3)) {
  71. std::ostringstream display;
  72. int needs_cls = 0;
  73. struct image {
  74. const char **lines;
  75. int size;
  76. int cls;
  77. int width; // height = size
  78. } images[] = {{ghost, sizeof(ghost) / sizeof(char *), 1, 0},
  79. {ghead, sizeof(ghead) / sizeof(char *), 1, 0},
  80. {wolf, sizeof(wolf) / sizeof(char *), 1, 0},
  81. {panther, sizeof(panther) / sizeof(char *), 1, 0},
  82. {bat, sizeof(bat) / sizeof(char *), 1, 0},
  83. {icu, sizeof(icu) / sizeof(char *), 0, 20},
  84. {skull, sizeof(skull) / sizeof(char *), 0, 19},
  85. {skullblink, sizeof(skullblink) / sizeof(char *), 0, 19}};
  86. static LastSeen last_files(2);
  87. int r;
  88. do {
  89. r = randint((sizeof(images) / sizeof(image)));
  90. } while (last_files.seen_before(r));
  91. std::string fgoto;
  92. if (!images[r].cls) {
  93. int x = 0, y = 0;
  94. x = randint(79 - images[r].width) + 1;
  95. y = randint(24 - images[r].size) + 1;
  96. display << "^f" << std::setfill('0') << std::setw(2) << x
  97. << std::setw(2) << y << "\x1b[1;1H";
  98. fgoto = display.str();
  99. // reset display
  100. display.str(std::string());
  101. display.clear();
  102. // render image, home cursor
  103. // slen = snprintf(fgoto, sizeof(fgoto), "^f%02d%02d\x1b[1;1H", x, y);
  104. } else {
  105. fgoto.assign("^F");
  106. }
  107. needs_cls = images[r].cls;
  108. // I get what's happening. Mystic moves cursor to home, CLS, cursor
  109. // home. When we get here, we're ALWAYS at the top of the screen...
  110. // Hence our bat isn't displayed at the end of the screen.
  111. // This is before the actual CLS, so we CLS before displaying our files.
  112. // I tried a ^P2 before doing this .. but I'd rather have the picture up
  113. // right away I think.
  114. // Ok, yes, there's no filename being sent. :P
  115. render_image(images[r].lines, images[r].size);
  116. display << (needs_cls ? "\x1b[2J" : "") << fgoto << restore_color
  117. << "^P3";
  118. // slen = snprintf(display, sizeof(display), "%s%s%s^P3",
  119. // needs_cls ? "\x1b[2J" : "", fgoto, restore_color);
  120. std::string display_output = display.str();
  121. ZF_LOGI("mangle(ANSI_CLS): %d file inserted %s", r,
  122. repr(display_output.c_str()));
  123. // Move the buffer so there's room for the display string.
  124. buffer.insert(pos, display_output);
  125. work.insert(pos, std::string(display_output.size(), ' '));
  126. return 1; // need_render = 1;
  127. } else {
  128. if (random_activate(4)) {
  129. int r;
  130. std::ostringstream display;
  131. const char *phrasing[] = {
  132. "^R1Haha^P1ha^P1ha", "Poof!", "Got U", "Anyone there?",
  133. "^R1Knock, ^P1Knock",
  134. /*
  135. This picks random color and position -- then
  136. homes cursor and changes to another color. (This can be seen.)
  137. */
  138. "^G0101^C07^S9Segmentation fault (core dumped)^P2"};
  139. static LastSeen last_phrasing(2);
  140. ZF_LOGI("mangle(ANSI_CLS)");
  141. do {
  142. r = randint(sizeof(phrasing) / sizeof(char *));
  143. } while (last_phrasing.seen_before(r));
  144. int color = randint(14) + 1;
  145. int x = randint(30) + 1;
  146. int y = randint(15) + 1;
  147. /*
  148. Don't have it pause there before moving the cursor.
  149. Move the cursor, get the color changed, THEN pause.
  150. Then act all crazy.
  151. NOTE: Make sure if you use any ^R Render effects, turn them off
  152. before trying to display the restore_color. :P ^R0 Also, make
  153. sure you re-home the cursor ^G0101 because that's where they are
  154. expecting the cursor to be! (At least it's how Mystic does it.)
  155. HOME, CLS, HOME, ... Not sure what others do there. We'll see.
  156. */
  157. if (strncmp(phrasing[r], "^G", 2) == 0) {
  158. display << "^S3^P1" << phrasing[r] << "^S0^R0" << restore_color
  159. << "^P1^G0101";
  160. // This starts with a GOTO, so don't use our random position
  161. // slen = snprintf(display, sizeof(display),
  162. // "^S3^P1%s^S0^R0%s^P1^G0101",
  163. // phrasing[r], restore_color);
  164. } else {
  165. display << "^G" << std::setw(2) << std::setfill('0') << x
  166. << std::setw(2) << y << "^S3^C" << std::setw(2) << color
  167. << "^P1" << phrasing[r] << "^S0^R0" << restore_color
  168. << "^P1^G0101";
  169. // slen = snprintf(display, sizeof(display),
  170. // "^G%02d%02d^S3^C%02d^P1%s^S0^R0%s^P1^G0101", x, y,
  171. // color, phrasing[r], restore_color);
  172. };
  173. std::string display_output = display.str();
  174. // sprintf(display, "^P1^S3^C%02d%s^S0^R0%s^P1", color, phrasing[r],
  175. // restore_color);
  176. // Added debug statement so we can identify what was sent... color,
  177. // number picked and what that is
  178. ZF_LOGD("mangle(ANSI_CLS): Inserted color=%02d r=%d phrase='%s'", color,
  179. r, phrasing[r]);
  180. ZF_LOGI("mangle(ANSI_CLS): %d %s", r, repr(display_output.c_str()));
  181. // Move the buffer so there's room for the display string.
  182. buffer.insert(pos, display_output);
  183. work.insert(pos, std::string(display_output.size(), ' '));
  184. return 1; // need_render = 1;
  185. }
  186. }
  187. }
  188. }
  189. int mangle(int fd, std::string &buffer) {
  190. // a simple default for now.
  191. ZF_LOGV_MEM(buffer.data(), buffer.size(), "mangle(%d): %lu bytes", fd,
  192. buffer.size());
  193. int need_render = 0;
  194. int mangled = 0;
  195. int mangled_chars = 0;
  196. static std::string work;
  197. static size_t work_size = 0;
  198. work.assign(buffer);
  199. // This should allow us to monitor any memory allocations
  200. if (work.capacity() != work_size) {
  201. ZF_LOGD("work cap %lu -> %lu", work_size, work.capacity());
  202. work_size = work.capacity();
  203. }
  204. const char *ANSI_CLS = "\x1b[2J";
  205. size_t pos = buffer.find(ANSI_CLS);
  206. if (pos != std::string::npos) {
  207. if (mangle_clrscr(buffer, work, pos)) {
  208. need_render = 1;
  209. }
  210. }
  211. // Ok, maybe the work string was a bad idea?
  212. static std::string text;
  213. static std::vector<int> text_offsets;
  214. size_t stri;
  215. text.clear();
  216. text_offsets.clear();
  217. for (stri = 0; stri < buffer.size(); ++stri) {
  218. // why wasn't \x1b[?1000h handled by console_char?
  219. // what happened to \x0c ? It is there.
  220. termchar tc = console_char(&console, work[stri]);
  221. if (tc.in_ansi) {
  222. if (tc.ansi != START) {
  223. // Ok, this is something. What is it?
  224. // ZF_LOGV("ANSI type %d at %lu", tc.ansi, stri);
  225. switch (tc.ansi) {
  226. case CURSOR:
  227. case CLEAR:
  228. case OTHER:
  229. text.append(1, '.');
  230. text_offsets.push_back(-1);
  231. break;
  232. case COLOR:
  233. // text.append(1, ' ');
  234. // text_offsets.push_back(-1);
  235. // color changes show as nothing in the text string.
  236. break;
  237. }
  238. }
  239. } else {
  240. // These should never get out of sync ...
  241. if (text.size() != text_offsets.size()) {
  242. ZF_LOGE("Error: text != text_offsets %lu != %lu", text.size(),
  243. text_offsets.size());
  244. }
  245. text.append(1, work[stri]);
  246. text_offsets.push_back(stri);
  247. }
  248. }
  249. ZF_LOGV_MEM(buffer.data(), buffer.size(), "Buffer:");
  250. ZF_LOGV_MEM(work.data(), work.size(), "Work:");
  251. ZF_LOGV_MEM(text.data(), text.size(), "Text Buffer:");
  252. // Output vector contents
  253. std::ostringstream oss;
  254. int comma = 0;
  255. for (auto it = std::begin(text_offsets); it != std::end(text_offsets); ++it) {
  256. if (comma) {
  257. oss << ", ";
  258. };
  259. comma++;
  260. oss << *it;
  261. if (comma == 30) {
  262. std::string temp_output = oss.str();
  263. ZF_LOGV("Vector: %s", temp_output.c_str());
  264. // reset ostringstream
  265. oss.str(std::string());
  266. oss.clear();
  267. comma = 0;
  268. }
  269. }
  270. std::string vector_output = oss.str();
  271. ZF_LOGV("Vector: %s", vector_output.c_str());
  272. // reset oss (if we need it)
  273. oss.str(std::string());
  274. oss.clear();
  275. // Begin the mangle process 2.0
  276. {
  277. ZF_LOGD("CharMan");
  278. CharMan cm(buffer, work, text, text_offsets);
  279. ZF_LOGD("CharMan %d, %d chars", cm.mangle_count, cm.mangle_chars);
  280. };
  281. if (need_render) {
  282. render(fd, buffer);
  283. } else {
  284. write(fd, buffer.data(), buffer.size());
  285. }
  286. return need_render;
  287. }