#pragma warning(disable : 4706) // assignment within conditional expression #pragma warning(disable : 4996) // _CRT_SECURE_NO_DEPRECATE #include "stdafx.h" #include #include #include #include #include #include #include #include #include "SerialPort\SerialPort.h" enum { COM_NO_PORT = -1, COM_MIN_PORT = 1, COM_MAX_PORT = 99 }; CSerialPort avr; int avrOnPort = COM_NO_PORT; // Serial Port 1, 2, ... taking note that some USB adapters can be up toward channel 11, 12, ... unsigned avrBaud = 9600; DWORD const retryInterval = 1000; // ms // Each DT is the hex-character from the stream // // typedef struct { uint8_t DT0; // * Baud Rate uint8_t DT1; // * Receive Buffer 'E' uint8_t DT2; // * Receive Buffer '0' uint8_t DT3; // * '1' uint8_t DT4; // * Command Timeout '9' uint8_t DT5; // * '0' uint8_t DT6; // * '0' uint8_t DT7; // * System '0':Ok, '1':Busy uint8_t DT8; // * Power 0:Off, 1:On uint8_t DT9; // Input 0: Phono, 1:CD, 2:Tuner, 3:CD-R, 4:MD-Tape, 5:DVD, 6:D-TV, 7:Cbl, 9:VCR1, A:VCR2 uint8_t DT10; // 6ch input 0:Off, 1:On uint8_t DT11; // Input Mode 0:AUTO, 2:DTS, 4:Analog, 5:Analog Only uint8_t DT12; // Audio Mute 0:Off, 1:On uint8_t DT13; uint8_t DT14; uint8_t DT15; uint8_t DT16; uint8_t DT17; uint8_t DT18; uint8_t DT19; uint8_t DT20; uint8_t DT21; uint8_t DT22; uint8_t DT23; uint8_t DT24; uint8_t DT25; uint8_t DT26; uint8_t DT27; uint8_t DT28; uint8_t DT29; uint8_t DT30; uint8_t DT31; uint8_t DT32; uint8_t DT33; uint8_t DT34; uint8_t DT35; uint8_t DT36; uint8_t DT37; uint8_t DT38; uint8_t DT39; uint8_t DT40; uint8_t DT41; uint8_t DT42; uint8_t DT43; uint8_t DT44; uint8_t DT45; uint8_t DT46; uint8_t DT47; uint8_t DT48; uint8_t DT49; uint8_t DT50; uint8_t DT51; uint8_t DT52; uint8_t DT53; uint8_t DT54; uint8_t DT55; uint8_t DT56; uint8_t DT57; uint8_t DT58; uint8_t DT59; uint8_t DT60; uint8_t DT61; uint8_t DT62; uint8_t DT63; uint8_t DT64; uint8_t DT65; uint8_t DT66; uint8_t DT67; uint8_t DT68; uint8_t DT69; uint8_t DT70; uint8_t DT71; uint8_t DT72; uint8_t DT73; uint8_t DT74; uint8_t DT75; uint8_t DT76; uint8_t DT77; uint8_t DT78; uint8_t DT79; uint8_t DT80; uint8_t DT81; uint8_t DT82; uint8_t DT83; uint8_t DT84; uint8_t DT85; uint8_t DT86; uint8_t DT87; uint8_t DT88; uint8_t DT89; uint8_t DT90; uint8_t DT91; uint8_t DT92; uint8_t DT93; uint8_t DT94; uint8_t DT95; uint8_t DT96; uint8_t DT97; uint8_t DT98; uint8_t DT99; uint8_t DT100; uint8_t DT101; uint8_t DT102; uint8_t DT103; uint8_t DT104; uint8_t DT105; uint8_t DT106; uint8_t DT107; uint8_t DT108; uint8_t DT109; uint8_t DT110; uint8_t DT111; uint8_t DT112; uint8_t DT113; uint8_t DT114; uint8_t DT115; uint8_t DT116; uint8_t DT117; uint8_t DT118; uint8_t DT119; uint8_t DT120; uint8_t DT121; uint8_t DT122; uint8_t DT123; uint8_t DT124; uint8_t DT125; uint8_t DT126; uint8_t DT127; uint8_t DT128; uint8_t DT129; uint8_t DT130; uint8_t DT131; uint8_t DT132; uint8_t DT133; uint8_t DT134; uint8_t DT135; uint8_t DT136; uint8_t DT137; // PB/SB Select 00: PR, 01: SB // Last documented register uint8_t DT138; uint8_t DT139; uint8_t DT140; uint8_t DT141; uint8_t DT142; uint8_t DT143; uint8_t DT144; uint8_t DT145; uint8_t DT146; uint8_t DT147; uint8_t DT148; uint8_t DT149; uint8_t DT150; uint8_t DT151; uint8_t DT152; uint8_t DT153; uint8_t DT154; uint8_t DT155; uint8_t DT156_255[100]; } AVR_Configuration_T; // 264 total typedef struct { uint8_t type[5]; // Model ID uint8_t version; // A-Z uint8_t length[2]; // 1 - 255 } AVR_StatusHeader_T; typedef struct { bool headerValid; bool configValid; AVR_StatusHeader_T header; AVR_Configuration_T config; } AVR_Status_T; //AVR_StatusHeader_T avrStatusHeader; //AVR_Configuration_T avrConfigData; AVR_Status_T avrStatus; typedef enum { EXIT_OK = 0, EXIT_IllegalOption = 1, EXIT_ConfigError = 2, EXIT_UserESC = 3, EXIT_UserQ = 4, EXIT_ExternalQ = 5 } ExitCode; constexpr auto MAXTEXTLEN = 512; char progname[MAXTEXTLEN]; int AttachToSerialPort(); int DetachSerialPort(); // SerialSend // // Try to send the message. // // @param[in] p is a pointer to the message to send // @param[in] len is the count of bytes in the message // @returns true if the serial interface accepted it. // bool SerialSend(const uint8_t *p, DWORD len); #define SERIALQUEUESIZE 3 typedef struct { uint8_t *messageToSend; DWORD len; } SerialQueue_T; static SerialQueue_T serialQueue[SERIALQUEUESIZE]; static int serialQueueCount = 0; // ProcessSerialQueue // // If there are parameters passed, insert a message into the queue. // Process the queue (with zero or more messages) to send // // @param[in] p is a pointer to the message to send // @param[in] len is the count of bytes in the message // @returns false if the queue (which is a fixed size) is full. // bool ProcessSerialQueue(const uint8_t *p = NULL, DWORD len = 0); // ProcessSerialReceive // // See if anything came in, and if so, process it. // // @returns true if a message was processed. // bool ProcessSerialReceive(void); void EnumerateComPorts(); unsigned long Hex2Dec(uint8_t *p, int dig); // Helpers to convert status codes to text const char *BusyToText(uint8_t val); const char *WarnToText(uint8_t val); const char *PlaybackToText(uint8_t val); const char *FsToText(uint8_t val); const char *ZonePower(uint8_t val); const char *VolumeDB(uint8_t val); const char *ProgramName(uint8_t val); void ShowAllStatusInfo(); bool UserWantsToExitCanSniff = false; DWORD progStartTime; // For special cursor positioning bool consoleInit = false; HANDLE hStdout, hStdin; CONSOLE_SCREEN_BUFFER_INFO csbi; WORD wOldColorAttrs; // Save the current console colors short consoleWidth = 105; short consoleHeight = 80; // 0 99 // +---------------------------------------------------------------+ // | Program banner information | 0 // | on two lines | // | Current status information starts on line 3 | // | and down a ways... | // | | // | | // | | // + Down near the bottom is the scroll region + 59 // | | // | | // | | 79 // +---------------------------------------------------------------+ 80 short scrollTop = 59; short scrollBot = 79; short consoleScrollHeight = 20; void Console_Init(); void Console_Cls(); bool Console_SetCursorVisibility(bool visible); void Console_SetCursor(short x, short y); void Console_Write(const char *text); void Console_WriteAt(short x, short y, const char *text); void Console_ScrollBottomRegion(short topLine, short bottomLine); bool Console_AdvanceToNextLineIfNotRoomFor(short x, int scroll = -1); typedef struct { const char *pMsg; DWORD MsgLen; } Message_T; typedef struct { const char Char; const char *pDescription; Message_T TxMsg; } UserCmd_T; const UserCmd_T UserCommands[] = { { 'R', "Ready", {"\x11" "000" "\x03", 5 } }, { 'P', "Power On", {"\x02" "07A1D" "\x03", 7} }, { 'p', "Power Off", {"\x02" "07A1E" "\x03", 7} }, { '1', "Zone 1 On", {"\x02" "07E7E" "\x03", 7} }, { '!', "Zone 1 Standby", {"\x02" "07E7F" "\x03", 7} }, { '2', "Zone 2 On", {"\x02" "07EBA" "\x03", 7} }, { '@', "Zone 2 Standby", {"\x02" "07EBB" "\x03", 7} }, { '3', "Zone 3 On", {"\x02" "07AED" "\x03", 7} }, { '#', "Zone 3 Standby", {"\x02" "07AEE" "\x03", 7} }, { 'V', "Vol 1 Up", {"\x02" "07A1A" "\x03", 7} }, { 'v', "Vol 1 Down", {"\x02" "07A1B" "\x03", 7} }, { 'M', "Vol 1 Mute", {"\x02" "07EA2" "\x03", 7} }, { 'm', "Vol 1 UnMute", {"\x02" "07EA3" "\x03", 7} }, { '/', "Show All" }, { '?', "Help on runtime commands" }, { 'S', "Set RTS" }, { 's', "Clear RTS" }, { '\x1B', "Quit App" }, }; typedef struct { //uint8_t type; // 0 to 4, and 0xFF for don't care //uint8_t guard; // 0 to 2, and 0xFF for don't care uint8_t rCmd; uint8_t configOffset; // offset into the Config DT0 - DT155 array, or 0xFF for no action uint8_t numToTransfer; // number of bytes to transfer into the Config array, or 0 for no action bool showAll; // true to call ShowAllStatusInfo after processing void (*fncHelper)(uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen); const char *TextFormatter; // used primarily for development printf(TextFormatter, value) const char *(*fncValueToText)(uint8_t rDat); } MessageHandler_T; void RxSystemReport( uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen); void RxWarningReport(uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen); void RxPlaybackReport(uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen); void RxFsReport(uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen); void RxPowerStatus( uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen); void RxMasterVolStatus(uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen); void RxProgramStatus(uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen); // rCmd: 2-ASCII Hex Bytes converted to uint8_t // configOffset: offset into avrStatus.config.DTxx // numToTransfer: number of bytes to transfer from message to config (0 = none) // fncHelper: optional function to call upon receipt // const MessageHandler_T MessageHandlers[] = { //rCmd cfgOfs num ShowAll function {0x00, 7, 1, 1, RxSystemReport }, {0x01, 0, 0, 0, RxWarningReport }, {0x10, 31, 1, 1, RxPlaybackReport }, {0x11, 32, 1, 1, RxFsReport }, {0x12, 33, 1, 1, NULL }, // EX/EX {0x13, 34, 1, 1, NULL }, // Thr / Bypass {0x14, 35, 1, 1, NULL }, // RED dts {0x15, 38, 1, 1, NULL }, // Tuner tuned {0x16, 43, 1, 1, NULL }, // Dts 96/24 {0x20, 8, 0, 1, RxPowerStatus, "Zone Power: %s", ZonePower}, {0x21, 9, 1, 1, NULL }, // Input Source {0x22, 11, 1, 1, NULL }, // Input Mode {0x23, 12, 1, 1, NULL }, // Mute Status {0x24, 13, 1, 1, NULL }, // Zone 2 Input Source {0x25, 14, 1, 1, NULL }, // Zone 2 Mute {0x26, 15, 2, 1, RxMasterVolStatus }, {0x27, 17, 2, 1, NULL }, // Zone 2 Vol {0x28, 19, 2, 1, RxProgramStatus }, // Program {0x29, 25, 1, 1, NULL }, // Tuner Page {0x2A, 26, 1, 1, NULL }, // Tuner Preset Number {0x2B, 23, 1, 1, NULL }, // OSD {0x2C, 24, 1, 1, NULL }, // Sleep Timer {0x2D, 22, 1, 1, NULL }, // EX/ES(Key) {0x2E, 29, 1, 1, NULL }, // Speaker Relay A {0x2F, 30, 1, 1, NULL }, // Speaker Relay B {0x36, 39, 1, 1, NULL }, // DC1 Trigger {0x3C, 45, 1, 1, NULL }, // DC2 Trigger {0x82, 27, 1, 1, NULL }, // Night Mode {0xA1, 0, 0, 0, NULL }, // Zone 3 Mute }; // RCMD 00 // void RxSystemReport(uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen) { (void)rCmd, pMessage, msgLen; //SetData(&avrStatus.config.DT8, pMessage + 5, 2); char buf[MAXTEXTLEN]; sprintf_s(buf, MAXTEXTLEN, "System Report: %s\n", BusyToText(rDat)); Console_Write(buf); Console_ScrollBottomRegion(scrollTop, scrollBot); //ShowAllStatusInfo(); } // RCMD 01 // void RxWarningReport(uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen) { (void)rCmd, pMessage, msgLen; //SetData(&avrStatus.config.DT8, pMessage + 5, 2); char buf[MAXTEXTLEN]; sprintf_s(buf, MAXTEXTLEN, "Warning Report: %s\n", WarnToText(rDat)); Console_Write(buf); Console_ScrollBottomRegion(scrollTop, scrollBot); //ShowAllStatusInfo(); } // RCMD 10 void RxPlaybackReport(uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen) { (void)rCmd, pMessage, msgLen; char buf[MAXTEXTLEN]; sprintf_s(buf, MAXTEXTLEN, "Playback Report: %s\n", PlaybackToText(rDat)); Console_Write(buf); Console_ScrollBottomRegion(scrollTop, scrollBot); //ShowAllStatusInfo(); } // RCMD 11 void RxFsReport(uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen) { (void)rCmd, pMessage, msgLen; char buf[MAXTEXTLEN]; sprintf_s(buf, MAXTEXTLEN, "Playback Report: %s\n", FsToText(rDat)); Console_Write(buf); Console_ScrollBottomRegion(scrollTop, scrollBot); //ShowAllStatusInfo(); } // RCMD 20 void RxPowerStatus(uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen) { (void)rCmd, pMessage, msgLen; //SetData(&avrStatus.config.DT8, pMessage + 5, 2); char buf[MAXTEXTLEN]; sprintf_s(buf, MAXTEXTLEN, "Zone Power: %s\n", ZonePower(rDat)); Console_Write(buf); Console_ScrollBottomRegion(scrollTop, scrollBot); //ShowAllStatusInfo(); } // RCMD 26 void RxMasterVolStatus(uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen) { (void)rCmd, pMessage, msgLen; char buf[MAXTEXTLEN]; sprintf_s(buf, MAXTEXTLEN, "Vol: %s\n", VolumeDB(rDat)); Console_Write(buf); Console_ScrollBottomRegion(scrollTop, scrollBot); //ShowAllStatusInfo(); } // RCMD 28 void RxProgramStatus(uint8_t rCmd, uint8_t rDat, uint8_t *pMessage, uint32_t msgLen) { (void)rCmd, pMessage, msgLen; char buf[MAXTEXTLEN]; sprintf_s(buf, MAXTEXTLEN, "Vol: %s\n", ProgramName(rDat)); Console_Write(buf); Console_ScrollBottomRegion(scrollTop, scrollBot); //ShowAllStatusInfo(); } int CS_KeyHit() { int h = _kbhit(); return h; } int CS_GetChar() { int k = _getch(); // More leadin chars return k; } BOOL WINAPI ControlIntercept(DWORD CtrlType) { switch (CtrlType) { case CTRL_C_EVENT: case CTRL_CLOSE_EVENT: case CTRL_BREAK_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: UserWantsToExitCanSniff = true; return FALSE; default: return FALSE; } } void ProcessWindowsMessage(void) { MSG msgx; bool msgReturn; msgReturn = PeekMessage(&msgx, NULL, WM_QUIT, WM_QUIT, PM_NOREMOVE); if (msgReturn == true && msgx.message == WM_QUIT) { UserWantsToExitCanSniff = true; } } // EmitBuffer // // Emits the provided buffer to the console, translating non-printable // characters into hex codes enclosed in square brackets. // // Consider line-wrapping to align the next line // void EmitBuffer(const char *prefix, const uint8_t *buf, size_t len = 0, bool appendReturn = false) { int i = 0; const char *p = (const char *)buf; DWORD now = timeGetTime(); char txtBuf[MAXTEXTLEN] = ""; if (len == 0) len = strlen((const char *)buf); sprintf_s(txtBuf, MAXTEXTLEN, "%7.3f: [%3d]%s", (float)(now - progStartTime)/1000.0f, (int)strlen(p), prefix); Console_Write(txtBuf); while (*p && ((unsigned)(p - (const char *)buf) < len)) { if (isprint(*p)) { putchar(*p); i++; } else if (*p == '\r') { putchar('\n'); Console_ScrollBottomRegion(scrollTop, scrollBot); } else if (*p == '\n') { // skip it } else { printf("[%02X]", (unsigned char)*p); } if ((i & 3) == 0) { if (Console_AdvanceToNextLineIfNotRoomFor(12, 1)) { //Console_ScrollBottomRegion(scrollTop, scrollBot); printf(" "); // sized to get past the timestamp, length, and prefix i = 0; } else { printf(" "); } } p++; } if (appendReturn) { Console_ScrollBottomRegion(scrollTop, scrollBot); // putch('\r'); } else { //putch('\n'); //Console_ScrollBottomRegion(scrollTop, scrollBot); } } void EchoSerialRecv(const uint8_t *pMsg) { Console_SetCursor(0, -1); EmitBuffer("< ", pMsg); Console_ScrollBottomRegion(scrollTop, scrollBot); } bool SerialSend(const uint8_t *p, DWORD len) { bool retVal = false; Console_SetCursor(0, -1); EmitBuffer("> ", p, len); Console_ScrollBottomRegion(scrollTop, scrollBot); if (avr.Write((const LPVOID)p, len) == len) { retVal = true; } else { Console_SetCursor(0, -1); Console_Write("***** Failed to send. Port not open?"); Console_ScrollBottomRegion(scrollTop, scrollBot); } return retVal; } bool ProcessSerialQueue(const uint8_t *p, DWORD len) { bool retVal = false; // assume fail static bool freshData = false; if (p && len) { if (serialQueueCount < SERIALQUEUESIZE) { serialQueue[serialQueueCount].messageToSend = (uint8_t *)malloc(len); if (serialQueue[serialQueueCount].messageToSend) { memcpy(serialQueue[serialQueueCount].messageToSend, p, len); serialQueue[serialQueueCount].len = len; serialQueueCount++; retVal = true; freshData = true; } } } if (serialQueueCount) { if (SerialSend((const uint8_t *)serialQueue[0].messageToSend, serialQueue[0].len)) { --serialQueueCount; free(serialQueue[0].messageToSend); for (int i = 0; i < serialQueueCount; i++) { serialQueue[i] = serialQueue[i + 1]; } retVal = true; } } return retVal; } void EmitSpinner() { static int x = 0; const char arrow[] = "|/-\\|/-\\"; printf("%c\b", arrow[x++]); if (x == 8) x = 0; } void EmitRuntimeHelp() { for (int i = 0; i < sizeof(UserCommands) / sizeof(UserCmd_T); i++) { char tinyBuf[6]; if (isprint(UserCommands[i].Char)) { sprintf_s(tinyBuf, sizeof(tinyBuf), "%c", UserCommands[i].Char); } else if (UserCommands[i].Char == '\x1B') { sprintf_s(tinyBuf, sizeof(tinyBuf), "%s", ""); } else if (UserCommands[i].Char < ' ') { sprintf_s(tinyBuf, sizeof(tinyBuf), "^%c", UserCommands[i].Char + 64); } else { sprintf_s(tinyBuf, sizeof(tinyBuf), "0x%02X", (unsigned char)UserCommands[i].Char); } char buf[MAXTEXTLEN]; sprintf_s(buf, MAXTEXTLEN, " %5s %-25s", tinyBuf, UserCommands[i].pDescription); Console_AdvanceToNextLineIfNotRoomFor((short)strlen(buf), 1); Console_Write(buf); } if (avr.IsOpen()) { Console_SetCursor(0, -1); printf(" Com Status: RTS: %-3s ", avr.Get_RTS_State() ? "ON" : "OFF"); printf("DTR: %-3s ", avr.Get_DTR_State() ? "ON" : "OFF"); printf("CTS: %-3s ", avr.Get_CTS_State() ? "ON" : "OFF"); printf("DSR: %-3s ", avr.Get_DSR_State() ? "ON" : "OFF"); printf("RI: %-3s\n", avr.Get_RI_State() ? "ON" : "OFF"); Console_ScrollBottomRegion(scrollTop, scrollBot); } } // PCMessage // // Various responses need to be formatted and shown on screen // The last param will allow resetting the column counter // and pre and post-pending \n // static void PCMessage(const char *msg, int len, uint8_t **src, const char * (fncHelper)(uint8_t val) = NULL) { char buf[MAXTEXTLEN]; const char *p = buf; if (fncHelper) { // Get the binary value if we need it uint8_t val = (uint8_t)Hex2Dec(*src, len); p = (*fncHelper)(val); *src += len; } else { // Create a string value when we need that strncpy(buf, (char *)*src, len); // s/w *src += len; buf[len] = '\0'; } char outBuf[MAXTEXTLEN]; sprintf_s(outBuf, MAXTEXTLEN, "%17s %-8s", msg, p ); Console_AdvanceToNextLineIfNotRoomFor(26); Console_Write(outBuf); } const char * BusyToText(uint8_t val) { static char buf[16]; if (val == 0) { strcpy_s(buf, sizeof(buf), "OK"); } else if (val == 1) { strcpy_s(buf, sizeof(buf), "BUSY"); } else { sprintf_s(buf, sizeof(buf), "0x%02X ?", (unsigned char)val); } return buf; } const char * WarnToText(uint8_t val) { static char buf[16]; if (val == 0) { strcpy_s(buf, sizeof(buf), "OK"); } else if (val == 1) { strcpy_s(buf, sizeof(buf), "BUSY"); } else { sprintf_s(buf, sizeof(buf), "0x%02X ?", (unsigned char)val); } return buf; } const char *PlaybackToText(uint8_t val) { static char buf[60]; const char *playbackList[] = { /* 0 */ "6Ch Input", /* 1 */ "Analog", /* 2 */ "PCM", /* 3 */ "DD (except 2/0)", /* 4 */ "DD (2/0)", /* 5 */ "DD Karaoke", /* 6 */ "DD EX", /* 7 */ "DTS", /* 8 */ "DTS ES", /* 9 */ "Other Digital", /* A */ "DTS Analog Mute", /* B */ "DTS Discrete", /* C */ "Other than AAC 2/0", /* D */ "AAC 2/0", }; if (val < sizeof(playbackList)/sizeof(playbackList[0])) strcpy_s(buf, sizeof(buf), playbackList[val]); else sprintf_s(buf, sizeof(buf), "0x%04X ?", val); return buf; } const char *FsToText(uint8_t val) { static char buf[60]; const char *fsList[] = { /* 0 */ "Analog", /* 1 */ "32 kHz", /* 2 */ "44.1 kHz", /* 3 */ "48 kHz", /* 4 */ "64 kHz", /* 5 */ "88.2 kHz", /* 6 */ "96 kHz", /* 7 */ "unknown", /* 8 */ "128.0 kHz unk", /* 9 */ "176.4 kHz unk", /* A */ "192.0 kHz unk", }; if (val < sizeof(fsList) / sizeof(fsList[0])) strcpy_s(buf, sizeof(buf), fsList[val]); else sprintf_s(buf, sizeof(buf), "0x%04X ?", val); return buf; } const char * OnOffText(uint8_t val) { static char buf[16]; if (val == 0) { strcpy_s(buf, sizeof(buf), "Off"); } else if (val == 1) { strcpy_s(buf, sizeof(buf), "On"); } else { sprintf_s(buf, sizeof(buf), "0x%04X ?", val); } return buf; } const char *ZonePower(uint8_t val) { static char buf[60]; const char *powerList[8] = { /* 0 */ "All Off (Main, Zone 2, Zone 3)", /* 1 */ "All On (Main, Zone 2, Zone 3)", /* 2 */ "Main On | Zone 2 Off | Zone 3 Off", /* 3 */ "Main Off | Zone 2 On | Zone 3 On ", /* 4 */ "Main On | Zone 2 On | Zone 3 Off", /* 5 */ "Main On | Zone 2 Off | Zone 3 Off", /* 6 */ "Main Off | Zone 2 On | Zone 3 On ", /* 7 */ "Main Off | Zone 2 Off | Zone 3 On ", }; if (val < 8) strcpy_s(buf, sizeof(buf), powerList[val]); else sprintf_s(buf, sizeof(buf), "0x%04X ?", val); return buf; } const char * InputText(uint8_t val) { const char *inputList[13] = { "0:Phono", "1:CD", "2:Tuner", "3:CD-R", "4:MD-Tape", "5:DVD", "6:D-TV", "7:Cable", "8:unk", "9:VCR1", "A:VCR2", "B:unk", "C:V-Aux" }; if (val <= 12) { return inputList[val]; } else { return "huh?"; } } const char *ProgramName(uint8_t val) { static char returnBuf[MAXTEXTLEN] = ""; returnBuf[0] = '\0'; if (val & 0x80) { strcpy_s(returnBuf, MAXTEXTLEN, "STRAIGHT "); } val &= 0x7F; // Not accommodating the other variants yet const char *nameList[] = { "Hall A (HALL1)", "Hall B", "Hall C", "unk", "Hall C", "Hall E", "Live Concert (HALL2)", "unk", "Tokyo", "Freiburg (CHURCH)", "Royaumont", "unk", "Village Gate", "Village Vanguard", "The Bottom Line (JAZZ)", "unk", "The Roxy Theater (ROCK)", "Warehouse Loft", "Arena", "unk", "Disco", "Party", "Game", "7 Ch Stereo", "Pop/Rock (Music Video)", "DJ", "unk", "unk", "Opera", "Pavillion", "unk", "unk", "Mono Movie", "Variety Sports", "unk", "unk", "Spectacre", "Sci - Fi", "unk", "unk", "Adventure", "General", "unk", "unk", "Normal", "Enhanced", "unk", "unk", "PLII Movie", "PLII Music", "Neo: 6 Movie", "Neo: 6 Music", "STEREO A 2CH Stereo", "STEREO B 2CH Direct Stereo" "THX A Cinema", "THX B Music", }; if (val < sizeof(nameList)) { strcat_s(returnBuf, MAXTEXTLEN, nameList[val]); } else { strcpy_s(returnBuf, MAXTEXTLEN, "huh?"); } return returnBuf; } // VolumeDB // // Convert the communication units of volume into db // // Piecewise // for values 0 to 199 // db = 0.5634 x - 112.11 // for values 200 to 255 // db = 0.5 x - 99.5 // Single Linear // db = 0.5574 x - 111.45 // const char *VolumeDB(uint32_t val) { static char buf[16]; float m, b; if (val < 200) { m = 0.5634f; b = -112.11f; } else { m = 0.5f; b = -99.5f; } float db = m * val + b; sprintf_s(buf, sizeof(buf), "%+5.1f db", db); return buf; } void ShowAllStatusInfo(void) { if (avrStatus.headerValid) { bool priorState = Console_SetCursorVisibility(false); Console_SetCursor(0, 2); char buf[MAXTEXTLEN]; sprintf_s(buf, MAXTEXTLEN, "Model ID: %c%c%c%c%c, ver: %c\n", avrStatus.header.type[0], avrStatus.header.type[1], avrStatus.header.type[2], avrStatus.header.type[3], avrStatus.header.type[4], avrStatus.header.version); Console_Write(buf); uint8_t *p = (uint8_t *)&avrStatus.config; PCMessage("Baud Rate", 1, &p); PCMessage("Rx Buffer", 2, &p); PCMessage("Cmd Timeout", 3, &p); PCMessage("Handshake", 1, &p); PCMessage("Busy", 1, &p, BusyToText); PCMessage("Power", 1, &p, OnOffText); if (avrStatus.configValid) { PCMessage("Input", 1, &p, InputText); PCMessage("6 ch", 1, &p); PCMessage("Inp mode", 1, &p); PCMessage("Mute", 1, &p, OnOffText); PCMessage("Zone 2", 1, &p, InputText); PCMessage("Mute 2", 1, &p, OnOffText); PCMessage("Volume", 2, &p, VolumeDB); PCMessage("Volume 2", 2, &p, VolumeDB); PCMessage("pgm", 2, &p); PCMessage("effect", 1, &p, OnOffText); PCMessage("6.1/es status", 1, &p); PCMessage("OSD", 1, &p); PCMessage("sleep", 1, &p); PCMessage("Tuner Pg", 1, &p); PCMessage("Tuner #", 1, &p); PCMessage("Night", 1, &p, OnOffText); PCMessage("?????", 1, &p); PCMessage("Spkr A", 1, &p, OnOffText); PCMessage("Spkr B", 1, &p, OnOffText); PCMessage("Playback", 1, &p); PCMessage("Fs", 1, &p); PCMessage("Ex/Es", 1, &p); PCMessage("Thr Bypass", 1, &p); PCMessage("Red DTS", 1, &p); PCMessage("Headph", 1, &p, OnOffText); PCMessage("Tuner Band", 1, &p); PCMessage("Tuner Tuned", 1, &p); PCMessage("DC1 Control Out", 1, &p); PCMessage("?????", 2, &p); PCMessage("DC1 Trig Ctrl", 1, &p); PCMessage("DTS 96/24", 1, &p, OnOffText); PCMessage("DC2 Trig Ctrl", 1, &p); PCMessage("DC2 Trig", 1, &p); PCMessage("Spkr B Set", 1, &p); PCMessage("Zone 2 SP out", 1, &p, OnOffText); PCMessage("Main R", 2, &p); PCMessage("Main L", 2, &p); PCMessage("Center", 2, &p); PCMessage("Rear R", 2, &p); PCMessage("Rear L", 2, &p); PCMessage("Sur Bk R", 2, &p); PCMessage("Sur Bk L", 2, &p); PCMessage("Front R", 2, &p); PCMessage("Front L", 2, &p); PCMessage("Sub", 2, &p); PCMessage("?????", 6, &p); PCMessage("LFE SP", 2, &p); PCMessage("LFE HP", 2, &p); PCMessage("Audio Delay", 2, &p); PCMessage("?????", 4, &p); PCMessage("Inp Mode Set", 1, &p); PCMessage("Dimmer", 1, &p); PCMessage("OSD Msg", 1, &p); PCMessage("OSD Shift", 2, &p); PCMessage("Gray Back", 1, &p); PCMessage("Video Conv", 1, &p, OnOffText); PCMessage("D Range SP", 1, &p); PCMessage("D Range HP", 1, &p); PCMessage("Zone 2 Vol out", 1, &p); PCMessage("?????", 1, &p); PCMessage("Memory Guard", 1, &p, OnOffText); PCMessage("SP set center", 1, &p); PCMessage("SP set main", 1, &p); PCMessage("SP set rear L/R", 1, &p); PCMessage("SP set rear ct", 1, &p); PCMessage("SP set front", 1, &p); PCMessage("SP set LFE/Bass", 1, &p); PCMessage("6 ch center", 1, &p); PCMessage("6 ch sub", 1, &p); PCMessage("Main level", 1, &p); PCMessage("Test Mode", 1, &p); PCMessage("?????", 1, &p); PCMessage("Lvl 6 ch main L", 2, &p); PCMessage("Lvl 6 ch main R", 2, &p); PCMessage("Lvl 6 ch center", 2, &p); PCMessage("Lvl 6 ch sl", 2, &p); PCMessage("Lvl 6 ch sr", 2, &p); PCMessage("Lvl 6 ch sbl", 2, &p); PCMessage("Lvl 6 ch sbr", 2, &p); PCMessage("Lvl 6 ch front l", 2, &p); PCMessage("Lvl 6 ch front r", 2, &p); PCMessage("Lvl 6 ch swfr", 2, &p); PCMessage("Zone 3 Inp", 1, &p); PCMessage("Zone 3 Mute", 1, &p); PCMessage("Zone 3 Vol", 2, &p); PCMessage("?????", 1, &p); PCMessage("MultiCh Select", 1, &p); PCMessage("MultiCh Surround", 1, &p); PCMessage("SP Set SW1", 1, &p); PCMessage("SP Set Crossover", 1, &p); PCMessage("Component OSD", 1, &p, OnOffText); PCMessage("PB/SB Select", 1, &p); } printf("\n"); Console_SetCursorVisibility(priorState); } } void ProcessKeyboard(void) { static uint32_t spin = 0; bool cmdFound = false; Console_SetCursor(0, -1); if (spin++ % 16 == 0) EmitSpinner(); if (CS_KeyHit()) { int c = CS_GetChar(); switch (c) { case '?': EmitRuntimeHelp(); break; case '/': ShowAllStatusInfo(); break; case '\x1B': UserWantsToExitCanSniff = true; break; case 'S': avr.Set_RTS_State(TRUE); Console_SetCursor(0, -1); printf("RTS set ON\n"); Console_ScrollBottomRegion(scrollTop, scrollBot); break; case 's': avr.Set_RTS_State(FALSE); Console_SetCursor(0, -1); printf("RTS set OFF\n"); Console_ScrollBottomRegion(scrollTop, scrollBot); break; default: for (int i = 0; i < sizeof(UserCommands) / sizeof(UserCmd_T); i++) { if (UserCommands[i].Char == c) { ProcessSerialQueue((const uint8_t *)UserCommands[i].TxMsg.pMsg, UserCommands[i].TxMsg.MsgLen); cmdFound = true; break; } } if (!cmdFound) { Console_SetCursor(0, -1); printf("Unrecognized command '%c' (0x%02X)\n", isprint(c) ? c : '.', (unsigned char)c); Console_ScrollBottomRegion(scrollTop, scrollBot); } break; } } return; } void GetProgName(char *name) { char *p = strrchr(name, '\\'); if (p) { p++; } else { p = name; } strcpy_s(progname, MAXTEXTLEN, p); p = strrchr(progname, '.'); if (0 == stricmp(p, ".exe")) { *p = '\0'; } } void EmitCommandLineHelp() { printf("%s [options] by Smartware Computing\n", progname); printf(" options:\n"); printf(" -C=X[,yyyy] Set to Com port X to baud rate yyyy\n"); printf(" Defaults baud is %d\n", avrBaud); printf("\n"); if (avrOnPort == COM_NO_PORT) { EnumerateComPorts(); } exit(EXIT_OK); } /******************************************************/ /* m a i n ( ) */ /******************************************************/ int __cdecl main(int argc, char *argv[]) { progStartTime = timeGetTime(); Console_Init(); GetProgName(argv[0]); // Change the colors... // SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_INTENSITY); for (int i = 1; i < argc; i++) { int param1, param2; if (1 == sscanf_s(argv[i], "-C=%d", ¶m1)) { avrOnPort = param1; } else if (2 == sscanf_s(argv[i], "-C=%d,%d", ¶m1, ¶m2)) { avrOnPort = param1; avrBaud = param2; } else { printf("***** Unrecognized command '%s' *****\n", argv[i]); EmitCommandLineHelp(); exit(EXIT_IllegalOption); } } if (argc == 0 || avrOnPort == COM_NO_PORT) { EmitCommandLineHelp(); } Console_Cls(); if (!SetConsoleCtrlHandler(ControlIntercept, TRUE)) { printf("WARN: Cannot install Console Control Handler\n"); } if (AttachToSerialPort()) { printf("AVR - a command shell utility for a Yamaha RX-2400 AVR by D.Smart\n"); printf("Attached to Serial Port on COM%i at %i baud\n", avrOnPort, avrBaud); Console_WriteAt(0, scrollTop - 1, "----------------------------------------"); do { ProcessKeyboard(); /* bool anyRcvdMsg = */ ProcessSerialReceive(); ProcessSerialQueue(); ProcessWindowsMessage(); } while (!UserWantsToExitCanSniff); DetachSerialPort(); } else { printf("Failed to attach to Serial Port on COM%i\n", avrOnPort); } return(0); } void SetData(uint8_t *targ, const uint8_t *string, size_t len = 0) { uint8_t *p = (uint8_t *)string; if (len == 0) len = strlen((const char *)string); memcpy(targ, p, len); } // Dec2Hex // // Convert a number to a hex-ascii string // const uint8_t *Dec2Hex(uint32_t val, uint16_t numDigits) { static uint8_t buf[16]; sprintf_s((char *)buf, 16, "%0*X", numDigits, val); return buf; } // Hex2Dec // // All responses are pretty much Hex-ASCII, so // we sometimes want to convert it to decimal // This takes a buffer and converts the specified // number of characters. // unsigned long Hex2Dec(uint8_t *p, int dig) { unsigned long x = 0; while (dig--) { if (*p >= '0' && *p <= '9') x = x * 16 + *p - '0'; else if (*p >= 'a' && *p <= 'f') x = x * 16 + 0x0a + *p - 'a'; else if (*p >= 'A' && *p <= 'F') x = x * 16 + 0x0a + *p - 'A'; p++; } return x; } bool CheckTheChecksum(uint8_t *szBuffer, uint32_t num) { uint8_t sum = 0; for (uint16_t i = 1; i < num - 3; i++) { sum += szBuffer[i]; } uint8_t cksum = (uint8_t)Hex2Dec(&szBuffer[num - 3], 2); //printf("CheckSum: %02X v. %c%c\n", sum, szBuffer[num - 3], szBuffer[num - 2]); return (sum == cksum); } // ProcessResponse // // @param[in] szBuffer is the received message // @param[in] len is the null terminated string length of the message // // Typical Message: // '\x02' 'type' 'guard' 'rcmd0' 'rcmd1' 'rdat0' 'rdat1' '\x03' // [0] [1] [2] [3] [4] [5] [6] [7] // void ProcessResponse(uint8_t *szBuffer, uint32_t len) { // These don't have a checksum ... // Example: [02] 4026 66[03] // 4 - controlled by encoder // 0 - guard status 0 = no guard // 26 - master vol // 66 - volume setting (-54.6db) uint8_t type = (uint8_t)Hex2Dec(&szBuffer[1], 1); uint8_t guard = (uint8_t)Hex2Dec(&szBuffer[2], 1); uint8_t rcmd = (uint8_t)Hex2Dec(&szBuffer[3], 2); uint8_t rdat = (uint8_t)Hex2Dec(&szBuffer[5], 2); char buf[MAXTEXTLEN]; (void)len; // not used bool found = false; for (int i = 0; i < sizeof(MessageHandlers) / sizeof(MessageHandlers[0]); i++) { if (MessageHandlers[i].rCmd == rcmd) { found = true; if (MessageHandlers[i].numToTransfer == 1) { memcpy(&avrStatus.config.DT0 + MessageHandlers[i].configOffset, &szBuffer[6], 1); } if (MessageHandlers[i].numToTransfer == 2) { memcpy(&avrStatus.config.DT0 + MessageHandlers[i].configOffset, &szBuffer[5], 2); } if (MessageHandlers[i].fncHelper) { (*MessageHandlers[i].fncHelper)(rcmd, rdat, szBuffer, len); } if (MessageHandlers[i].showAll) { ShowAllStatusInfo(); } } } Console_SetCursor(0, -1); if (!found) { sprintf_s(buf, MAXTEXTLEN, "type: %X, guard: %X, cmd: %02X, data: %02X", type, guard, rcmd, rdat); Console_Write(buf); Console_ScrollBottomRegion(scrollTop, scrollBot); } switch (rcmd) { case 0x20: // Power status // SetData(&avrStatus.config.DT8, Dec2Hex(rdat, 1)); // sprintf_s(buf, MAXTEXTLEN, "Zone Power: %s\n", ZonePower(rdat)); // Console_Write(buf); // Console_ScrollBottomRegion(scrollTop, scrollBot); // ShowAllStatusInfo(); // break; case 0x23: // Mute // SetData(&avrStatus.config.DT12, Dec2Hex(rdat, 1)); // sprintf_s(buf, MAXTEXTLEN, "Mute: %s\n", OnOffText(rdat)); // Console_Write(buf); // Console_ScrollBottomRegion(scrollTop, scrollBot); // ShowAllStatusInfo(); // break; case 0x25: // Zone 2 Mute // SetData(&avrStatus.config.DT14, Dec2Hex(rdat, 1)); // sprintf_s(buf, MAXTEXTLEN, "Zone 2 Mute: %s\n", OnOffText(rdat)); // Console_Write(buf); // Console_ScrollBottomRegion(scrollTop, scrollBot); // ShowAllStatusInfo(); // break; case 0x26: // Master Volume // RxMasterVolStatus(rcmd, rdat, szBuffer, len); // SetData(&avrStatus.config.DT15, Dec2Hex(rdat, 2)); // sprintf_s(buf, MAXTEXTLEN, "Vol: %s\n", VolumeDB(rdat)); // Console_Write(buf); // Console_ScrollBottomRegion(scrollTop, scrollBot); // ShowAllStatusInfo(); // break; case 0x28: // Program // SetData(&avrStatus.config.DT19, Dec2Hex(rdat, 2)); // sprintf_s(buf, MAXTEXTLEN, "Prog: %s\n", ProgramName(rdat)); // Console_Write(buf); // Console_ScrollBottomRegion(scrollTop, scrollBot); // ShowAllStatusInfo(); break; case 0x82: // Night Mode // SetData(&avrStatus.config.DT27, Dec2Hex(rdat, 1)); // sprintf_s(buf, MAXTEXTLEN, "Night Mode: %s\n", OnOffText(rdat)); // Console_Write(buf); // Console_ScrollBottomRegion(scrollTop, scrollBot); // ShowAllStatusInfo(); break; case 0x36: // DC1 Trigger // SetData(&avrStatus.config.DT39, Dec2Hex(rdat, 1)); // sprintf_s(buf, MAXTEXTLEN, "Trigger Out 1: %s\n", OnOffText(rdat)); // Console_Write(buf); // Console_ScrollBottomRegion(scrollTop, scrollBot); // ShowAllStatusInfo(); break; case 0x3C: // DC2 Trigger // SetData(&avrStatus.config.DT45, Dec2Hex(rdat, 1)); // sprintf_s(buf, MAXTEXTLEN, "Trigger Out 2: %s\n", OnOffText(rdat)); // Console_Write(buf); // Console_ScrollBottomRegion(scrollTop, scrollBot); // ShowAllStatusInfo(); break; case 0xA1: // Zone 3 Mute // //SetData(&avrStatus.config.DT14, Dec2Hex(rdat, 1)); // sprintf_s(buf, MAXTEXTLEN, "Zone 3 Mute: %s\n", OnOffText(rdat)); // Console_Write(buf); // Console_ScrollBottomRegion(scrollTop, scrollBot); // ShowAllStatusInfo(); break; default: // Everything not yet handled // sprintf_s(buf, MAXTEXTLEN, "type: %X, guard: %X, cmd: %02X, data: %02X", type, guard, rcmd, rdat); // Console_Write(buf); // Console_ScrollBottomRegion(scrollTop, scrollBot); break; } } // AnalyzeResponse // // Given a response string, typically of the form: // [11] .... [03] // [12] .... [03] // ... etc // // void AnalyzeResponse(uint8_t *szBuffer, uint32_t num) { switch (szBuffer[0]) { case 0x02: // STX ProcessResponse(szBuffer, num); break; case 0x11: // DC1 break; case 0x12: // DC2 if (CheckTheChecksum(szBuffer, num)) { if (num == 21) { memcpy(&avrStatus.header, &szBuffer[1], sizeof(AVR_StatusHeader_T)); num = Hex2Dec(&avrStatus.header.length[0], 2); memcpy(&avrStatus.config.DT0, &szBuffer[9], num); // Copy bits of the config avrStatus.headerValid = true; } else if (num == 150) { memcpy(&avrStatus.header, &szBuffer[1], sizeof(AVR_StatusHeader_T)); num = Hex2Dec(&avrStatus.header.length[0], 2); memcpy(&avrStatus.config.DT0, &szBuffer[9], num); // Copy the config //memcpy(&avrStatus.header, &szBuffer[1], sizeof(AVR_StatusHeader_T) + sizeof(AVR_Configuration_T)); avrStatus.headerValid = true; avrStatus.configValid = true; } else { printf("***** Received message of unexpected length [%u]\n", num); } ShowAllStatusInfo(); } else { printf("Checksum failure on Status Header\n"); } //PrintConfiguration(szBuffer); break; case 0x14: // DC4 Extended Response //DecodeExtended(szBuffer); // Decode Extended response break; case '0': //rcmd = Hex2Dec(&szBuffer[0], 2); //DecodeString(rcmd, &szBuffer[2]); break; default: //printf("[%d] %s\n", i, szBuffer); break; } } // ProcessSerialReceive // // All character sequences end with \x03 (this is the equiv of a ) // // bool ProcessSerialReceive() { bool anythingReceived = false; uint8_t rcv_buff[MAXTEXTLEN] = { 0 }; static uint8_t messageBuf[MAXTEXTLEN] = { 0 }; static uint8_t *p = messageBuf; // used to fill the rcv_buff as data comes in uint32_t num = avr.Read(rcv_buff, MAXTEXTLEN); if (num) { for (uint32_t i = 0; i < num; i++) { *p = rcv_buff[i]; if (*p++ == '\x03') { // End of message *p = '\0'; // null terminate it EchoSerialRecv(messageBuf); // // @TODO Now do something with the received message // AnalyzeResponse(messageBuf, (uint32_t)strlen((char *)messageBuf)); p = messageBuf; // Reset the buffer for the next message, which might be in rcv_buff *p = '\0'; anythingReceived = true; } } if (messageBuf[0]) { //EmitBuffer("~", messageBuf, 0, true); // Show them the partial receipt if anything is there } } return anythingReceived; } void EnumerateComPorts() { printf("Com Port Scan:\n"); int foundPorts = 0; for (int pNum = COM_MIN_PORT; pNum < COM_MAX_PORT; pNum++) { char cBuf[20]; // generously sized sprintf_s(cBuf, sizeof(cBuf), "\\\\.\\COM%d", pNum); bool portFound = false; HANDLE port = CreateFile(cBuf, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); if (port == INVALID_HANDLE_VALUE) { DWORD dwError = GetLastError(); if ((dwError == ERROR_ACCESS_DENIED) || (dwError == ERROR_GEN_FAILURE) || (dwError == ERROR_SHARING_VIOLATION) || (dwError == ERROR_SEM_TIMEOUT)) { foundPorts++; portFound = true; } } else { foundPorts++; portFound = true; CloseHandle(port); } if (portFound) { printf(" Com Port %d found.\n", pNum); } } if (foundPorts == 0) { printf(" No Com Ports found, perhaps you need to plug in an adapter?\n"); } } int DetachSerialPort() { if (avr.IsOpen()) { avr.Close(); } return true; } int AttachToSerialPort() { bool success = false; if (avrOnPort >= COM_MIN_PORT && avrOnPort <= COM_MAX_PORT) { char buf[20]; // generously sized. sprintf_s(buf, sizeof(buf), "\\\\.\\COM%d", avrOnPort); DWORD Access = GENERIC_WRITE | GENERIC_READ; if (avr.Open(buf, avrBaud, 8, NOPARITY, ONESTOPBIT, Access)) { success = true; avr.Set_RTS_State(false); Sleep(250); avr.Set_RTS_State(true); } } return success; } void Console_Init() { hStdin = GetStdHandle(STD_INPUT_HANDLE); hStdout = GetStdHandle(STD_OUTPUT_HANDLE); if (!GetConsoleScreenBufferInfo(hStdout, &csbi)) { fprintf(stderr, "Error: Unable to get console buffer info. Code: %lu\n", GetLastError()); exit(1); } // Desired size (width x height) COORD newSize; newSize.X = consoleWidth; // columns newSize.Y = consoleHeight; // rows // Step 1: Shrink window if needed before shrinking buffer SMALL_RECT tempWindow = csbi.srWindow; tempWindow.Right = tempWindow.Left + (newSize.X - 1); tempWindow.Bottom = tempWindow.Top + (newSize.Y - 1); if (!SetConsoleWindowInfo(hStdout, TRUE, &tempWindow)) { fprintf(stderr, "Error: Unable to temporarily resize window. Code: %lu\n", GetLastError()); exit(1); } // Step 2: Set new buffer size if (!SetConsoleScreenBufferSize(hStdout, newSize)) { fprintf(stderr, "Error: Unable to set console buffer size. Code: %lu\n", GetLastError()); exit(1); } // Step 3: Set final window size to match buffer SMALL_RECT newWindow; newWindow.Left = 0; newWindow.Top = 0; newWindow.Right = newSize.X - 1; newWindow.Bottom = newSize.Y - 1; if (!SetConsoleWindowInfo(hStdout, TRUE, &newWindow)) { fprintf(stderr, "Error: Unable to set console window size. Code: %lu\n", GetLastError()); exit(1); } // end wOldColorAttrs = csbi.wAttributes; //SetConsoleTextAttribute(hStdout, FOREGROUND_RED | FOREGROUND_INTENSITY); } void Console_Close() { SetConsoleTextAttribute(hStdout, wOldColorAttrs); } void Console_Cls() { COORD coordScreen = { 0, 0 }; // home for the cursor DWORD cCharsWritten; DWORD dwConSize; // Get the number of character cells in the current buffer. if (!GetConsoleScreenBufferInfo(hStdout, &csbi)) { return; } dwConSize = csbi.dwSize.X * csbi.dwSize.Y; // Fill the entire screen with blanks. if (!FillConsoleOutputCharacter(hStdout, // Handle to console screen buffer (TCHAR)' ', // Character to write to the buffer dwConSize, // Number of cells to write coordScreen, // Coordinates of first cell &cCharsWritten)) { // Receive number of characters written return; } // Get the current text attribute. if (!GetConsoleScreenBufferInfo(hStdout, &csbi)) { return; } // Set the buffer's attributes accordingly. if (!FillConsoleOutputAttribute(hStdout, // Handle to console screen buffer csbi.wAttributes, // Character attributes to use dwConSize, // Number of cells to set attribute coordScreen, // Coordinates of first cell &cCharsWritten)) { // Receive number of characters written return; } // Put the cursor at its home coordinates. SetConsoleCursorPosition(hStdout, coordScreen); } void Console_Write(const char *text) { DWORD written; GetConsoleScreenBufferInfo(hStdout, &csbi); if (csbi.dwCursorPosition.Y >= (csbi.srWindow.Bottom - consoleScrollHeight)) { //Console_ScrollBottomRegion(scrollTop, scrollBot); } WriteConsole(hStdout, text, (DWORD)strlen(text), &written, nullptr); } void Console_WriteAt(short x, short y, const char *text) { Console_SetCursor(x, y); DWORD written; WriteConsole(hStdout, text, (DWORD)strlen(text), &written, nullptr); } void Console_SetCursor(short x, short y) { GetConsoleScreenBufferInfo(hStdout, &csbi); csbi.dwCursorPosition.X = (short)((x < 0) ? csbi.srWindow.Right + x : x); csbi.dwCursorPosition.Y = (short)((y < 0) ? csbi.srWindow.Bottom + y : y); SetConsoleCursorPosition(hStdout, csbi.dwCursorPosition); } void Console_ScrollBottomRegion(short topLine, short bottomLine) { GetConsoleScreenBufferInfo(hStdout, &csbi); if (topLine < 0) topLine = csbi.srWindow.Bottom + topLine; if (bottomLine < 0) bottomLine = csbi.srWindow.Bottom + bottomLine; SMALL_RECT scrollRect = { 0, topLine, csbi.dwMaximumWindowSize.X-1, bottomLine }; COORD dest = { 0, topLine - 1 }; CHAR_INFO fill = { ' ', { FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE } }; ScrollConsoleScreenBuffer(hStdout, &scrollRect, &scrollRect, dest, &fill); putch('\r'); // Cursor to left margin } bool Console_AdvanceToNextLineIfNotRoomFor(short x, int scroll) { bool advanced = false; GetConsoleScreenBufferInfo(hStdout, &csbi); if (csbi.dwCursorPosition.X + x > csbi.dwMaximumWindowSize.X) { advanced = true; if (scroll == 1) { Console_ScrollBottomRegion(scrollTop, scrollBot); } else { csbi.dwCursorPosition.Y++; } Console_SetCursor(0, csbi.dwCursorPosition.Y); } return advanced; } // Console_SetCursorVisibility // // Set the state of the cursor and provide the prior state so it // can be restored // // @param[in] bool the desired state, true is visible // @returns the prior state, true is visible // bool Console_SetCursorVisibility(bool visible) { CONSOLE_CURSOR_INFO cursorInfo; bool priorState = true; if (!GetConsoleCursorInfo(hStdout, &cursorInfo)) { // printf("***** Error: Unable to get cursor info.\n"); return priorState; } priorState = cursorInfo.bVisible; cursorInfo.bVisible = visible; // true = show, false = hide if (!SetConsoleCursorInfo(hStdout, &cursorInfo)) { // std::cerr << "Error: Unable to set cursor info.\n"; } return priorState; }