func loadBoards(boards map[string]*types.Board, packageId string, platformId string, folder string, logger i18n.Logger) error {
	boardsProperties, err := properties.Load(filepath.Join(folder, constants.FILE_BOARDS_TXT), logger)
	if err != nil {
		return i18n.WrapError(err)
	}

	localProperties, err := properties.SafeLoad(filepath.Join(folder, constants.FILE_BOARDS_LOCAL_TXT), logger)
	if err != nil {
		return i18n.WrapError(err)
	}

	boardsProperties = boardsProperties.Merge(localProperties)

	propertiesByBoardId := boardsProperties.FirstLevelOf()
	delete(propertiesByBoardId, constants.BOARD_PROPERTIES_MENU)

	for boardID, boardProperties := range propertiesByBoardId {
		boardProperties[constants.ID] = boardID
		board := getOrCreateBoard(boards, boardID)
		board.Properties.Merge(boardProperties)
		boards[boardID] = board
	}

	return nil
}
func (s *HardwareLoader) Run(ctx *types.Context) error {
	logger := ctx.GetLogger()

	packages := &types.Packages{}
	packages.Packages = make(map[string]*types.Package)
	packages.Properties = make(map[string]string)

	folders := ctx.HardwareFolders
	folders, err := utils.AbsolutizePaths(folders)
	if err != nil {
		return i18n.WrapError(err)
	}

	for _, folder := range folders {
		stat, err := os.Stat(folder)
		if err != nil {
			return i18n.WrapError(err)
		}
		if !stat.IsDir() {
			return i18n.ErrorfWithLogger(logger, constants.MSG_MUST_BE_A_FOLDER, folder)
		}

		hardwarePlatformTxt, err := properties.SafeLoad(filepath.Join(folder, constants.FILE_PLATFORM_TXT), logger)
		if err != nil {
			return i18n.WrapError(err)
		}
		packages.Properties.Merge(hardwarePlatformTxt)

		subfolders, err := utils.ReadDirFiltered(folder, utils.FilterDirs)
		if err != nil {
			return i18n.WrapError(err)
		}
		subfolders = utils.FilterOutFoldersByNames(subfolders, constants.FOLDER_TOOLS)

		for _, subfolder := range subfolders {
			subfolderPath := filepath.Join(folder, subfolder.Name())
			packageId := subfolder.Name()

			if _, err := os.Stat(filepath.Join(subfolderPath, constants.FOLDER_HARDWARE)); err == nil {
				subfolderPath = filepath.Join(subfolderPath, constants.FOLDER_HARDWARE)
			}

			targetPackage := getOrCreatePackage(packages, packageId)
			err = loadPackage(targetPackage, subfolderPath, logger)
			if err != nil {
				return i18n.WrapError(err)
			}
			packages.Packages[packageId] = targetPackage
		}
	}

	ctx.Hardware = packages

	return nil
}
func loadPlatform(targetPlatform *types.Platform, packageId string, folder string, logger i18n.Logger) error {
	_, err := os.Stat(filepath.Join(folder, constants.FILE_BOARDS_TXT))
	if err != nil && !os.IsNotExist(err) {
		return i18n.WrapError(err)
	}

	if os.IsNotExist(err) {
		return nil
	}

	targetPlatform.Folder = folder

	err = loadBoards(targetPlatform.Boards, packageId, targetPlatform.PlatformId, folder, logger)
	if err != nil {
		return i18n.WrapError(err)
	}

	assignDefaultBoardToPlatform(targetPlatform)

	platformTxt, err := properties.SafeLoad(filepath.Join(folder, constants.FILE_PLATFORM_TXT), logger)
	if err != nil {
		return i18n.WrapError(err)
	}

	localPlatformProperties, err := properties.SafeLoad(filepath.Join(folder, constants.FILE_PLATFORM_LOCAL_TXT), logger)
	if err != nil {
		return i18n.WrapError(err)
	}

	targetPlatform.Properties = targetPlatform.Properties.Clone()
	targetPlatform.Properties.Merge(platformTxt)
	targetPlatform.Properties.Merge(localPlatformProperties)

	programmersProperties, err := properties.SafeLoad(filepath.Join(folder, constants.FILE_PROGRAMMERS_TXT), logger)
	if err != nil {
		return i18n.WrapError(err)
	}
	targetPlatform.Programmers = properties.MergeMapsOfProperties(make(map[string]properties.Map), targetPlatform.Programmers, programmersProperties.FirstLevelOf())

	return nil
}
func loadPackage(targetPackage *types.Package, folder string, logger i18n.Logger) error {
	packagePlatformTxt, err := properties.SafeLoad(filepath.Join(folder, constants.FILE_PLATFORM_TXT), logger)
	if err != nil {
		return i18n.WrapError(err)
	}
	targetPackage.Properties.Merge(packagePlatformTxt)

	subfolders, err := utils.ReadDirFiltered(folder, utils.FilterDirs)
	if err != nil {
		return i18n.WrapError(err)
	}

	subfolders = utils.FilterOutFoldersByNames(subfolders, constants.FOLDER_TOOLS)

	platforms := targetPackage.Platforms
	for _, subfolder := range subfolders {
		subfolderPath := filepath.Join(folder, subfolder.Name())
		platformId := subfolder.Name()

		_, err := os.Stat(filepath.Join(subfolderPath, constants.FILE_BOARDS_TXT))
		if err != nil && os.IsNotExist(err) {
			theOnlySubfolder, err := utils.TheOnlySubfolderOf(subfolderPath)
			if err != nil {
				return i18n.WrapError(err)
			}

			if theOnlySubfolder != constants.EMPTY_STRING {
				subfolderPath = filepath.Join(subfolderPath, theOnlySubfolder)
			}
		}

		platform := getOrCreatePlatform(platforms, platformId)
		err = loadPlatform(platform, targetPackage.PackageId, subfolderPath, logger)
		if err != nil {
			return i18n.WrapError(err)
		}
		platforms[platformId] = platform
	}

	return nil
}