// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package macho

import (
	
	
	
	
)

// A FatFile is a Mach-O universal binary that contains at least one architecture.
type FatFile struct {
	Magic  uint32
	Arches []FatArch
	closer io.Closer
}

// A FatArchHeader represents a fat header for a specific image architecture.
type FatArchHeader struct {
	Cpu    Cpu
	SubCpu uint32
	Offset uint32
	Size   uint32
	Align  uint32
}

const fatArchHeaderSize = 5 * 4

// A FatArch is a Mach-O File inside a FatFile.
type FatArch struct {
	FatArchHeader
	*File
}

// ErrNotFat is returned from NewFatFile or OpenFat when the file is not a
// universal binary but may be a thin binary, based on its magic number.
var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil}

// NewFatFile creates a new FatFile for accessing all the Mach-O images in a
// universal binary. The Mach-O binary is expected to start at position 0 in
// the ReaderAt.
func ( io.ReaderAt) (*FatFile, error) {
	var  FatFile
	 := io.NewSectionReader(, 0, 1<<63-1)

	// Read the fat_header struct, which is always in big endian.
	// Start with the magic number.
	 := binary.Read(, binary.BigEndian, &.Magic)
	if  != nil {
		return nil, &FormatError{0, "error reading magic number", nil}
	} else if .Magic != MagicFat {
		// See if this is a Mach-O file via its magic number. The magic
		// must be converted to little endian first though.
		var  [4]byte
		binary.BigEndian.PutUint32([:], .Magic)
		 := binary.LittleEndian.Uint32([:])
		if  == Magic32 ||  == Magic64 {
			return nil, ErrNotFat
		} else {
			return nil, &FormatError{0, "invalid magic number", nil}
		}
	}
	 := int64(4)

	// Read the number of FatArchHeaders that come after the fat_header.
	var  uint32
	 = binary.Read(, binary.BigEndian, &)
	if  != nil {
		return nil, &FormatError{, "invalid fat_header", nil}
	}
	 += 4

	if  < 1 {
		return nil, &FormatError{, "file contains no images", nil}
	}

	// Combine the Cpu and SubCpu (both uint32) into a uint64 to make sure
	// there are not duplicate architectures.
	 := make(map[uint64]bool, )
	// Make sure that all images are for the same MH_ type.
	var  Type

	// Following the fat_header comes narch fat_arch structs that index
	// Mach-O images further in the file.
	.Arches = make([]FatArch, )
	for  := uint32(0);  < ; ++ {
		 := &.Arches[]
		 = binary.Read(, binary.BigEndian, &.FatArchHeader)
		if  != nil {
			return nil, &FormatError{, "invalid fat_arch header", nil}
		}
		 += fatArchHeaderSize

		 := io.NewSectionReader(, int64(.Offset), int64(.Size))
		.File,  = NewFile()
		if  != nil {
			return nil, 
		}

		// Make sure the architecture for this image is not duplicate.
		 := (uint64(.Cpu) << 32) | uint64(.SubCpu)
		if ,  := [];  ||  {
			return nil, &FormatError{, fmt.Sprintf("duplicate architecture cpu=%v, subcpu=%#x", .Cpu, .SubCpu), nil}
		}
		[] = true

		// Make sure the Mach-O type matches that of the first image.
		if  == 0 {
			 = .Type
		} else {
			if .Type !=  {
				return nil, &FormatError{, fmt.Sprintf("Mach-O type for architecture #%d (type=%#x) does not match first (type=%#x)", , .Type, ), nil}
			}
		}
	}

	return &, nil
}

// OpenFat opens the named file using os.Open and prepares it for use as a Mach-O
// universal binary.
func ( string) (*FatFile, error) {
	,  := os.Open()
	if  != nil {
		return nil, 
	}
	,  := NewFatFile()
	if  != nil {
		.Close()
		return nil, 
	}
	.closer = 
	return , nil
}

func ( *FatFile) () error {
	var  error
	if .closer != nil {
		 = .closer.Close()
		.closer = nil
	}
	return 
}