Package dotaSoundEditor.Controls

Source Code of dotaSoundEditor.Controls.HeroPanel

package dotaSoundEditor.Controls;

import dotaSoundEditor.Helpers.PortraitFinder;
import dotaSoundEditor.Helpers.ScriptParser;
import dotaSoundEditor.Helpers.Utility;
import dotaSoundEditor.*;
import dotaSoundEditor.Helpers.CacheManager;
import info.ata4.vpk.VPKArchive;
import info.ata4.vpk.VPKEntry;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import org.apache.commons.io.FileUtils;

public class HeroPanel extends EditorPanel
{

    Executor e = Executors.newSingleThreadExecutor();
    PortraitFinder portraitFinder;

    //Just used for designer compatibility. Should never be called from code.
    public HeroPanel()
    {
        initComponents();
    }

    public HeroPanel(String _vpkPath, String _installDir)
    {

        vpkPath = _vpkPath;
        installDir = _installDir;
        this.setName("Hero Spells");
        initComponents();

        portraitFinder = Utility.portraitFinder;
        currentDropdown = heroSpellsDropdown;
        currentTree = heroSpellTree;
        this.populateDropdownBox();
        this.attachDoubleClickListenerToTree();
        this.setVisible(true);
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents()
    {

        jLabel1 = new javax.swing.JLabel();
        heroImageLabel = new javax.swing.JLabel();
        heroSpellsDropdown = new javax.swing.JComboBox();
        jScrollPane1 = new javax.swing.JScrollPane();

        setPreferredSize(new java.awt.Dimension(485, 495));

        jLabel1.setText("Hero:");
        jLabel1.setName("heroLabel"); // NOI18N

        heroImageLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        heroImageLabel.setMaximumSize(new java.awt.Dimension(128, 72));
        heroImageLabel.setMinimumSize(new java.awt.Dimension(128, 72));
        heroImageLabel.setPreferredSize(new java.awt.Dimension(128, 72));

        heroSpellsDropdown.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        heroSpellsDropdown.setName("heroDropdownBox"); // NOI18N
        heroSpellsDropdown.addItemListener(new java.awt.event.ItemListener()
        {
            public void itemStateChanged(java.awt.event.ItemEvent evt)
            {
                heroSpellsDropdownStateChanged(evt);
            }
        });

        jScrollPane1.setName("heroListFrame"); // NOI18N

        heroSpellTree.setMinimumSize(new java.awt.Dimension(72, 64));
        jScrollPane1.setViewportView(heroSpellTree);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel1)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(heroSpellsDropdown, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(0, 0, Short.MAX_VALUE))
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 465, Short.MAX_VALUE)
                    .addGroup(layout.createSequentialGroup()
                        .addGap(0, 0, Short.MAX_VALUE)
                        .addComponent(heroImageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(0, 0, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel1)
                    .addComponent(heroSpellsDropdown, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(heroImageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 369, Short.MAX_VALUE)
                .addContainerGap())
        );
    }// </editor-fold>//GEN-END:initComponents

    private void heroSpellsDropdownStateChanged(java.awt.event.ItemEvent evt)//GEN-FIRST:event_heroSpellsDropdownStateChanged
    {//GEN-HEADEREND:event_heroSpellsDropdownStateChanged
        //TODO: Find a way to inform the Advanced button that it needs to reset.
        if (evt.getStateChange() == ItemEvent.SELECTED)
        {
            System.out.println("Dropdown selected: " + currentDropdown.getSelectedItem().toString());
            //In a background thread so the app doesn't choke on fast scroling
            if (!getAdvancedMode())
            {
                e.execute(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        populateSoundList();
                    }
                });
            }
            else
            {
                String scriptPath = getCurrentScriptString();
                ScriptParser parser = new ScriptParser(new File(Paths.get(scriptPath).toString()));
                TreeModel model = parser.getTreeModel();
                currentTree.setModel(model);
                currentTree.setEditable(true);
                for (int i = 0; i < currentTree.getRowCount(); i++)
                {
                    currentTree.expandRow(i);
                }
            }
            currentTree.setRootVisible(false);
            currentTree.setShowsRootHandles(true);
            try
            {
                fillImageFrame((NamedHero) currentDropdown.getSelectedItem());
            }
            catch (IOException ex)
            {
                System.err.println(ex.getMessage());
            }
        }
    }//GEN-LAST:event_heroSpellsDropdownStateChanged
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel heroImageLabel;
    private final javax.swing.JTree heroSpellTree = new javax.swing.JTree();
    private javax.swing.JComboBox heroSpellsDropdown;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JScrollPane jScrollPane1;
    // End of variables declaration//GEN-END:variables

    //TODO: Use the much better-written MusicPanel populateDropdownBox to refactor this mess
    @Override
    protected void populateDropdownBox()
    {
        currentDropdown.removeAllItems();
        Set heroList = new CopyOnWriteArraySet();
        //Build list of heroes and populate dropwdown with it               
        File file = new File(vpkPath);
        VPKArchive vpk = new VPKArchive();

        System.out.println(file);

        try
        {
            vpk.load(file);
        }
        catch (Exception ex)
        {
            System.err.println("Can't open archive: " + ex.getMessage());
            return;
        }

        for (VPKEntry entry : vpk.getEntries())
        {
            if (entry.getPath().contains("scripts/game_sounds_heroes/"))
            {
                heroList.add(entry.getName());
            }
        }
        //Format and prettify hero list
        for (Object hero : heroList)
        {
            String heroString = hero.toString();
            heroString = heroString.replace("game_sounds_", "");
            NamedHero tempNamedHero = new NamedHero(heroString);
            heroList.remove(hero);
            heroList.add(tempNamedHero);
        }
        Object[] heroListArray = heroList.toArray();
        Arrays.sort(heroListArray);
        for (Object h : heroListArray)
        {
            NamedHero tempHero = (NamedHero) h;
            currentDropdown.addItem(tempHero);
        }
    }

    @Override
    protected void populateSoundList()
    {
        inAdvancedMode = false;
        currentTree.setEditable(false);
        NamedHero selectedHero = (NamedHero) currentDropdown.getSelectedItem();

        Path scriptPath = Paths.get(this.installDir, "/dota/scripts/game_sounds_heroes/game_sounds_" + selectedHero.getInternalName() + ".txt");
        File scriptFile = new File(scriptPath.toString());
        String scriptKey = "game_sounds_" + selectedHero.getInternalName() + ".txt".toLowerCase();
        VPKEntry entry;
        boolean needsValidation = false;

        //if it doesn't exist yet, don't bother validating, and just write it out
        if (!scriptFile.isFile())
        {
            entry = this.getHeroScriptFile(selectedHero.getInternalName());
            this.writeHeroScriptFile(entry, false);
            this.updateCache(scriptKey, entry.getCRC32());
        }
        else //if it exists, we need to validate it
        {
            needsValidation = true;
        }
        ScriptParser parser = new ScriptParser(scriptPath.toFile());
        TreeModel scriptTree = parser.getTreeModel();
        if (needsValidation)
        {
            CacheManager cm = CacheManager.getInstance();
            boolean isUpToDate = this.validateScriptFile(scriptKey, "scripts/game_sounds_heroes/" + scriptKey);
            if (!isUpToDate)
            {
                this.writeHeroScriptFile(cm.getCachedVpkEntry(), true);
                mergeNewChanges(scriptTree, scriptPath);
                this.updateCache(cm.getCachedVpkEntry().getName() + ".txt", cm.getCachedVpkEntry().getCRC32());
            }
        }
        this.currentTreeModel = scriptTree;

        //TODO: Break this out into separate method
        TreeNode rootNode = (TreeNode) scriptTree.getRoot();
        int childCount = rootNode.getChildCount();

        TreeModel soundListTreeModel = new DefaultTreeModel(new DefaultMutableTreeNode("root"));
        ArrayList<String> wavePathsList = new ArrayList<>();
        for (int i = 0; i < childCount; i++)
        {
            String nodeValue = scriptTree.getChild(rootNode, i).toString();
            if (nodeValue.trim().startsWith("//"))
            {
                continue;
            }
            wavePathsList = super.getWavePathsAsList((TreeNode) scriptTree.getChild(rootNode, i));
            DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(nodeValue);

            for (String s : wavePathsList)
            {
                DefaultMutableTreeNode tempNode = new DefaultMutableTreeNode(s);
                newNode.add(tempNode);
            }
            ((DefaultMutableTreeNode) soundListTreeModel.getRoot()).add(newNode);
        }

        currentTree.setModel(soundListTreeModel);
    }

    private VPKEntry getHeroScriptFile(String heroName)
    {
        heroName = heroName.toLowerCase();
        String internalScriptPath = "scripts/game_sounds_heroes/game_sounds_" + heroName + ".txt";
        File vpkFile = new File(vpkPath);
        VPKArchive vpk = new VPKArchive();
        try
        {
            vpk.load(vpkFile);
        }
        catch (Exception ex)
        {
            JOptionPane.showMessageDialog(this,
                    "Error: Unable to open VPK file.\nDetails: " + ex.getMessage(),
                    "Error opening VPK", JOptionPane.ERROR_MESSAGE);
            ex.printStackTrace();
            return null;
        }

        VPKEntry entry = vpk.getEntry(internalScriptPath);
        return entry;
    }

    //TODO: Possible model for abstracing into the parent
    private void writeHeroScriptFile(VPKEntry entryToWrite, boolean overwriteExisting)
    {
        File existsChecker = new File(Paths.get(installDir, entryToWrite.getPath()).toString());
        boolean fileExistsLocally = existsChecker.exists() ? true : false;
        if (fileExistsLocally && !overwriteExisting)
        {
            return;
        }

        File entryFile = new File(Paths.get(installDir, "/dota/").toFile(), entryToWrite.getPath());
        File entryDir = entryFile.getParentFile();
        if (entryDir != null && !entryDir.exists())
        {
            entryDir.mkdirs();
        }
        try (FileChannel fc = FileUtils.openOutputStream(entryFile).getChannel())
        {
            fc.write(entryToWrite.getData());
        }
        catch (IOException ex)
        {
            JOptionPane.showMessageDialog(this,
                    "Error: Unable to write script file to disk.\nDetails: " + ex.getMessage(),
                    "Error writing script file", JOptionPane.ERROR_MESSAGE);
        }
    }

    @Override
    protected void fillImageFrame(Object _selectedItem) throws IOException
    {
        NamedHero selectedItem;
        if (_selectedItem instanceof NamedHero)
        {
            selectedItem = (NamedHero) _selectedItem;
        }
        else
        {
            return;
        }
        try
        {
            heroImageLabel.setIcon(new ImageIcon(portraitFinder.getPortrait(selectedItem.getInternalName())));
        }
        catch (NullPointerException ex)
        {
            System.err.println("Icon not found for hero: " + selectedItem.getFriendlyName());
            heroImageLabel.setIcon(new ImageIcon(""));
        }
    }

    private String getScriptPathByHeroName(String internalName)
    {
        String scriptPathString =
                Paths.get(installDir, "/dota/scripts/game_sounds_heroes/game_sounds_"
                + internalName + ".txt").toString();

        File scriptFilePath = new File(scriptPathString);

        if (scriptFilePath.isFile())
        {
            return scriptFilePath.getAbsolutePath();
        }
        else
        {
            return null;
        }
    }

    @Override
    protected void revertButtonActionPerformed(java.awt.event.ActionEvent evt)
    {
        //TODO: See if we can abstract away some of this + promptUserForNewFile()'s functionality       

        if (currentTree.getSelectionRows().length != 0
                && ((TreeNode) currentTree.getSelectionPath().getLastPathComponent()).isLeaf())
        {
            DefaultMutableTreeNode selectedNode = ((DefaultMutableTreeNode) currentTree.getSelectionPath().getLastPathComponent());
            String selectedWaveString = ((DefaultMutableTreeNode) selectedNode).getUserObject().toString();
            String selectedWaveParentString = ((DefaultMutableTreeNode) ((DefaultMutableTreeNode) selectedNode).getParent()).getUserObject().toString();
            selectedNode = (DefaultMutableTreeNode) this.getTreeNodeFromWavePath(selectedWaveString);

            //First go in and delete the sound in customSounds  
            deleteSoundFileByWaveString(selectedWaveString);

            //Get the relevant wavestring from the internal scriptfile                   
            VPKArchive vpk = new VPKArchive();
            try
            {
                vpk.load(new File(this.vpkPath));
            }
            catch (IOException ex)
            {
                ex.printStackTrace();
            }
            String scriptDir = this.getScriptPathByHeroName(((NamedHero) currentDropdown.getSelectedItem()).getInternalName());
            scriptDir = scriptDir.replace(Paths.get(installDir, "/dota/").toString(), "");
            scriptDir = scriptDir.replace("\\", "/");                           //Match internal forward slashes
            scriptDir = scriptDir.substring(1);                                 //Cut off leading slash
            scriptDir = scriptDir.substring(0, scriptDir.lastIndexOf("/") + 1); //Cut off file extension           

            String scriptFileString = null;
            byte[] bytes = null;
            for (VPKEntry entry : vpk.getEntriesForDir(scriptDir))
            {
                if (entry.getName().contains("game_sounds_" + ((NamedHero) currentDropdown.getSelectedItem()).getInternalName()))
                {
                    try
                    {
                        ByteBuffer scriptBuffer = null;
                        scriptBuffer = entry.getData();
                        bytes = new byte[scriptBuffer.remaining()];
                        scriptBuffer.get(bytes);
                    }
                    catch (IOException ex)
                    {
                        ex.printStackTrace();
                    }
                    scriptFileString = new String(bytes, Charset.forName("UTF-8"));
                    break;
                }
            }
            ArrayList<String> wavePathList = this.getWavePathsAsList(selectedNode.getParent());
            int waveStringIndex = wavePathList.indexOf(selectedWaveString);

            //Cut off every part of the scriptFileString before we get to the entry describing the relevant hero action, so we don't accidentally get the wrong wavepaths           
            StringBuilder scriptFileStringShortened = new StringBuilder();
            Scanner scan = new Scanner(scriptFileString);
            boolean found = false;
            while (scan.hasNextLine())
            {
                String curLine = scan.nextLine();
                if (curLine.equals(selectedWaveParentString))
                {
                    found = true;
                }
                if (found == true)
                {
                    scriptFileStringShortened.append(curLine).append(System.lineSeparator());
                }
            }
            scriptFileString = scriptFileStringShortened.toString();
            ArrayList<String> internalWavePathsList = getWavePathListFromString(scriptFileString);
            String replacementString = internalWavePathsList.get(waveStringIndex);

            selectedNode.setUserObject(replacementString);
            ScriptParser parser = new ScriptParser(this.currentTreeModel);
            parser.writeModelToFile(this.getScriptPathByHeroName(((NamedHero) currentDropdown.getSelectedItem()).getInternalName()));

            //Modify the UI treeNode in addition to the backing TreeNode
            ((DefaultMutableTreeNode) currentTree.getLastSelectedPathComponent()).setUserObject(replacementString);
            ((DefaultTreeModel) currentTree.getModel()).nodeChanged(((DefaultMutableTreeNode) currentTree.getLastSelectedPathComponent()));
        }
    }

    @Override
    protected void revertAllButtonActionPerformed(java.awt.event.ActionEvent evt)
    {
        //Delete existing script file
        String scriptFilePath = getScriptPathByHeroName(((NamedHero) currentDropdown.getSelectedItem()).getInternalName());
        File scriptFileToDelete = new File(scriptFilePath);
        if (scriptFileToDelete.isFile())
        {
            try
            {
                java.nio.file.Files.delete(Paths.get(scriptFilePath));
            }
            catch (NoSuchFileException | DirectoryNotEmptyException | SecurityException ex)
            {
                ex.printStackTrace();
            }
            catch (IOException ex)
            {
                System.err.println("IOException in delete.");
            }
        }
        else
        {
            System.err.println("Unable to delete script file at " + scriptFileToDelete.getAbsolutePath());
        }

        //Repopulate soundtree
        populateSoundList();
    }

    @Override
    protected void playSoundButtonActionPerformed(ActionEvent evt)
    {
        if (currentTree.getSelectionRows().length != 0
                && ((TreeNode) currentTree.getSelectionPath().getLastPathComponent()).isLeaf())
        {
            this.playSelectedTreeSound(currentTree.getSelectionPath());
        }
    }

    @Override
    protected void replaceButtonActionPerformed(java.awt.event.ActionEvent evt)
    {
        if (currentTree.getSelectionRows() != null
                && ((TreeNode) currentTree.getSelectionPath().getLastPathComponent()).isLeaf())
        {
            TreeNode selectedFile = ((TreeNode) currentTree.getSelectionPath().getLastPathComponent());
            promptUserForNewFile(selectedFile.toString());
        }
    }

    @Override
    String getCurrentScriptString()
    {
        return this.getScriptPathByHeroName(((NamedHero) currentDropdown.getSelectedItem()).getInternalName());
    }

    @Override
    String getCustomSoundPathString()
    {
        return Paths.get("custom", ((NamedHero) currentDropdown.getSelectedItem()).getInternalName()).toString() + File.separator;
    }

    @Override
    void updateCache(String scriptKey, long internalCrc)
    {
        CacheManager cm = CacheManager.getInstance();
        String internalPath = "scripts/game_sounds_heroes/game_sounds_" + ((NamedHero) currentDropdown.getSelectedItem()).getInternalName() + ".txt";
        cm.putScript(scriptKey, internalPath, internalCrc);
    }
}
TOP

Related Classes of dotaSoundEditor.Controls.HeroPanel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.