/*
 * ************************************************************************************************************
 * Author: Marcin Załęczny
 * Copyright (C)2010 Marcin Załęczny <mzaleczny@hotmail.com>
 * License: The GNU Lesser General Public License, version 3.0 (LGPLv3)
 * Library Name: mzstd
 * Version: 1.07
 * First release date:   2010-11-08
 * Current release date: 2011-03-04
 *
 *
 * This file is part of mzstd library
 * 
 * mzstd library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * mzstd library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * ************************************************************************************************************
 */

#include "mzMemoryView.h"

CMemoryView::CMemoryView(LPVOID pointer, DWORD size, DWORD offset) {
    this->setRealClassName(L"CMemoryView");
    this->setMemory(pointer, size);
    this->_offset = offset;
}

CMemoryView::~CMemoryView() {
}

VOID CMemoryView::setMemory(LPVOID pointer, DWORD size) {
    this->_pointer = pointer;
    this->_size = size;
}

CString CMemoryView::getStringOfBytes(BOOL withOffsets, BOOL withASCII) {
    CString result;
    CString line;
    CString ascii;
    CString indentation = L"  ";
    CString spacing = L"    ";
    CString asciiIndentation = indentation.substr(0, indentation.length()/2);
    
    ULONG count = this->_size;
    BYTE* ptr = (BYTE*)this->_pointer;
    UCHAR ch;
    DWORD currentOffset = this->_offset;
    line = COMMON_EMPTY_STRING;
    for (LONG i = 0; i < count; i++) {
        line += FormatHexValue(ptr[i], FORMAT_ALIGN_LEFT, L'0', 2, FALSE, FALSE, TRUE, 2);
        if (withASCII) {
            ch = (UCHAR)ptr[i];
            if (ch < 32 || ch > 127) {
                ch = '.';
            }
            ascii += ch;
        }
        if ((i+1)%16 > 0) {
            if ((i+1)%8 == 0) {
                line += indentation + indentation;
                if (withASCII) {
                    ascii += asciiIndentation;
                }
            } else {
                line += indentation;
            }
        } else {
            if (withASCII) {
                line += spacing + ascii;
                ascii = COMMON_EMPTY_STRING;
            }
            if (withOffsets) {
                line = FormatHexValue(currentOffset, FORMAT_ALIGN_LEFT, L'0', 8, FALSE, FALSE, TRUE, 8) + L":  " + line;
                currentOffset += 16;
            }
            result += line + L"\n";
            line = COMMON_EMPTY_STRING;
        }
    }
    
    if (line != COMMON_EMPTY_STRING) {
        if (withASCII) {
            // we must pad line with spaces
            LONG padCount = 16 - this->_size % 16;
            for (LONG i = 0; i < padCount-1; i++) {
                line += CString(L"  ") + indentation;
            }
            if (16-padCount < 8) {
                line += indentation + CString(L"  ");
            } else if (padCount == 8) {
                line += indentation;
            } else {
                line += CString(L"  ");
            }
            line += spacing + ascii;
            ascii = COMMON_EMPTY_STRING;
        }
        if (withOffsets) {
            line = FormatHexValue(currentOffset, FORMAT_ALIGN_LEFT, L'0', 8, FALSE, FALSE, TRUE, 8) + L":  " + line;
            currentOffset += 16;
        }
        result += line;
    }
    
    result.rtrim();
    return result;
}

CString CMemoryView::getStringOfWords(BOOL withOffsets, BOOL withASCII) {
    CString result;
    CString ascii;
    CString indentation = L"  ";
    CString spacing = L"    ";
    CString asciiIndentation = indentation.substr(0, indentation.length()/2);
    
    ULONG count = this->_size / sizeof(WORD);
    WORD* ptr = (WORD*)this->_pointer;
    BYTE* ptrb;
    UCHAR ch;
    for (LONG i = 0; i < count; i++) {
        result += FormatHexValue(ptr[i], FORMAT_ALIGN_LEFT, L'0', 4, FALSE, FALSE, TRUE, 4);
        if (withASCII) {
            ptrb = (BYTE*)&ptr[i];
            ch = (UCHAR)ptrb[i];
            if (ch == 0 || ch > 127) {
                ch = '.';
            }
            ascii += ch;
            
            ch = (UCHAR)ptrb[i+1];
            if (ch == 0 || ch > 127) {
                ch = '.';
            }
            ascii += ch;
        }
        if ((i+1)%8 > 0) {
            if ((i+1)%4 == 0) {
                result += indentation + indentation;
                if (withASCII) {
                    ascii += asciiIndentation;
                }
            } else {
                result += indentation;
            }
        } else {
            if (withASCII) {
                result += spacing + ascii;
                ascii = COMMON_EMPTY_STRING;
            }
            result += L"\n";
        }
    }
    
    /* TODO as in this->getStringOfBytes
    if (line != COMMON_EMPTY_STRING) {
        if (withASCII) {
            // we must pad line with spaces
            LONG padCount = 8 - this->_size % 8;
            for (LONG i = 0; i < padCount-1; i++) {
                line += CString(L"  ") + indentation;
            }
            if (8-padCount < 4) {
                line += indentation + CString(L"  ");
            } else if (padCount == 4) {
                line += indentation;
            } else {
                line += CString(L"  ");
            }
            line += spacing + ascii;
            ascii = COMMON_EMPTY_STRING;
        }
        if (withOffsets) {
            line = FormatHexValue(currentOffset, FORMAT_ALIGN_LEFT, L'0', 8, FALSE, FALSE, TRUE, 8) + L":  " + line;
            currentOffset += 16;
        }
        result += line;
    }
    */
    result.rtrim();
    return result;
}

CString CMemoryView::getStringOfDwords(BOOL withOffsets, BOOL withASCII) {
    CString result;
    CString ascii;
    CString indentation = L"  ";
    CString spacing = L"    ";
    CString asciiIndentation = indentation.substr(0, indentation.length()/2);
    
    ULONG count = this->_size / sizeof(DWORD);
    DWORD* ptr = (DWORD*)this->_pointer;
    BYTE* ptrb;
    UCHAR ch;
    for (LONG i = 0; i < count; i++) {
        result += FormatHexValue(ptr[i], FORMAT_ALIGN_LEFT, L'0', 8, FALSE, FALSE, TRUE, 8);
        if (withASCII) {
            ptrb = (BYTE*)&ptr[i];
            ch = (UCHAR)ptrb[i];
            if (ch == 0 || ch > 127) {
                ch = '.';
            }
            ascii += ch;
            
            ch = (UCHAR)ptrb[i+1];
            if (ch == 0 || ch > 127) {
                ch = '.';
            }
            ascii += ch;
            
            ch = (UCHAR)ptrb[i+2];
            if (ch == 0 || ch > 127) {
                ch = '.';
            }
            ascii += ch;
            
            ch = (UCHAR)ptrb[i+3];
            if (ch == 0 || ch > 127) {
                ch = '.';
            }
            ascii += ch;
        }
        if ((i+1)%4 > 0) {
            if ((i+1)%2 == 0) {
                result += indentation + indentation;
                if (withASCII) {
                    ascii += asciiIndentation;
                }
            } else {
                result += indentation;
            }
        } else {
            if (withASCII) {
                result += spacing + ascii;
                ascii = COMMON_EMPTY_STRING;
            }
            result += L"\n";
        }
    }
    
    /* TODO as in this->getStringOfBytes
    if (line != COMMON_EMPTY_STRING) {
        if (withASCII) {
            // we must pad line with spaces
            LONG padCount = 4 - this->_size % 4;
            for (LONG i = 0; i < padCount-1; i++) {
                line += CString(L"  ") + indentation;
            }
            if (4-padCount < 2) {
                line += indentation + CString(L"  ");
            } else if (padCount == 2) {
                line += indentation;
            } else {
                line += CString(L"  ");
            }
            line += spacing + ascii;
            ascii = COMMON_EMPTY_STRING;
        }
        if (withOffsets) {
            line = FormatHexValue(currentOffset, FORMAT_ALIGN_LEFT, L'0', 8, FALSE, FALSE, TRUE, 8) + L":  " + line;
            currentOffset += 16;
        }
        result += line;
    }
    */
    result.rtrim();
    return result;
}

VOID CMemoryView::print(CString indent, BOOL dumpAsBytes, BOOL dumpAsWords, BOOL dumpAsDwords) {
    CString output = this->getDump(indent, dumpAsBytes, dumpAsWords, dumpAsDwords);
    if (output != COMMON_EMPTY_STRING) {
        Stdout(output.getBuffer());
    }
}

CString CMemoryView::getDump(CString indent, BOOL dumpAsBytes, BOOL dumpAsWords, BOOL dumpAsDwords) {
    if (dumpAsBytes || dumpAsWords || dumpAsDwords) {
        CString output;
        CString dump;
        if (dumpAsBytes) {
            output += indent + L"BYTE DUMP:\n";
            dump = this->getStringOfBytes();
            dump.prependEachLine(indent + L"    ");
            output += dump + "\n";
        }
        if (dumpAsWords) {
            output += indent + L"WORD DUMP:\n";
            dump = this->getStringOfWords();
            dump.prependEachLine(indent + L"    ");
            output += dump + "\n";
        }
        if (dumpAsDwords) {
            output += indent + L"DWORD DUMP:\n";
            dump = this->getStringOfDwords();
            dump.prependEachLine(indent + L"    ");
            output += dump + "\n";
        }    
        return output;
    }
    
    return COMMON_EMPTY_STRING;
}

CString CMemoryView::toString() {
    return this->getDump(L"", TRUE, FALSE, FALSE);
}
