#!/usr/bin/env python
"""
 * dump-msole.py:
 *
 * Copyright (C) 2006 Danny Milosavljevic, Fabasoft (danny.milosavljevic@fabalabs.org)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 or later of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
"""

from __future__ import division

import struct
import sys
import exceptions
import os
#from msole import ElementType, Color, SectorNumber, Sid, Header, Fat, MiniFat, MiniStream, Stream, DirectoryEntry, determine_path, traverse_directory_tree_left_right
import msole

from cStringIO import StringIO


def write_directory_index_entry(index, entry):
  out_path = "directory/%d" % index
  if not os.path.exists(out_path): 
    os.makedirs(out_path)

  file("%s/name" % out_path, "w").write(entry.name)
  #file("%s/element_type" % out_path, "w").write("%s" % entry.element_type)
  #file("%s/color" % out_path, "w").write("%s" % entry.color)

  if entry.child_sid != msole.Sid.NoStream:
    os.symlink("../../directory/%d" % entry.child_sid, "%s/child_sid" % out_path)

  if entry.left_sid != msole.Sid.NoStream:
    os.symlink("../../directory/%d" % entry.left_sid, "%s/left_sid" % out_path)

  if entry.right_sid != msole.Sid.NoStream:
    os.symlink("../../directory/%d" % entry.right_sid, "%s/right_sid" % out_path)

  #file("%s/storage_class_id" % out_path, "w").write(entry._format_storage_class_id_dump(entry.storage_class_id))
  #file("%s/storage_flags" % out_path, "w").write("%s" % entry.storage_flags)
  #file("%s/storage_creation_time" % out_path, "w").write(entry._format_time_dump(entry.storage_creation_time))
  #file("%s/storage_modification_time" % out_path, "w").write(entry._format_time_dump(entry.storage_modification_time))
  #file("%s/start_sector" % out_path, "w").write("%s" % entry.start_sector)
  #file("%s/size" % out_path, "w").write("%s" % entry.size)

  f = file("%s/attributes" % out_path, "w")
  #f.write(entry.name)
  f.write("element_type = %s\n" % entry.element_type)
  f.write("color = %s\n" % entry.color)
  f.write("storage_class_id = %s\n" % entry._format_storage_class_id_dump(entry.storage_class_id))
  f.write("storage_flags = %s\n" % entry.storage_flags)
  f.write("storage_creation_time = %s\n" % entry._format_time_dump(entry.storage_creation_time))
  f.write("storage_modification_time = %s\n" % entry._format_time_dump(entry.storage_modification_time))
  #f.write("start_sector = %s\n" % entry.start_sector)
  #f.write("size = %s\n" % entry.size)
  f.close()

stream_1 = file(sys.argv[1], "rb")

header_1 = msole.Header()
header_1.read_from(stream_1)
header_1.verify()
#header_1.dump()


difat_1 = msole.Difat(header_1)
difat_1.read_from(stream_1)
#print difat_1.sector_numbers


fat_1 = msole.Fat(header_1, difat_1)
fat_1.read_from(stream_1)
#print fat_1.fat

mini_fat_1 = msole.MiniFat(header_1, fat_1)
mini_fat_1.read_from(stream_1)
#mini_fat_1.dump(0, "Mini Fat")

directory_1 = msole.Directory(header_1, fat_1)
directory_1.read_from(stream_1)

root_entry = directory_1.entries[0]
assert(root_entry.element_type == msole.ElementType.Root)

mini_stream_start = root_entry.start_sector
assert(mini_stream_start <= msole.SectorNumber.MaximumRegular or mini_stream_start == msole.SectorNumber.EndOfChain)

mini_stream_1 = None

if mini_stream_start != msole.SectorNumber.EndOfChain:
  mini_stream_1 = msole.Stream(header_1, fat_1, stream_1, mini_stream_start, root_entry.size)


print "root name = %s" % root_entry.name
 
entries_sorted_by_name = [(entry.name, index, entry) for index, entry in enumerate(directory_1.entries) if entry.element_type != msole.ElementType.Invalid]
entries_sorted_by_name.sort()

if not os.path.exists("directory"): 
  os.makedirs("directory")

for x_name, index, entry in entries_sorted_by_name:
  entry_path = msole.determine_path(directory_1, msole.Sid(index))
  assert(entry_path.startswith("/"))
  print "path = %s" % entry_path
  entry.dump(0, "") # SID# %d " % index)

  write_directory_index_entry(index, entry)

  if entry.element_type == msole.ElementType.Stream:
    if entry.size < header_1.maximum_byte_size_for_mini_stream:
      #mini_stream_1.seek(entry.start_sector * (2 ** header_1.mini_sector_shift))
      stream_2 = msole.MiniStream(header_1, mini_fat_1, mini_stream_1, entry.start_sector, entry.size)
    else:
      stream_2 = msole.Stream(header_1, fat_1, stream_1, entry.start_sector, entry.size)

    data = stream_2.read(entry.size)
    #print "L", len(data), entry.size
    assert(len(data) == entry.size)
    
    #entry_path = determine_path(directory_1, directory_parents_1, Sid(index))
    
    out_path = "." + entry_path
   
    out_directory_path = os.path.dirname(out_path)
    
    if not os.path.exists(out_directory_path): 
      os.makedirs(out_directory_path)

    #print "path", out_path
      
    out_file = file(out_path, "wb")
    out_file.write(data)
    out_file.close()
  elif entry.element_type == msole.ElementType.Storage:
    out_path = "." + entry_path

    #print entry_path

    if not os.path.exists(out_path): 
      os.makedirs(out_path)
    

  print


