# Volatility
# Copyright (C) 2007 Volatile Systems
#
# Original Source:
# Copyright (C) 2004,2005,2006 4tphi Research
# Author: {npetroni,awalters}@4tphi.net (Nick Petroni and AAron Walters)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or (at
# your option) any later version.
#
# This program 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#

"""
@author:       AAron Walters
@license:      GNU General Public License 2.0 or later
@contact:      awalters@volatilesystems.com
@organization: Volatile Systems

Alias for all address spaces
"""

# pylint: disable=missing-docstring

import os
import struct


class FileAddressSpace:
    def __init__(self, fname, mode='rb', fast=False):
        self.fname = fname
        self.name = fname
        self.fhandle = open(fname, mode)
        self.fsize = os.path.getsize(fname)

        if fast:
            self.fast_fhandle = open(fname, mode)

    def fread(self, len):
        return self.fast_fhandle.read(len)

    def read(self, addr, len):
        self.fhandle.seek(addr)
        return self.fhandle.read(len)

    def read_long(self, addr):
        string = self.read(addr, 4)
        (longval,) = struct.unpack('L', string)
        return longval

    def get_address_range(self):
        return [0, self.fsize - 1]

    def get_available_addresses(self):
        return [self.get_address_range()]

    def is_valid_address(self, addr):
        return addr < self.fsize - 1

    def close(self):
        self.fhandle.close()


# Code below written by Brendan Dolan-Gavitt

BLOCK_SIZE = 0x1000


class HiveFileAddressSpace:
    def __init__(self, fname):
        self.fname = fname
        self.base = FileAddressSpace(fname)

    def vtop(self, vaddr):
        return vaddr + BLOCK_SIZE + 4

    def read(self, vaddr, length, zero=False):
        first_block = BLOCK_SIZE - vaddr % BLOCK_SIZE
        full_blocks = ((length + (vaddr % BLOCK_SIZE)) // BLOCK_SIZE) - 1
        left_over = (length + vaddr) % BLOCK_SIZE

        paddr = self.vtop(vaddr)
        if paddr is None and zero:
            if length < first_block:
                return "\0" * length
            else:
                stuff_read = "\0" * first_block
        elif paddr is None:
            return None
        else:
            if length < first_block:
                stuff_read = self.base.read(paddr, length)
                if not stuff_read and zero:
                    return "\0" * length
                else:
                    return stuff_read

            stuff_read = self.base.read(paddr, first_block)
            if not stuff_read and zero:
                stuff_read = "\0" * first_block

        new_vaddr = vaddr + first_block
        for __ in range(0, full_blocks):
            paddr = self.vtop(new_vaddr)
            if paddr is None and zero:
                stuff_read = stuff_read + "\0" * BLOCK_SIZE
            elif paddr is None:
                return None
            else:
                new_stuff = self.base.read(paddr, BLOCK_SIZE)
                if not new_stuff and zero:
                    new_stuff = "\0" * BLOCK_SIZE
                elif not new_stuff:
                    return None
                else:
                    stuff_read = stuff_read + new_stuff
            new_vaddr = new_vaddr + BLOCK_SIZE

        if left_over > 0:
            paddr = self.vtop(new_vaddr)
            if paddr is None and zero:
                stuff_read = stuff_read + "\0" * left_over
            elif paddr is None:
                return None
            else:
                stuff_read = stuff_read + self.base.read(paddr, left_over)
        return stuff_read

    def read_long_phys(self, addr):
        string = self.base.read(addr, 4)
        (longval,) = struct.unpack('L', string)
        return longval

    def is_valid_address(self, vaddr):
        paddr = self.vtop(vaddr)
        if not paddr:
            return False
        return self.base.is_valid_address(paddr)