Example #1
0
// Extract extracts CEL and CL2 archives.
func Extract(archiveName string) (err error) {
	imageCount, found := imgconf.GetImageCount(archiveName)
	if !found {
		return fmt.Errorf("no archived images in %q.", archiveName)
	}
	archivePath, err := mpq.GetPath(archiveName)
	if err != nil {
		return err
	}
	fr, err := os.Open(archivePath)
	if err != nil {
		return err
	}
	defer fr.Close()
	fws, err := createOutputImages(archivePath, imageCount)
	if err != nil {
		return err
	}
	defer closeFiles(fws)
	ext := path.Ext(archiveName)
	switch ext {
	case ".cel":
		err = ExtractCel(fr, fws)
	case ".cl2":
		err = ExtractCl2(fr, fws)
	default:
		return fmt.Errorf("imgarchive.Extract: unknown extension: %q.", ext)
	}
	if err != nil {
		return fmt.Errorf("imgarchive.Extract: error while extracting %q: %s.", archiveName, err)
	}
	return nil
}
Example #2
0
// Parse parses a given TIL file and returns a slice of squares, based on the
// TIL format described above.
func Parse(tilName string) (squares []Square, err error) {
	tilPath, err := mpq.GetPath(tilName)
	if err != nil {
		return nil, err
	}
	fr, err := os.Open(tilPath)
	if err != nil {
		return nil, err
	}
	defer fr.Close()
	for {
		var x [4]uint16
		err = binary.Read(fr, binary.LittleEndian, &x)
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}
		square := Square{
			PillarNumTop:    int(x[0]),
			PillarNumRight:  int(x[1]),
			PillarNumLeft:   int(x[2]),
			PillarNumBottom: int(x[3]),
		}
		squares = append(squares, square)
	}
	return squares, nil
}
Example #3
0
// Parse parses a given SOL file and returns a slice of solids, based on the
// SOL format described above.
func Parse(solName string) (solids []Solid, err error) {
	solPath, err := mpq.GetPath(solName)
	if err != nil {
		return nil, err
	}
	fr, err := os.Open(solPath)
	if err != nil {
		return nil, err
	}
	defer fr.Close()

	var x uint8
	for {
		err = binary.Read(fr, binary.LittleEndian, &x)
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}
		var solid Solid
		if x&0x01 != 0 {
			solid.Sol0x01 = true
		}
		if x&0x02 != 0 {
			solid.Sol0x02 = true
		}
		if x&0x04 != 0 {
			solid.Sol0x04 = true
		}
		if x&0x08 != 0 {
			solid.Sol0x08 = true
		}
		if x&0x10 != 0 {
			solid.Sol0x10 = true
		}
		if x&0x20 != 0 {
			solid.Sol0x20 = true
		}
		if x&0x40 != 0 {
			solid.Sol0x40 = true
		}
		if x&0x80 != 0 {
			solid.Sol0x80 = true
		}
		solids = append(solids, solid)
	}

	return solids, nil
}
Example #4
0
// GetFrames returns a slice of frames, whose content has been retrieved based
// on the CEL format described above.
//
// Note: The absolute path of celName is resolved using mpq.GetPath.
func GetFrames(celName string) (frames [][]byte, err error) {
	// Open CEL file.
	celPath, err := mpq.GetPath(celName)
	if err != nil {
		return nil, err
	}
	f, err := os.Open(celPath)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	// Read frame count.
	var frameCount uint32
	err = binary.Read(f, binary.LittleEndian, &frameCount)
	if err != nil {
		return nil, fmt.Errorf("cel.GetFrames: unable to read frame count for %q: %v", celName, err)
	}

	// Read frame offsets.
	frameOffsets := make([]uint32, frameCount+1)
	err = binary.Read(f, binary.LittleEndian, frameOffsets)
	if err != nil {
		return nil, fmt.Errorf("cel.GetFrames: unable to read frame offsets for %q: %v", celName, err)
	}

	// Read frame contents.
	frames = make([][]byte, frameCount)
	for frameNum := range frames {
		// Ignore frame header.
		headerSize := imgconf.GetHeaderSize(celName)
		frameStart := int64(frameOffsets[frameNum]) + int64(headerSize)

		// Read frame content.
		frameEnd := int64(frameOffsets[frameNum+1])
		frameSize := frameEnd - frameStart
		frame := make([]byte, frameSize)
		_, err = f.ReadAt(frame, frameStart)
		if err != nil {
			return nil, fmt.Errorf("cel.GetFrames: unable to read frame content for %q: %v", celName, err)
		}
		frames[frameNum] = frame
	}

	return frames, nil
}
Example #5
0
// Parse parses a given MIN file and returns a slice of pillars, based on the
// MIN format described above.
func Parse(minName string) (pillars []Pillar, err error) {
	minPath, err := mpq.GetPath(minName)
	if err != nil {
		return nil, err
	}
	fr, err := os.Open(minPath)
	if err != nil {
		return nil, err
	}
	defer fr.Close()
	var blockCount int
	switch minName {
	case "l1.min", "l2.min", "l3.min":
		blockCount = 10
	case "l4.min", "town.min":
		blockCount = 16
	}
	tmp := make([]uint16, blockCount)
	for {
		err = binary.Read(fr, binary.LittleEndian, &tmp)
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}
		pillar := Pillar{}
		pillar.Blocks = make([]Block, blockCount)
		for i := 0; i < blockCount; i++ {
			frameNumPlus1 := int(tmp[i] & 0x0FFF)
			if frameNumPlus1 != 0 {
				pillar.Blocks[i].IsValid = true
				pillar.Blocks[i].FrameNum = frameNumPlus1 - 1
			}
			pillar.Blocks[i].Type = int(tmp[i]&0x7000) >> 12
		}
		pillars = append(pillars, pillar)
	}
	return pillars, nil
}
Example #6
0
// Parse parses a given DUN file and stores each pillarNum at a coordinate in
// the dungeon, based on the DUN format described above.
//
// Below is a description of how the squares are positioned on the dungeon map:
//    1) Start at the coordinates colStart, rowStart.
//    2) Place a square.
//       - Each square is two cols in width and two rows in height.
//    3) Increment col with two.
//    4) goto 2) dunQWidth number of times.
//    5) Increment row with two.
//    6) goto 2) dunQHeight number of times.
//
// ref: GetPillarRect (illustration of map coordinate system)
//
// Any additional cell data is stored afterwards using row major.
func (dungeon *Dungeon) Parse(dunName string) (err error) {
	dunPath, err := mpq.GetPath(dunName)
	if err != nil {
		return err
	}
	fr, err := os.Open(dunPath)
	if err != nil {
		return err
	}
	defer fr.Close()
	var tmp [2]uint16
	err = binary.Read(fr, binary.LittleEndian, &tmp)
	if err != nil {
		return err
	}
	dunQWidth := int(tmp[0])
	dunQHeight := int(tmp[1])
	colStart, err := dunconf.GetColStart(dunName)
	if err != nil {
		return err
	}
	rowStart, err := dunconf.GetRowStart(dunName)
	if err != nil {
		return err
	}
	nameWithoutExt, err := GetLevelName(dunName)
	if err != nil {
		return err
	}

	// squareNumsPlus1.
	squares, err := til.Parse(nameWithoutExt + ".til")
	if err != nil {
		return err
	}
	row := rowStart
	for i := 0; i < dunQHeight; i++ {
		col := colStart
		for j := 0; j < dunQWidth; j++ {
			var x uint16
			err = binary.Read(fr, binary.LittleEndian, &x)
			if err != nil {
				return err
			}
			squareNumPlus1 := int(x)
			if squareNumPlus1 != 0 {
				square := squares[squareNumPlus1-1]
				dungeon[col][row]["pillarNum"] = square.PillarNumTop
				dungeon[col+1][row]["pillarNum"] = square.PillarNumRight
				dungeon[col][row+1]["pillarNum"] = square.PillarNumLeft
				dungeon[col+1][row+1]["pillarNum"] = square.PillarNumBottom
			}
			col += 2
		}
		row += 2
	}

	dunWidth := 2 * dunQWidth
	dunHeight := 2 * dunQHeight

	// TODO: Figure out what these values are used for. Items?
	row = rowStart
	for i := 0; i < dunHeight; i++ {
		col := colStart
		for j := 0; j < dunWidth; j++ {
			var x uint16
			err = binary.Read(fr, binary.LittleEndian, &x)
			if err != nil {
				// Some DUN files only contain the pillar IDs.
				if err == io.EOF && i == 0 && j == 0 {
					return nil
				}
				return err
			}
			dungeon[col][row]["unknown"] = int(x)
			col++
		}
		row++
	}

	// dunMonsterIDs.
	row = rowStart
	for i := 0; i < dunHeight; i++ {
		col := colStart
		for j := 0; j < dunWidth; j++ {
			var x uint16
			err = binary.Read(fr, binary.LittleEndian, &x)
			if err != nil {
				if err == io.EOF && i == 0 && j == 0 {
					return nil
				}
				return err
			}
			// TODO: Lookup monster idx from dunMonsterID.
			// ref: 4B6C98
			dungeon[col][row]["dunMonsterID"] = int(x)
			col++
		}
		row++
	}

	// dunObjectIDs.
	row = rowStart
	for i := 0; i < dunHeight; i++ {
		col := colStart
		for j := 0; j < dunWidth; j++ {
			var x uint16
			err = binary.Read(fr, binary.LittleEndian, &x)
			if err != nil {
				if err == io.EOF && i == 0 && j == 0 {
					return nil
				}
				return err
			}
			// TODO: Lookup object idx from dunObjectID.
			// ref: 4AAD28
			dungeon[col][row]["dunObjectID"] = int(x)
			col++
		}
		row++
	}

	// transparencies.
	row = rowStart
	for i := 0; i < dunHeight; i++ {
		col := colStart
		for j := 0; j < dunWidth; j++ {
			var x uint16
			err = binary.Read(fr, binary.LittleEndian, &x)
			if err != nil {
				if err == io.EOF && i == 0 && j == 0 {
					return nil
				}
				return err
			}
			dungeon[col][row]["transparency"] = int(x)
			col++
		}
		row++
	}

	return nil
}