// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018 Eduardo Aguiar
//
// 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, 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, see <http://www.gnu.org/licenses/>.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <mobius/partition/partition_system_impl_apm.h>
#include <mobius/decoder/data_decoder.h>
#include <mobius/io/bytearray_io.h>

namespace mobius
{
namespace partition
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief check if stream has an instance of APM partition system
//! \param disk disk object
//! \return true/false
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bool
partition_system_impl_apm::is_instance (const mobius::disk::disk disk)
{
  const mobius::bytearray SIGNATURE = {'P', 'M', 0x00, 0x00};

  mobius::io::sector_reader_adaptor reader (disk.new_reader (), disk.get_sector_size ());
  reader.seek (1);
  auto data = reader.read ();

  return data.slice (0, 3) == SIGNATURE;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief constructor
//! \param disk disk object
//! \see File System Forensic Analysis, "Apple Partitions" section
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
partition_system_impl_apm::partition_system_impl_apm (const mobius::disk::disk disk)
{
  mobius::io::sector_reader_adaptor reader (disk.new_reader (), disk.get_sector_size ());
  entry e;

  // add driver descriptor map entry
  e.starting_sector = 0;
  e.ending_sector = 0;
  e.sectors = 1;
  e.type = "ddm";
  e.description = "Driver Descriptor Map";
  _add_entry (e);

  // partition map
  reader.seek (1);
  auto data = reader.read ();
  mobius::decoder::data_decoder decoder (mobius::io::new_bytearray_reader (data));

  decoder.skip (4);
  auto partitions = decoder.get_uint32_be ();

  // add partition map entry
  e.starting_sector = 1;
  e.ending_sector = partitions + 1;
  e.sectors = partitions + 1;
  e.type = "pmap";
  e.description = "Partition Map";
  _add_entry (e);

  // read each partition
  for (std::uint32_t i = 0; i < partitions - 1; i++)
    {
      reader.seek (i + 2);
      data = reader.read ();

      mobius::decoder::data_decoder table_decoder (mobius::io::new_bytearray_reader (data));
      table_decoder.skip (8);
      auto starting_sector = table_decoder.get_uint32_be ();
      auto sectors = table_decoder.get_uint32_be ();
      auto name = table_decoder.get_string_by_size (32);
      auto type = table_decoder.get_string_by_size (32);
      table_decoder.skip (8);
      auto flags = table_decoder.get_uint32_be ();

      if (type != "Apple_Free")
        {
          // create partition
          partition p;
          p.set_starting_sector (starting_sector);
          p.set_ending_sector (starting_sector + sectors - 1);
          p.set_starting_address (starting_sector * reader.get_sector_size ());
          p.set_ending_address ((p.get_ending_sector () + 1) * reader.get_sector_size () - 1);
          p.set_name (name);
          p.set_type (type);
          p.is_bootable (flags & 0x00000008);
          p.is_readable (flags & 0x00000010);
          p.is_writeable (flags & 0x00000020);
          _add_partition (p);

          // create partition table entry
          entry e;
          e.starting_sector = starting_sector;
          e.ending_sector = starting_sector + sectors - 1;
          e.sectors = sectors;
          e.type = "partition";
          e.description = p.get_name () + " partition";

          if (p.is_bootable ()) e.flags += 'B';
          if (p.is_readable ()) e.flags += 'R';
          if (p.is_writeable ()) e.flags += 'W';
          _add_entry (e);
        }
    }

  // add freespaces between non-contiguous partitions
  _add_freespaces (reader);
}

} // namespace partition
} // namespace mobius
