Tuesday, March 7, 2023

Diary #5 C++ Hex Encoder/Decoder

As part of my Websocket Controller (over-network keyboard input forwarder), I wanted to send the clipboard from the client to the server. To do this with non-ASCII chars, I encoded them as hex. This is copy-pasted from the changes I made this morning, to enable the clipboard to send unicode long strings (wstring), as opposed to the previous single-byte std:::strings.

 (needs #include <string>)
const char *hexChars = "0123456789ABCDEF";
string encodeHex(std::wstring wstr){ // because we can only use std::string as an arg for websocketpp's send(). Encode wstring as hex.
    string o = "";
    cout << "wstring has length: " << wstr.length() << endl;
    for (int xint = 0, len = wstr.length(); xint < len; xint++){
        // high order byte of wchar_t
        unsigned char c1 = (wstr[xint] & 65280) >> 8;
        unsigned char c1_left = hexChars[c1 / 16];
        unsigned char c1_right = hexChars[c1 % 16];
        // low order byte of wchar_t
        unsigned char c2 = wstr[xint] & 255;
        unsigned char c2_left = hexChars[c2 / 16];
        unsigned char c2_right = hexChars[c2 % 16];
        o += c1_left;
        o += c1_right;
        o += c2_left;
        o += c2_right;
    }
    cout << "Finished encoded hex: " << o << endl;
    cout << "It has length: " << o.length() << endl;
    return o;
}

wstring decodeHex(std::string str){ // turn (uppercase) hex string back into wstring
    wstring o = L"";
    cout << "decoding from hex: " << str << endl;
    cout << "hex str len: " << str.length() << endl;
    int len = str.length();
    if ((len & 1) == 1) return L"[Odd num of chars, not hex]";
    if ((len % 4) != 0) return L"[Length wrong for wstring, can't de-hex]"; // need 4 hex letters per wchar_t
    for (int xint = 0; xint < len; xint++){
        unsigned char HI_left; // high order byte of wchar_t, left hex char
        unsigned char HI_right;
        unsigned char LO_left; // low order byte of wchar_t, left hex char
        unsigned char LO_right;

        // char 1, hex 1
        if (str[xint] >= 'A' && str[xint] <= 'F'){
            HI_left = 10 + (str[xint++] - 'A');
        }
        else if (str[xint] >= '0' && str[xint] <= '9'){
            HI_left = str[xint++] - '0';
        }
        else{
            return L"[Char invalid, not hex]"; // only accepting capital A-F & 0-9 as hex
        }

        // char 1, hex 2
        if (str[xint] >= 'A' && str[xint] <= 'F'){
            HI_right = 10 + (str[xint++] - 'A');
        }
        else if (str[xint] >= '0' && str[xint] <= '9'){
            HI_right = str[xint++] - '0';
        }
        else{
            return L"[Char invalid, not hex]";
        }

        // char 2, hex 1
        if (str[xint] >= 'A' && str[xint] <= 'F'){
            LO_left = 10 + (str[xint++] - 'A');
        }
        else if (str[xint] >= '0' && str[xint] <= '9'){
            LO_left = str[xint++] - '0';
        }
        else{
            return L"[Char invalid, not hex]";
        }

        // char 2, hex 2
        if (str[xint] >= 'A' && str[xint] <= 'F'){
            LO_right = 10 + (str[xint] - 'A');
        }
        else if (str[xint] >= '0' && str[xint] <= '9'){
            LO_right = str[xint] - '0';
        }
        else{
            return L"[Char invalid, not hex]";
        }


        wchar_t fin = ((HI_left * 16) + HI_right) << 8; // assemble high order byte
        fin += (LO_left * 16) + LO_right; // assemble low order byte
        wcout << L"___constructed char " << fin << endl;
        cout << "decode progress: " << xint << " / " << len << endl;
        o += fin; // add decoded wchar_t to wstring
    }
    wcout << L"finished wstring: " << o << endl;
    return o;
}

 

 

(needs #include <string> and #include <windows.h>)
/*
void toClipboard(string new_text){
    OpenClipboard(0);
    EmptyClipboard();
    HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, new_text.size() + 1);
    if (!hg){
        CloseClipboard();
        return;
    }
    memcpy(GlobalLock(hg), new_text.c_str(), new_text.size() + 1);
    GlobalUnlock(hg);
    SetClipboardData(CF_TEXT, hg);
    CloseClipboard();
    GlobalFree(hg);
}
legacy ASCII version
*/

void toClipboard(wstring new_text){
    OpenClipboard(0);
    EmptyClipboard();
    HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, (new_text.size() * 2) + 2); // 2 bytes per char, plus 2-byte null terminating char
    if (!hg){
        CloseClipboard();
        return;
    }
    memcpy(GlobalLock(hg), new_text.c_str(), (new_text.size() * 2) + 2);
    GlobalUnlock(hg);
    SetClipboardData(CF_UNICODETEXT, hg);
    CloseClipboard();
    GlobalFree(hg);
}

/*string fromClipboard(){
    string out = "";
    if (OpenClipboard(nullptr)){
        HANDLE hData = GetClipboardData(CF_TEXT); // CF_UNICODETEXT will only put 1 char into the char *pszText, CF_TEXT will work normally
        if (hData != nullptr){
            char *pszText = static_cast<char*>(GlobalLock(hData));
            cout << "pszText = " << pszText << endl;
            if (pszText != nullptr){
                out = pszText;
            }
            else{
                out = "pszText = nullptr";
            }
            GlobalUnlock(hData);
        }
        else{
            out = "hData = nullptr";
        }
        CloseClipboard();
    }
    else{
        out = "failed to open clipboard";
    }
    return out;
}
// legacy ASCII version
*/

Monday, March 6, 2023

Coding Challenge #16 Distance to nearest vowel

 Distance to nearest Vowel (Edabit)
Given a string, return an array with the distance to the nearest vowel for each character. It searches for the nearest both forwards and backwards.

Example: 'abc' -> [0,1,2]

'abzxciou' -> [0,1,1,2,2,0,0,0]

We'll force lowercase and make non-letter characters return "N/A" in the array.

function doit(str){
    var o = [];
    str = str.toLowerCase();
    
    for(var xint = 0, len = str.length;xint < len;xint++){
        var c = str.charCodeAt(xint);
        if(c >= aCode && c <= zCode){ // is a letter
            // we'll just manually check by looping. use modulo to contain value.
            var leastVowelDistance = 26;
            var forwardsVowelDistance = 26;
            var backwardsVowelDistance = 26;
            var cOffset = c - aCode;
            for(var yint=0;yint<25;yint++){ // forward
                var xchar = alphabet[(cOffset + yint) % 26]; // for upward
                if(xchar == 'a' || xchar == 'e' || xchar == 'i' || xchar == 'o' || xchar == 'u'){
                    forwardsVowelDistance = yint;
                    break;
                }
            }
           
            for(var yint = 0;yint < 25;yint++){ // backwards
                var xidx = cOffset - yint + (yint > cOffset ? 26 : 0);
                var xchar = alphabet[xidx];
               
                if(xchar == 'a' || xchar == 'e' || xchar == 'i' || xchar == 'o' || xchar == 'u'){
                    backwardsVowelDistance = yint;
                    break;
                }
            }
           
            leastVowelDistance = Math.min(forwardsVowelDistance,backwardsVowelDistance);
            o.push(leastVowelDistance);
           
            // debug info, shows the distance forwards and backwards for each letter
            //console.log("Letter = " + alphabet[cOffset] + ", forwardsVowelDistance = " + forwardsVowelDistance + ", backwardsVowelDistance = " + backwardsVowelDistance + ", leastVowelDistance = " + leastVowelDistance);
        }else{
            o.push('N/A'); // not a letter
        }
    }
    
    return o;
}
// note: we're not using 'y' as a vowel here
var vowels = ['a'.charCodeAt(0),'e'.charCodeAt(0),'i'.charCodeAt(0),'o'.charCodeAt(0),'u'.charCodeAt(0)];
var alphabet = [];
var aCode = 'a'.charCodeAt(0);
var zCode = 'z'.charCodeAt(0);
for(var xint=0;xint<26;xint++)alphabet.push(String.fromCharCode(aCode+xint));
function test(str){
    if(str == undefined){ // make random string
        var len = 5;
        str = "";
        var range = zCode - aCode;
        for(var xint = 0;xint < len;xint++)
            str += String.fromCharCode( Math.round(Math.random()*range) + aCode);
    }
    console.log('str: ' + str + '. result: ' + doit(str).toString());
}



test('abc');
test('xyz');
test('cabbage');
test('Spider Man');
test('$ #@ ^*$@ #*');
test('AaAaHhHh');
test('그거양은 체친구입니다.');
for(var xint = 0;xint < 5;xint++) test();

Sunday, March 5, 2023

Coding Challenge #15 - Super Int (u128 JavaScript)

Super Int! (based off of Edabit "Big Integers, Big Errors")

Today we'll create a 128-bit unsigned integer and make some basic operators for it.


Saturday, March 4, 2023

Coding Challenge #14

 Edabit: Jake's Meal Time

// Jake's Meal Time
// Jake eats breakfast at 7am, lunch at 12pm, and dinner at 7pm.
// Using the current time, calculate hours and minutes until Jake's next meal, and specify which meal that is.
// We act like current time's seconds is always 0. We'll calculate to the hour before and then add in the minutes left (if minutes left would be 60, we change minutes left to 0 and add one more 'hour until')

function mealTicket(time){
    // input
    var date = new Date(time);
    var hour = date.getHours();
    var min = date.getMinutes();
    
    // output
    var mealName = "N/A";
    var hoursTil = 0;
    var minsTil = 0;
    
    // logic
    if(hour < 7 || hour >= 19){ // calc for breaky-boo
        mealName = "breakfast";
        if(hour >= 19){
            hoursTil = 24-hour;
            hour = 0;
        }
        hoursTil += 6 - hour;
       
        if(min == 0) hoursTil += 1;
        else minsTil = 60 - min;
    }else if(hour < 12){ // calc for lunch
        mealName = "lunch";
        hoursTil += 11 - hour;
       
        if(min == 0) hoursTil += 1;
        else minsTil = 60 - min;
    }else if(hour < 19){
        mealName = "dinner";
        hoursTil += 18 - hour;
       
        if(min == 0) hoursTil += 1;
        else minsTil = 60 - min;
    }
    
    console.log("Jake's next meal is " + mealName + " in " + hoursTil + " hours, " + minsTil + " minutes. His next meal ticket is for the time: " + date);
}

function test(){ // generate random date
    var n = Math.floor(Math.random() * 4) + 1;
    var op = Math.floor(Math.random() * 4);
    var nowDate = (new Date()).valueOf();
    if(op == 0) mealTicket(nowDate + (n * 1000000));
    else if(op == 1) mealTicket(nowDate - (n * 1000000));
    else if(op == 2) mealTicket(nowDate * n);
    else if(op == 3) mealTicket(nowDate / n);
    else mealTicket(nowDate);
}

mealTicket((new Date()).valueOf());
test();
test();
test();
test();
test();

Friday, March 3, 2023

Coding Challenge #13

Edabit: Return the sum of two numbers

// from the 'Very Easy' page:
simple_sum = (a,b)=>{return a+b};


Now let's take it up one notch by returning a new function that hard-codedly adds the two arguments.

This is just for fun to explore some more of JavaScript.

function make_adder(a,b){
    var fnStr = "function adder(){return " + a + " + " + b + "}";
    window.eval(fnStr);
    try{
        console.log('Using created function, ' + a + ' + ' + b + ' = ' + window['adder']());
    }catch{
        console.log('failed to call function');
    }
}
make_adder(1,2) // expecting 3
make_adder(1,20) // 21
make_adder(13,17) // 30
make_adder(99,1) // 100

interestingly, just eval(fnStr) doesn't work ( i guess because of the function's scope?), but window.eval(fnStr) works as expected. 

Thursday, March 2, 2023

Coding Challenge #12

Daily Coding Problem #13


 // Daily Coding Problem #13
// With integer dLimit and string str, return the longest substring of str that contains at most dLimit distinct characters
// (so the goal is to look for letter duplication sections, they score higher)
// example: dLimit=2, 'aasdf': 'aas' has 2 distinct chars 'a' and 's', while being longest
// example 2: dLimit=2, 'hoooow?' : 'hoooo' or 'oooow', either.
// example 3: dLimit=3, 'hoooow?' : 'hoooow' or 'oooow?', either
function checkStr(str, dLimit){
    var ostr = "";
    for(var xint = 0, len = str.length;xint < len;xint++){ // check each char as a new sequence
        var distinctLeft = dLimit;
        var streakChars = [];
        var tmpStr = "";
        for(var yint = xint;yint < len;yint++){ // go through the sequence and find longest substring with dLimit distinct chars
            var inStreak = false;
            for(var zint = 0, zlen = streakChars.length;zint < zlen;zint++){ // check streakChars to see whether this one is distinct
                var candidate = str.charAt(yint);
                if(streakChars[zint] == candidate){
                    inStreak = true;
                    break;
                }
            }
            
            if(inStreak){ // already in streakChars
                tmpStr += str.charAt(yint);
            }else{ // new char in streak
                distinctLeft--;
                if(distinctLeft < 0) // this char would be too many for dLimit
                    break;
                streakChars.push(str.charAt(yint));
                tmpStr += str.charAt(yint);
            }
        }
        if(tmpStr.length > ostr.length) ostr = tmpStr; // update longest substring found within dLimit distinct chars
    }
    return ostr;
}

function test(str, dLimit){
    console.log('dLimit = ' + dLimit + ', string = \'' + str + '\'. Result: \'' + checkStr(str, dLimit) + '\'');
}
function testRan(){ // generate random strings to test
    var dLimit = Math.floor(Math.random() * 2) + 2; // dLimit will be [2,4] inclusive
    var strLen = Math.floor(Math.random() * 5) + 5; // string length will be [5,10] inclusive
    var aCode = 'a'.charCodeAt(0);
    var testStr = "";
    for(var xint = 0;xint < strLen;xint++) // generate random letters [a,f]
        testStr += String.fromCharCode(aCode + Math.floor(Math.random()*5));
    
    test(testStr, dLimit);
}

test('hahaf', 2);
test('hahaf', 3);
test('doodle', 2);
test('doodle', 3);
test('manhandle', 3);
test('bazooka', 3);
for(var rint = 0;rint < 5;rint++) testRan();

Wednesday, March 1, 2023

Diary #4 LNK 1169, LNK 2005, EnumWindows Example

Working on a simple menu that looks like the PS4's menu. I got LNK 1169 and LNK 2005, and quickly found the cause/fix:

Why does LNK 1169 (and consequently LNK 2005) happen?

It can be a simple code error like 2 different .cpp files each declaring 'int a;', or it could be a project error where file A and B are both 'Source Files', and file A #include's file B.

My case was B. I'd like to quickly log it for myself to help identify and fix it in the future if it escapes me at that time.

The error in the Error List:

Both files test_app.cpp and ps4_menu_sim.cpp are 'Source Files'
 
The file test_app.cpp including ps4_menu_sim.cpp:


 

The fix: right click the file that is included by the other file in Solution Explorer, and click Remove (then click "Remove", and NOT "Delete" when it asks you whether you want to remove it from the project or permanently delete the file).

By my understanding, all 'Source Files' are included by default and it's only in the 'Source Files' that these double declarations mainly occur? Because multiple files can include other dependency files, such as string.h or iostream.h without error. Guess 'Source Files' is best used for OOP classes and globals.

 To avoid the error in the future: instead of right click -> Add on the Source Files tab, right click -> Add on the Resource Files tab. Or just don't add it and it would also be included perfectly well.

 

Also,  an example of EnumWindows, which needs EnumWindowProc passed to it as the first arg. This example stores the found window handles into a windowEnumerator object. More than the visible windows are found by this method (all 'top-level' windows are found by EnumWindows). EnumWindowProc needs to return false to stop enumerating... I'm not really sure how make it return true until I've enumerated enough without checking for a particular window in EnumWindowProc or using global variables top stop after enumerating a certain amount. Since this code returns true indefinitely, it is therefore unusable. Oh well.

#include <iostream>

#include <Windows.h>

#include <string>

class windowEnumerator{
    int len = 2; // indexes available
public:
    HWND *wins = new HWND[2];
    int used = 0; // indexes used/filled
    windowEnumerator(){}
    ~windowEnumerator(){
        delete[] wins;
    }
    bool expand(){
        len *= 2; // double array size
        HWND *newAry = new HWND[len];
        if (!newAry) return false; // couldn't allocate to heap
        for (int xint = 0; xint < used; xint++) // copy over info from old, small array to new array
            newAry[xint] = wins[xint];
        delete[] wins; // free space allocated for old array
        wins = newAry; // make wins point to new array
        return true;
    }
    void add(HWND hwnd){
        if (used >= len && !expand()) return;
        wins[used] = hwnd;
        used++;
    }
    HWND get(int index){
        return (index < used ? wins[index] : nullptr);
    }
    void clear(){
        for (int xint = 0; xint < used; xint++){
            wins[xint] = nullptr;
        }
        used = 0;
    }
}winEnum;
BOOL CALLBACK EnumWindowProc(HWND hwnd, LPARAM lParam){
    winEnum.add(hwnd);
    return true;
}

int main(){
    // let's try something new: enumerate open windows
    if (EnumWindows(EnumWindowProc, NULL)){
        cout << "Successfully enumerated windows this way." << endl;
        cout << "Count of windows found: " << winEnum.used << endl;
        for (int xint = 0; xint < winEnum.used; xint++){
            // get window title bar name
            const int bufferSize = 1024;
            char textBuffer[bufferSize] = "";
            SendMessageA(winEnum.wins[xint], WM_GETTEXT, (WPARAM)bufferSize, (LPARAM)textBuffer);
            std::string str(textBuffer);

            cout << "[ " << xint << " ] ( " << (int)winEnum.wins[xint] << " ): " << str << endl;
        }
    }
    else{
        cout << "Failed to enumerate windows this way." << endl;
    }
    cout << "enter to quit" << endl;
    system("pause");
    //ps4_menu_sim::startup();
    //ps4_menu_sim::main();
    //ps4_menu_sim::shutdown

return 0;

}

Coding Challenge #54 C++ int to std::string (no stringstream or to_string())

Gets a string from an integer (ejemplo gratis: 123 -> "123") Wanted to come up with my own function for this like 10 years ago ...